From 568971be7735ce5022c5c2691d5efd399aa4012b Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Mon, 15 Mar 2021 16:51:16 +0200 Subject: [PATCH 01/76] Fix AudoFocus pausing video when attempting to play (#1) Fix AudioFocus bug that could cause the player to stop responding to play/pause in some instances. Fixes issue react-native-video#1945 This was caused by the player requesting audio focus on each play (un-pause) and that resulted in a small window of Audio focus loss and then gain. The focus loss results in the player being paused while the player was supposed to play at the time. The solution is to keep track of Audio focus and not request new focus if we already have it. --- CHANGELOG.md | 2 ++ .../com/brentvatne/exoplayer/ReactExoplayerView.java | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53e2518c..731db2e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Changelog +- Fix Android AudioFocus bug that could cause player to not respond to play/pause in some instances [#2311](https://github.com/react-native-video/react-native-video/pull/2311) + ### Version 5.1.0-alpha9 - Add ARM64 support for windows [#2137](https://github.com/react-native-community/react-native-video/pull/2137) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index b41b2768..20ce983a 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -122,6 +122,7 @@ class ReactExoplayerView extends FrameLayout implements private boolean isPaused; private boolean isBuffering; private boolean muted = false; + private boolean hasAudioFocus = false; private float rate = 1f; private float audioVolume = 1f; private int minLoadRetryCount = 3; @@ -567,7 +568,7 @@ class ReactExoplayerView extends FrameLayout implements } private boolean requestAudioFocus() { - if (disableFocus || srcUri == null) { + if (disableFocus || srcUri == null || this.hasAudioFocus) { return true; } int result = audioManager.requestAudioFocus(this, @@ -582,8 +583,8 @@ class ReactExoplayerView extends FrameLayout implements } if (playWhenReady) { - boolean hasAudioFocus = requestAudioFocus(); - if (hasAudioFocus) { + this.hasAudioFocus = requestAudioFocus(); + if (this.hasAudioFocus) { player.setPlayWhenReady(true); } } else { @@ -678,6 +679,7 @@ class ReactExoplayerView extends FrameLayout implements public void onAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_LOSS: + this.hasAudioFocus = false; eventEmitter.audioFocusChanged(false); pausePlayback(); audioManager.abandonAudioFocus(this); @@ -686,6 +688,7 @@ class ReactExoplayerView extends FrameLayout implements eventEmitter.audioFocusChanged(false); break; case AudioManager.AUDIOFOCUS_GAIN: + this.hasAudioFocus = true; eventEmitter.audioFocusChanged(true); break; default: From 61009817603b3440cb0d00dda47633d26f11f239 Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Wed, 17 Mar 2021 17:49:10 +0200 Subject: [PATCH 02/76] Upgrade exoplayer to 2.13.2 (#2) Upgrade ExoPlayer from 2.11.4 to 2.13.2 and fix any related issues related to the upgrade and deprecated method use. --- android-exoplayer/build.gradle | 4 +- .../brentvatne/exoplayer/ExoPlayerView.java | 15 ++-- .../exoplayer/ReactExoplayerView.java | 74 ++++++++++--------- 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/android-exoplayer/build.gradle b/android-exoplayer/build.gradle index 296e21cb..cebc9df3 100644 --- a/android-exoplayer/build.gradle +++ b/android-exoplayer/build.gradle @@ -28,7 +28,7 @@ android { dependencies { implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}" - implementation('com.google.android.exoplayer:exoplayer:2.11.4') { + implementation('com.google.android.exoplayer:exoplayer:2.13.2') { exclude group: 'com.android.support' } @@ -37,7 +37,7 @@ dependencies { implementation "androidx.core:core:1.1.0" implementation "androidx.media:media:1.1.0" - implementation('com.google.android.exoplayer:extension-okhttp:2.11.4') { + implementation('com.google.android.exoplayer:extension-okhttp:2.13.2') { exclude group: 'com.squareup.okhttp3', module: 'okhttp' } implementation 'com.squareup.okhttp3:okhttp:3.14.3' diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java index afa5106c..99446bde 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java @@ -17,6 +17,7 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.video.VideoListener; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.text.Cue; @@ -113,8 +114,8 @@ public final class ExoPlayerView extends FrameLayout { } /** - * Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#setTextOutput} and - * {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous + * Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#addTextOutput} and + * {@link SimpleExoPlayer#addVideoListener} method of the player will be called and previous * assignments are overridden. * * @param player The {@link SimpleExoPlayer} to use. @@ -124,8 +125,8 @@ public final class ExoPlayerView extends FrameLayout { return; } if (this.player != null) { - this.player.setTextOutput(null); - this.player.setVideoListener(null); + this.player.addTextOutput(null); + this.player.removeVideoListener(componentListener); this.player.removeListener(componentListener); this.player.setVideoSurface(null); } @@ -133,9 +134,9 @@ public final class ExoPlayerView extends FrameLayout { shutterView.setVisibility(VISIBLE); if (player != null) { setVideoView(); - player.setVideoListener(componentListener); + player.addVideoListener(componentListener); player.addListener(componentListener); - player.setTextOutput(componentListener); + player.addTextOutput(componentListener); } } @@ -205,7 +206,7 @@ public final class ExoPlayerView extends FrameLayout { layout.invalidateAspectRatio(); } - private final class ComponentListener implements SimpleExoPlayer.VideoListener, + private final class ComponentListener implements VideoListener, TextOutput, ExoPlayer.EventListener { // TextRenderer.Output implementation diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 20ce983a..d7c33214 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -37,7 +37,7 @@ import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; -import com.google.android.exoplayer2.drm.DefaultDrmSessionEventListener; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaDrm; @@ -62,7 +62,7 @@ import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; -import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.trackselection.ExoTrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.ui.PlayerControlView; import com.google.android.exoplayer2.upstream.BandwidthMeter; @@ -88,7 +88,7 @@ class ReactExoplayerView extends FrameLayout implements BecomingNoisyListener, AudioManager.OnAudioFocusChangeListener, MetadataOutput, - DefaultDrmSessionEventListener { + DrmSessionEventListener { private static final String TAG = "ReactExoplayerView"; @@ -395,7 +395,7 @@ class ReactExoplayerView extends FrameLayout implements @Override public void run() { if (player == null) { - TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); + ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); trackSelector.setParameters(trackSelector.buildUponParameters() .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); @@ -410,23 +410,11 @@ class ReactExoplayerView extends FrameLayout implements DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(getContext()) .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); - // DRM - DrmSessionManager drmSessionManager = null; - if (self.drmUUID != null) { - try { - drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl, - self.drmLicenseHeader); - } catch (UnsupportedDrmException e) { - int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported - : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME - ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); - eventEmitter.error(getResources().getString(errorStringId), e); - return; - } - } - // End DRM - player = ExoPlayerFactory.newSimpleInstance(getContext(), renderersFactory, - trackSelector, defaultLoadControl, drmSessionManager, bandwidthMeter); + player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) + .setTrackSelector​(trackSelector) + .setBandwidthMeter(bandwidthMeter) + .setLoadControl(defaultLoadControl) + .build(); player.addListener(self); player.addMetadataOutput(self); exoPlayerView.setPlayer(player); @@ -441,8 +429,24 @@ class ReactExoplayerView extends FrameLayout implements if (playerNeedsSource && srcUri != null) { exoPlayerView.invalidateAspectRatio(); + // DRM + DrmSessionManager drmSessionManager = null; + if (self.drmUUID != null) { + try { + drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl, + self.drmLicenseHeader); + } catch (UnsupportedDrmException e) { + int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported + : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME + ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); + eventEmitter.error(getResources().getString(errorStringId), e); + return; + } + } + // End DRM + ArrayList mediaSourceList = buildTextSources(); - MediaSource videoSource = buildMediaSource(srcUri, extension); + MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager); MediaSource mediaSource; if (mediaSourceList.size() == 0) { mediaSource = videoSource; @@ -473,7 +477,7 @@ class ReactExoplayerView extends FrameLayout implements }, 1); } - private DrmSessionManager buildDrmSessionManager(UUID uuid, + private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException { if (Util.SDK_INT < 18) { return null; @@ -486,11 +490,11 @@ class ReactExoplayerView extends FrameLayout implements keyRequestPropertiesArray[i + 1]); } } - return new DefaultDrmSessionManager<>(uuid, + return new DefaultDrmSessionManager(uuid, FrameworkMediaDrm.newInstance(uuid), drmCallback, null, false, 3); } - private MediaSource buildMediaSource(Uri uri, String overrideExtension) { + private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) { int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension : uri.getLastPathSegment()); switch (type) { @@ -498,26 +502,30 @@ class ReactExoplayerView extends FrameLayout implements return new SsMediaSource.Factory( new DefaultSsChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false) - ).setLoadErrorHandlingPolicy( + ).setDrmSessionManager(drmSessionManager) + .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(uri); case C.TYPE_DASH: return new DashMediaSource.Factory( new DefaultDashChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false) - ).setLoadErrorHandlingPolicy( + ).setDrmSessionManager(drmSessionManager) + .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(uri); case C.TYPE_HLS: return new HlsMediaSource.Factory( mediaDataSourceFactory - ).setLoadErrorHandlingPolicy( + ).setDrmSessionManager(drmSessionManager) + .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(uri); case C.TYPE_OTHER: return new ProgressiveMediaSource.Factory( mediaDataSourceFactory - ).setLoadErrorHandlingPolicy( + ).setDrmSessionManager(drmSessionManager) + .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(uri); default: { @@ -1339,23 +1347,23 @@ class ReactExoplayerView extends FrameLayout implements @Override - public void onDrmKeysLoaded() { + public void onDrmKeysLoaded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) { Log.d("DRM Info", "onDrmKeysLoaded"); } @Override - public void onDrmSessionManagerError(Exception e) { + public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, Exception e) { Log.d("DRM Info", "onDrmSessionManagerError"); eventEmitter.error("onDrmSessionManagerError", e); } @Override - public void onDrmKeysRestored() { + public void onDrmKeysRestored(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) { Log.d("DRM Info", "onDrmKeysRestored"); } @Override - public void onDrmKeysRemoved() { + public void onDrmKeysRemoved(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) { Log.d("DRM Info", "onDrmKeysRemoved"); } From 61e6535b2f9b31eccae7b1b653515a1668df05b1 Mon Sep 17 00:00:00 2001 From: Adrian Mui Date: Thu, 18 Mar 2021 03:58:04 -0700 Subject: [PATCH 03/76] Feature/toggle buffering (#3) This PR adds the property disableBuffering: boolean for android. The PR was initally created as a personal fork and referenced in crunchyroll/vilos#1227 also updated RNVLoadControl constructor and method to reflect new ExoPlayer.LoadControl api related ticket: https://jira.tenkasu.net/browse/VEX-3776 --- Video.js | 1 + .../exoplayer/ReactExoplayerView.java | 50 ++++++++++++++++--- .../exoplayer/ReactExoplayerViewManager.java | 6 +++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/Video.js b/Video.js index b492d48b..f1119150 100644 --- a/Video.js +++ b/Video.js @@ -470,6 +470,7 @@ Video.propTypes = { ignoreSilentSwitch: PropTypes.oneOf(['ignore', 'obey']), reportBandwidth: PropTypes.bool, disableFocus: PropTypes.bool, + disableBuffering: PropTypes.bool, controls: PropTypes.bool, audioOnly: PropTypes.bool, currentTime: PropTypes.number, diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index d7c33214..7881c914 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -26,6 +26,7 @@ import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.util.RNLog; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.DefaultRenderersFactory; @@ -70,6 +71,7 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultAllocator; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.HttpDataSource; +import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import java.net.CookieHandler; @@ -148,6 +150,7 @@ class ReactExoplayerView extends FrameLayout implements private Dynamic textTrackValue; private ReadableArray textTracks; private boolean disableFocus; + private boolean disableBuffering; private boolean preventsDisplaySleepDuringVideoPlayback = true; private float mProgressUpdateInterval = 250.0f; private boolean playInBackground = false; @@ -183,7 +186,7 @@ class ReactExoplayerView extends FrameLayout implements } } }; - + public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) { Timeline.Window window = new Timeline.Window(); if(!player.getCurrentTimeline().isEmpty()) { @@ -388,6 +391,28 @@ class ReactExoplayerView extends FrameLayout implements view.layout(view.getLeft(), view.getTop(), view.getMeasuredWidth(), view.getMeasuredHeight()); } + private class RNVLoadControl extends DefaultLoadControl { + public RNVLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs, int bufferForPlaybackMs, int bufferForPlaybackAfterRebufferMs, int targetBufferBytes, boolean prioritizeTimeOverSizeThresholds, int backBufferDurationMs, boolean retainBackBufferFromKeyframe) { + super(allocator, + minBufferMs, + maxBufferMs, + bufferForPlaybackMs, + bufferForPlaybackAfterRebufferMs, + targetBufferBytes, + prioritizeTimeOverSizeThresholds, + backBufferDurationMs, + retainBackBufferFromKeyframe); + } + + @Override + public boolean shouldContinueLoading(long playbackPositionUs, long bufferedDurationUs, float playbackSpeed) { + if (ReactExoplayerView.this.disableBuffering) { + return false; + } + return super.shouldContinueLoading(playbackPositionUs, bufferedDurationUs, playbackSpeed); + } + } + private void initializePlayer() { ReactExoplayerView self = this; // This ensures all props have been settled, to avoid async racing conditions. @@ -401,19 +426,24 @@ class ReactExoplayerView extends FrameLayout implements .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); - DefaultLoadControl.Builder defaultLoadControlBuilder = new DefaultLoadControl.Builder(); - defaultLoadControlBuilder.setAllocator(allocator); - defaultLoadControlBuilder.setBufferDurationsMs(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs); - defaultLoadControlBuilder.setTargetBufferBytes(-1); - defaultLoadControlBuilder.setPrioritizeTimeOverSizeThresholds(true); - DefaultLoadControl defaultLoadControl = defaultLoadControlBuilder.createDefaultLoadControl(); + RNVLoadControl loadControl = new RNVLoadControl( + allocator, + minBufferMs, + maxBufferMs, + bufferForPlaybackMs, + bufferForPlaybackAfterRebufferMs, + -1, + true, + DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS, + DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME + ); DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(getContext()) .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) .setTrackSelector​(trackSelector) .setBandwidthMeter(bandwidthMeter) - .setLoadControl(defaultLoadControl) + .setLoadControl(loadControl) .build(); player.addListener(self); player.addMetadataOutput(self); @@ -1282,6 +1312,10 @@ class ReactExoplayerView extends FrameLayout implements this.disableFocus = disableFocus; } + public void setDisableBuffering(boolean disableBuffering) { + this.disableBuffering = disableBuffering; + } + public void setFullscreen(boolean fullscreen) { if (fullscreen == isFullscreen) { return; // Avoid generating events when nothing is changing diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 0d81e0b2..2fc12e42 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -63,6 +63,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager Date: Fri, 7 May 2021 11:37:57 +0300 Subject: [PATCH 04/76] VEX-4556: The app crashes when the premium episode is loaded (#4) Fix player crashing when it is being cleared. --- .../java/com/brentvatne/exoplayer/ExoPlayerView.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java index 99446bde..61cb0dd6 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java @@ -86,6 +86,14 @@ public final class ExoPlayerView extends FrameLayout { addViewInLayout(layout, 0, aspectRatioParams); } + private void clearVideoView() { + if (surfaceView instanceof TextureView) { + player.clearVideoTextureView((TextureView) surfaceView); + } else if (surfaceView instanceof SurfaceView) { + player.clearVideoSurfaceView((SurfaceView) surfaceView); + } + } + private void setVideoView() { if (surfaceView instanceof TextureView) { player.setVideoTextureView((TextureView) surfaceView); @@ -125,10 +133,10 @@ public final class ExoPlayerView extends FrameLayout { return; } if (this.player != null) { - this.player.addTextOutput(null); + this.player.removeTextOutput(componentListener); this.player.removeVideoListener(componentListener); this.player.removeListener(componentListener); - this.player.setVideoSurface(null); + clearVideoView(); } this.player = player; shutterView.setVisibility(VISIBLE); From 80873102a4e739cf6b08343a91c21f760c6deaf5 Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Mon, 17 May 2021 13:09:09 +0300 Subject: [PATCH 05/76] VEX-4579: Network loss handling (#5) Add support for customizing back buffer duration and handle network errors gracefully to prevent releasing the player when network is lost. --- README.md | 14 ++++++++ .../DefaultReactExoplayerConfig.java | 14 +++++++- .../exoplayer/ReactExoplayerConfig.java | 3 ++ ...ReactExoplayerLoadErrorHandlingPolicy.java | 33 +++++++++++++++++++ .../exoplayer/ReactExoplayerView.java | 13 +++++++- .../exoplayer/ReactExoplayerViewManager.java | 12 +++++++ 6 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerLoadErrorHandlingPolicy.java diff --git a/README.md b/README.md index 1b6a913e..fc6681f0 100644 --- a/README.md +++ b/README.md @@ -276,10 +276,12 @@ var styles = StyleSheet.create({ * [allowsExternalPlayback](#allowsexternalplayback) * [audioOnly](#audioonly) * [automaticallyWaitsToMinimizeStalling](#automaticallyWaitsToMinimizeStalling) +* [backBufferDurationMs](#backBufferDurationMs) * [bufferConfig](#bufferconfig) * [controls](#controls) * [currentPlaybackTime](#currentPlaybackTime) * [disableFocus](#disableFocus) +* [disableDisconnectError](#disableDisconnectError) * [filter](#filter) * [filterEnabled](#filterEnabled) * [fullscreen](#fullscreen) @@ -367,6 +369,11 @@ A Boolean value that indicates whether the player should automatically delay pla Platforms: iOS +#### backBufferDurationMs +The number of milliseconds of buffer to keep before the current position. This allows rewinding without rebuffering within that duration. + +Platforms: Android ExoPlayer + #### bufferConfig Adjust the buffer settings. This prop takes an object with one or more of the properties listed below. @@ -416,6 +423,13 @@ Determines whether video audio should override background music/audio in Android Platforms: Android Exoplayer +#### disableDisconnectError +Determines if the player needs to throw an error when connection is lost or not +* **false (default)** - Player will throw an error when connection is lost +* **true** - Player will keep trying to buffer when network connect is lost + +Platforms: Android Exoplayer + ### DRM To setup DRM please follow [this guide](./DRM.md) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DefaultReactExoplayerConfig.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DefaultReactExoplayerConfig.java index d68274b7..3475273c 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DefaultReactExoplayerConfig.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DefaultReactExoplayerConfig.java @@ -9,16 +9,28 @@ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; public class DefaultReactExoplayerConfig implements ReactExoplayerConfig { private final DefaultBandwidthMeter bandwidthMeter; + private boolean disableDisconnectError = false; public DefaultReactExoplayerConfig(Context context) { this.bandwidthMeter = new DefaultBandwidthMeter.Builder(context).build(); } - @Override public LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount) { + if (this.disableDisconnectError) { + // Use custom error handling policy to prevent throwing an error when losing network connection + return new ReactExoplayerLoadErrorHandlingPolicy(minLoadRetryCount); + } return new DefaultLoadErrorHandlingPolicy(minLoadRetryCount); } + public void setDisableDisconnectError(boolean disableDisconnectError) { + this.disableDisconnectError = disableDisconnectError; + } + + public boolean getDisableDisconnectError() { + return this.disableDisconnectError; + } + @Override public DefaultBandwidthMeter getBandwidthMeter() { return bandwidthMeter; diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerConfig.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerConfig.java index 522578b7..2cc56f9a 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerConfig.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerConfig.java @@ -9,5 +9,8 @@ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; public interface ReactExoplayerConfig { LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount); + void setDisableDisconnectError(boolean disableDisconnectError); + boolean getDisableDisconnectError(); + DefaultBandwidthMeter getBandwidthMeter(); } diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerLoadErrorHandlingPolicy.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerLoadErrorHandlingPolicy.java new file mode 100644 index 00000000..c701310f --- /dev/null +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerLoadErrorHandlingPolicy.java @@ -0,0 +1,33 @@ +package com.brentvatne.exoplayer; + +import java.io.IOException; +import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy; +import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException; +import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy.LoadErrorInfo; +import com.google.android.exoplayer2.C; + +public final class ReactExoplayerLoadErrorHandlingPolicy extends DefaultLoadErrorHandlingPolicy { + private int minLoadRetryCount = Integer.MAX_VALUE; + + public ReactExoplayerLoadErrorHandlingPolicy(int minLoadRetryCount) { + super(minLoadRetryCount); + this.minLoadRetryCount = minLoadRetryCount; + } + + @Override + public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) { + if (loadErrorInfo.exception instanceof HttpDataSourceException) { + // Capture the error we get when there is no network connectivity and keep retrying it + return 1000; // Retry every second + } else if(loadErrorInfo.errorCount < this.minLoadRetryCount) { + return Math.min((loadErrorInfo.errorCount - 1) * 1000, 5000); // Default timeout handling + } else { + return C.TIME_UNSET; // Done retrying and will return the error immediately + } + } + + @Override + public int getMinimumLoadableRetryCount(int dataType) { + return Integer.MAX_VALUE; + } +} \ No newline at end of file diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 7881c914..488f2d3b 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -139,6 +139,7 @@ class ReactExoplayerView extends FrameLayout implements private Handler mainHandler; // Props from React + private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS; private Uri srcUri; private String extension; private boolean repeat; @@ -151,6 +152,7 @@ class ReactExoplayerView extends FrameLayout implements private ReadableArray textTracks; private boolean disableFocus; private boolean disableBuffering; + private boolean disableDisconnectError; private boolean preventsDisplaySleepDuringVideoPlayback = true; private float mProgressUpdateInterval = 250.0f; private boolean playInBackground = false; @@ -434,7 +436,7 @@ class ReactExoplayerView extends FrameLayout implements bufferForPlaybackAfterRebufferMs, -1, true, - DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS, + backBufferDurationMs, DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME ); DefaultRenderersFactory renderersFactory = @@ -527,6 +529,7 @@ class ReactExoplayerView extends FrameLayout implements private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) { int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension : uri.getLastPathSegment()); + config.setDisableDisconnectError(this.disableDisconnectError); switch (type) { case C.TYPE_SS: return new SsMediaSource.Factory( @@ -1312,10 +1315,18 @@ class ReactExoplayerView extends FrameLayout implements this.disableFocus = disableFocus; } + public void setBackBufferDurationMs(int backBufferDurationMs) { + this.backBufferDurationMs = backBufferDurationMs; + } + public void setDisableBuffering(boolean disableBuffering) { this.disableBuffering = disableBuffering; } + public void setDisableDisconnectError(boolean disableDisconnectError) { + this.disableDisconnectError = disableDisconnectError; + } + public void setFullscreen(boolean fullscreen) { if (fullscreen == isFullscreen) { return; // Avoid generating events when nothing is changing diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 2fc12e42..c460f176 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -49,6 +49,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager Date: Tue, 18 May 2021 09:20:32 -0600 Subject: [PATCH 06/76] Fix for issue #2153 --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index b41b2768..56be2eb5 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1219,13 +1219,11 @@ class ReactExoplayerView extends FrameLayout implements public void setMutedModifier(boolean muted) { this.muted = muted; - audioVolume = muted ? 0.f : 1.f; if (player != null) { - player.setVolume(audioVolume); + player.setVolume(muted ? 0.f : audioVolume); } } - public void setVolumeModifier(float volume) { audioVolume = volume; if (player != null) { From cba88fa9d8d9b7ef86ec310bc81c5038581c4f43 Mon Sep 17 00:00:00 2001 From: Nick Fujita Date: Wed, 30 Jun 2021 10:24:21 +0900 Subject: [PATCH 07/76] VEX-5044: Allow exoplayer to preinit with empty source (#6) - Allow player to be init before source is provided, and later update once a source is provided. - Adds handling for providing a empty source in order to stop playback and clear out any existing content --- README.md | 6 ++++++ .../exoplayer/ReactExoplayerView.java | 18 ++++++++++++++---- .../exoplayer/ReactExoplayerViewManager.java | 1 + 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fc6681f0..fdb8231b 100644 --- a/README.md +++ b/README.md @@ -770,6 +770,12 @@ Platforms: Android ExoPlayer #### source Sets the media source. You can pass an asset loaded via require or an object with a uri. +Setting the source will trigger the player to attempt to load the provided media with all other given props. Please be sure that all props are provided before/at the same time as setting the source. + +Rendering the player component with a null source will init the player, and start playing once a source value is provided. + +Providing a null source value after loading a previous source will stop playback, and clear out the previous source content. + The docs for this prop are incomplete and will be updated as each option is investigated and tested. diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 488f2d3b..209cc949 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -497,6 +497,7 @@ class ReactExoplayerView extends FrameLayout implements player.prepare(mediaSource, !haveResumePosition, false); playerNeedsSource = false; + reLayout(exoPlayerView); eventEmitter.loadStart(); loadVideoStarted = true; } @@ -1045,7 +1046,6 @@ class ReactExoplayerView extends FrameLayout implements public void setSrc(final Uri uri, final String extension, Map headers) { if (uri != null) { - boolean isOriginalSourceNull = srcUri == null; boolean isSourceEqual = uri.equals(srcUri); this.srcUri = uri; @@ -1055,12 +1055,23 @@ class ReactExoplayerView extends FrameLayout implements DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, bandwidthMeter, this.requestHeaders); - if (!isOriginalSourceNull && !isSourceEqual) { + if (!isSourceEqual) { reloadSource(); } } } + public void clearSrc() { + if (srcUri != null) { + player.stop(true); + this.srcUri = null; + this.extension = null; + this.requestHeaders = null; + this.mediaDataSourceFactory = null; + clearResumePosition(); + } + } + public void setProgressUpdateInterval(final float progressUpdateInterval) { mProgressUpdateInterval = progressUpdateInterval; } @@ -1071,14 +1082,13 @@ class ReactExoplayerView extends FrameLayout implements public void setRawSrc(final Uri uri, final String extension) { if (uri != null) { - boolean isOriginalSourceNull = srcUri == null; boolean isSourceEqual = uri.equals(srcUri); this.srcUri = uri; this.extension = extension; this.mediaDataSourceFactory = buildDataSourceFactory(true); - if (!isOriginalSourceNull && !isSourceEqual) { + if (!isSourceEqual) { reloadSource(); } } diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index c460f176..fc9a1354 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -147,6 +147,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager headers = src.hasKey(PROP_SRC_HEADERS) ? toStringMap(src.getMap(PROP_SRC_HEADERS)) : null; if (TextUtils.isEmpty(uriString)) { + videoView.clearSrc(); return; } From 93604b2c25f09405fd1765a02405ed5da00e5bcf Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Wed, 7 Jul 2021 18:59:55 +0300 Subject: [PATCH 08/76] VEX-3245: Buffer Progress UI While Paused (#7) Add support for onBufferProgress prop on Android to get buffer data even when the player is paused. --- CHANGELOG.md | 2 + README.md | 13 +++++++ Video.js | 7 ++++ .../exoplayer/ReactExoplayerView.java | 39 +++++++++++++++++++ .../exoplayer/VideoEventEmitter.java | 12 ++++++ 5 files changed, 73 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 731db2e8..3fdf4ea4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Changelog +- Add support for `onBufferProgress` on Android for getting buffer data even when the player is paused + - Fix Android AudioFocus bug that could cause player to not respond to play/pause in some instances [#2311](https://github.com/react-native-video/react-native-video/pull/2311) ### Version 5.1.0-alpha9 diff --git a/README.md b/README.md index fdb8231b..d46e8d39 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,7 @@ var styles = StyleSheet.create({ ### Event props * [onAudioBecomingNoisy](#onaudiobecomingnoisy) * [onBandwidthUpdate](#onbandwidthupdate) +* [onBufferProgress](#onbufferprogress) * [onEnd](#onend) * [onExternalPlaybackChange](#onexternalplaybackchange) * [onFullscreenPlayerWillPresent](#onfullscreenplayerwillpresent) @@ -944,6 +945,18 @@ Note: On Android ExoPlayer, you must set the [reportBandwidth](#reportbandwidth) Platforms: Android ExoPlayer + +#### onBufferProgress +Callback function that is called on a set interval which contains the buffer start and end position in ms. + +Payload: +Property | Type | Description +--- | --- | --- +start | number | The buffer start (ms) +end | number | The buffer end (ms) + +Platforms: Android ExoPlayer + #### onEnd Callback function that is called when the player reaches the end of the media. diff --git a/Video.js b/Video.js index f1119150..88d90324 100644 --- a/Video.js +++ b/Video.js @@ -233,6 +233,12 @@ export default class Video extends Component { } }; + _onBufferProgress = (event) => { + if (this.props.onBufferProgress) { + this.props.onBufferProgress(event.nativeEvent); + } + }; + _onGetLicense = (event) => { if (this.props.drm && this.props.drm.getLicense instanceof Function) { const data = event.nativeEvent; @@ -311,6 +317,7 @@ export default class Video extends Component { onVideoSeek: this._onSeek, onVideoEnd: this._onEnd, onVideoBuffer: this._onBuffer, + onVideoBufferProgress: this._onBufferProgress, onVideoBandwidthUpdate: this._onBandwidthUpdate, onTimedMetadata: this._onTimedMetadata, onVideoAudioBecomingNoisy: this._onAudioBecomingNoisy, diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 209cc949..b928ef07 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -81,6 +81,8 @@ import java.util.ArrayList; import java.util.Locale; import java.util.UUID; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; @SuppressLint("ViewConstructor") class ReactExoplayerView extends FrameLayout implements @@ -137,6 +139,7 @@ class ReactExoplayerView extends FrameLayout implements private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS; private Handler mainHandler; + private Timer bufferCheckTimer; // Props from React private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS; @@ -415,6 +418,40 @@ class ReactExoplayerView extends FrameLayout implements } } + private void startBufferCheckTimer() { + SimpleExoPlayer player = this.player; + VideoEventEmitter eventEmitter = this.eventEmitter; + Handler mainHandler = this.mainHandler; + + if (this.bufferCheckTimer != null) { + this.stopBufferCheckTimer(); + } + + this.bufferCheckTimer = new Timer(); + TimerTask bufferCheckTimerTask = new TimerTask() { + @Override + public void run() { + if (mainHandler != null) { + mainHandler.post(new Runnable() { + public void run() { + if (player != null) { + double bufferedDuration = (double) (player.getBufferedPercentage() * player.getDuration() / 100); + eventEmitter.bufferProgress(0d, bufferedDuration); + } + } + }); + } + }; + }; + + this.bufferCheckTimer.scheduleAtFixedRate(bufferCheckTimerTask, 500, 1000); + } + + private void stopBufferCheckTimer() { + this.bufferCheckTimer.cancel(); + this.bufferCheckTimer = null; + } + private void initializePlayer() { ReactExoplayerView self = this; // This ensures all props have been settled, to avoid async racing conditions. @@ -506,6 +543,7 @@ class ReactExoplayerView extends FrameLayout implements initializePlayerControl(); setControls(controls); applyModifiers(); + startBufferCheckTimer(); } }, 1); } @@ -597,6 +635,7 @@ class ReactExoplayerView extends FrameLayout implements private void releasePlayer() { if (player != null) { + stopBufferCheckTimer(); updateResumePosition(); player.release(); player.removeMetadataOutput(this); diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java index ea0cc5ac..a6d3b6c8 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java @@ -42,6 +42,7 @@ class VideoEventEmitter { private static final String EVENT_RESUME = "onPlaybackResume"; private static final String EVENT_READY = "onReadyForDisplay"; private static final String EVENT_BUFFER = "onVideoBuffer"; + private static final String EVENT_BUFFER_PROGRESS = "onVideoBufferProgress"; private static final String EVENT_IDLE = "onVideoIdle"; private static final String EVENT_TIMED_METADATA = "onTimedMetadata"; private static final String EVENT_AUDIO_BECOMING_NOISY = "onVideoAudioBecomingNoisy"; @@ -63,6 +64,7 @@ class VideoEventEmitter { EVENT_RESUME, EVENT_READY, EVENT_BUFFER, + EVENT_BUFFER_PROGRESS, EVENT_IDLE, EVENT_TIMED_METADATA, EVENT_AUDIO_BECOMING_NOISY, @@ -87,6 +89,7 @@ class VideoEventEmitter { EVENT_RESUME, EVENT_READY, EVENT_BUFFER, + EVENT_BUFFER_PROGRESS, EVENT_IDLE, EVENT_TIMED_METADATA, EVENT_AUDIO_BECOMING_NOISY, @@ -104,6 +107,8 @@ class VideoEventEmitter { private static final String EVENT_PROP_STEP_FORWARD = "canStepForward"; private static final String EVENT_PROP_STEP_BACKWARD = "canStepBackward"; + private static final String EVENT_PROP_BUFFER_START = "bufferStart"; + private static final String EVENT_PROP_BUFFER_END = "bufferEnd"; private static final String EVENT_PROP_DURATION = "duration"; private static final String EVENT_PROP_PLAYABLE_DURATION = "playableDuration"; private static final String EVENT_PROP_SEEKABLE_DURATION = "seekableDuration"; @@ -206,6 +211,13 @@ class VideoEventEmitter { receiveEvent(EVENT_BUFFER, map); } + void bufferProgress(double start, double end) { + WritableMap map = Arguments.createMap(); + map.putDouble(EVENT_PROP_BUFFER_START, start); + map.putDouble(EVENT_PROP_BUFFER_END, end); + receiveEvent(EVENT_BUFFER_PROGRESS, map); + } + void idle() { receiveEvent(EVENT_IDLE, null); } From a0b679cc076c2fb669df40834742f1d882aeadac Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Sun, 26 Sep 2021 16:38:10 +0300 Subject: [PATCH 09/76] VEX-5545: Some Android codecs don't support specific resolutions (#8) Add support for detecting if format is supported and exclude unsupported resolutions from auto quality selection and video track info in RN. --- .../exoplayer/ReactExoplayerView.java | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index b928ef07..9fe85c61 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -44,6 +44,7 @@ import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaDrm; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.drm.UnsupportedDrmException; +import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; import com.google.android.exoplayer2.metadata.Metadata; @@ -921,7 +922,9 @@ class ReactExoplayerView extends FrameLayout implements videoTrack.putString("codecs", format.codecs != null ? format.codecs : ""); videoTrack.putString("trackId", format.id == null ? String.valueOf(trackIndex) : format.id); - videoTracks.pushMap(videoTrack); + if (isFormatSupported(format)) { + videoTracks.pushMap(videoTrack); + } } } return videoTracks; @@ -1241,10 +1244,31 @@ class ReactExoplayerView extends FrameLayout implements if (groupIndex == C.INDEX_UNSET && trackType == C.TRACK_TYPE_VIDEO && groups.length != 0) { // Video auto // Add all tracks as valid options for ABR to choose from TrackGroup group = groups.get(0); - tracks = new int[group.length]; + int[] allTracks = new int[group.length]; groupIndex = 0; + for (int j = 0; j < group.length; j++) { - tracks[j] = j; + allTracks[j] = j; + } + + // Valiate list of all tracks and add only supported formats + int supportedFormatLength = 0; + ArrayList supportedTrackList = new ArrayList(); + for (int g = 0; g < allTracks.length; g++) { + Format format = group.getFormat(g); + if (isFormatSupported(format)) { + supportedFormatLength++; + } + } + tracks = new int[supportedFormatLength + 1]; + int o = 0; + for (int k = 0; k < allTracks.length; k++) { + Format format = group.getFormat(k); + if (isFormatSupported(format)) { + tracks[o] = allTracks[k]; + supportedTrackList.add(allTracks[k]); + o++; + } } } @@ -1262,6 +1286,25 @@ class ReactExoplayerView extends FrameLayout implements trackSelector.setParameters(selectionParameters); } + private boolean isFormatSupported(Format format) { + int width = format.width == Format.NO_VALUE ? 0 : format.width; + int height = format.height == Format.NO_VALUE ? 0 : format.height; + float frameRate = format.frameRate == Format.NO_VALUE ? 0 : format.frameRate; + String mimeType = format.sampleMimeType; + if (mimeType == null) { + return true; + } + boolean isSupported = false; + try { + MediaCodecInfo codecInfo = MediaCodecUtil.getDecoderInfo(mimeType, false, false); + isSupported = codecInfo.isVideoSizeAndRateSupportedV21(width, height, frameRate); + } catch (Exception e) { + // Failed to get decoder info - assume it is supported + isSupported = true; + } + return isSupported; + } + private int getGroupIndexForDefaultLocale(TrackGroupArray groups) { if (groups.length == 0){ return C.INDEX_UNSET; From 2fcc1de6269d374c272f4cf1cdcc02e71d1188cb Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Tue, 12 Oct 2021 14:56:50 +0300 Subject: [PATCH 10/76] VEX-5825: Android player error handling improvements (#10) Improve error handling. --- .../exoplayer/ReactExoplayerView.java | 203 ++++++++++-------- .../exoplayer/VideoEventEmitter.java | 20 ++ 2 files changed, 139 insertions(+), 84 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 9fe85c61..4bb7f43b 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -43,6 +43,7 @@ import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaDrm; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; +import com.google.android.exoplayer2.drm.MediaDrmCallbackException; import com.google.android.exoplayer2.drm.UnsupportedDrmException; import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; @@ -459,94 +460,103 @@ class ReactExoplayerView extends FrameLayout implements new Handler().postDelayed(new Runnable() { @Override public void run() { - if (player == null) { - ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); - trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); - trackSelector.setParameters(trackSelector.buildUponParameters() - .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); + try { + if (player == null) { + ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); + trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); + trackSelector.setParameters(trackSelector.buildUponParameters() + .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); - DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); - RNVLoadControl loadControl = new RNVLoadControl( - allocator, - minBufferMs, - maxBufferMs, - bufferForPlaybackMs, - bufferForPlaybackAfterRebufferMs, - -1, - true, - backBufferDurationMs, - DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME - ); - DefaultRenderersFactory renderersFactory = - new DefaultRenderersFactory(getContext()) - .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); - player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) - .setTrackSelector​(trackSelector) - .setBandwidthMeter(bandwidthMeter) - .setLoadControl(loadControl) - .build(); - player.addListener(self); - player.addMetadataOutput(self); - exoPlayerView.setPlayer(player); - audioBecomingNoisyReceiver.setListener(self); - bandwidthMeter.addEventListener(new Handler(), self); - setPlayWhenReady(!isPaused); - playerNeedsSource = true; - - PlaybackParameters params = new PlaybackParameters(rate, 1f); - player.setPlaybackParameters(params); - } - if (playerNeedsSource && srcUri != null) { - exoPlayerView.invalidateAspectRatio(); - - // DRM - DrmSessionManager drmSessionManager = null; - if (self.drmUUID != null) { - try { - drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl, - self.drmLicenseHeader); - } catch (UnsupportedDrmException e) { - int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported - : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME - ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); - eventEmitter.error(getResources().getString(errorStringId), e); - return; - } - } - // End DRM - - ArrayList mediaSourceList = buildTextSources(); - MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager); - MediaSource mediaSource; - if (mediaSourceList.size() == 0) { - mediaSource = videoSource; - } else { - mediaSourceList.add(0, videoSource); - MediaSource[] textSourceArray = mediaSourceList.toArray( - new MediaSource[mediaSourceList.size()] + DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); + RNVLoadControl loadControl = new RNVLoadControl( + allocator, + minBufferMs, + maxBufferMs, + bufferForPlaybackMs, + bufferForPlaybackAfterRebufferMs, + -1, + true, + backBufferDurationMs, + DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME ); - mediaSource = new MergingMediaSource(textSourceArray); + DefaultRenderersFactory renderersFactory = + new DefaultRenderersFactory(getContext()) + .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); + player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) + .setTrackSelector​(trackSelector) + .setBandwidthMeter(bandwidthMeter) + .setLoadControl(loadControl) + .build(); + player.addListener(self); + player.addMetadataOutput(self); + exoPlayerView.setPlayer(player); + audioBecomingNoisyReceiver.setListener(self); + bandwidthMeter.addEventListener(new Handler(), self); + setPlayWhenReady(!isPaused); + playerNeedsSource = true; + + PlaybackParameters params = new PlaybackParameters(rate, 1f); + player.setPlaybackParameters(params); + } + if (playerNeedsSource && srcUri != null) { + exoPlayerView.invalidateAspectRatio(); + + // DRM + DrmSessionManager drmSessionManager = null; + if (self.drmUUID != null) { + try { + drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl, + self.drmLicenseHeader); + } catch (UnsupportedDrmException e) { + int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported + : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME + ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); + eventEmitter.error(getResources().getString(errorStringId), e, "3003"); + return; + } + } + // End DRM + + ArrayList mediaSourceList = buildTextSources(); + MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager); + MediaSource mediaSource; + if (mediaSourceList.size() == 0) { + mediaSource = videoSource; + } else { + mediaSourceList.add(0, videoSource); + MediaSource[] textSourceArray = mediaSourceList.toArray( + new MediaSource[mediaSourceList.size()] + ); + mediaSource = new MergingMediaSource(textSourceArray); + } + + boolean haveResumePosition = resumeWindow != C.INDEX_UNSET; + if (haveResumePosition) { + player.seekTo(resumeWindow, resumePosition); + } + player.prepare(mediaSource, !haveResumePosition, false); + playerNeedsSource = false; + + reLayout(exoPlayerView); + eventEmitter.loadStart(); + loadVideoStarted = true; } - boolean haveResumePosition = resumeWindow != C.INDEX_UNSET; - if (haveResumePosition) { - player.seekTo(resumeWindow, resumePosition); - } - player.prepare(mediaSource, !haveResumePosition, false); - playerNeedsSource = false; - - reLayout(exoPlayerView); - eventEmitter.loadStart(); - loadVideoStarted = true; + // Initializing the playerControlView + initializePlayerControl(); + setControls(controls); + applyModifiers(); + startBufferCheckTimer(); + + } catch (Exception ex) { + self.playerNeedsSource = true; + Log.e("ExoPlayer Exception", "Failed to initialize Player!"); + Log.e("ExoPlayer Exception", ex.toString()); + eventEmitter.error(ex.toString(), ex, "1001"); } - - // Initializing the playerControlView - initializePlayerControl(); - setControls(controls); - applyModifiers(); - startBufferCheckTimer(); } }, 1); + } private DrmSessionManager buildDrmSessionManager(UUID uuid, @@ -567,6 +577,9 @@ class ReactExoplayerView extends FrameLayout implements } private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) { + if (uri == null) { + throw new IllegalStateException("Invalid video uri"); + } int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension : uri.getLastPathSegment()); config.setDisableDisconnectError(this.disableDisconnectError); @@ -1015,6 +1028,7 @@ class ReactExoplayerView extends FrameLayout implements @Override public void onPlayerError(ExoPlaybackException e) { String errorString = "ExoPlaybackException type : " + e.type; + String errorCode = "2001"; // Playback error code 2xxx (2001 - unknown playback exception) Exception ex = e; if (e.type == ExoPlaybackException.TYPE_RENDERER) { Exception cause = e.getRendererException(); @@ -1024,24 +1038,45 @@ class ReactExoplayerView extends FrameLayout implements (MediaCodecRenderer.DecoderInitializationException) cause; if (decoderInitializationException.codecInfo.name == null) { if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) { + errorCode = "2011"; errorString = getResources().getString(R.string.error_querying_decoders); } else if (decoderInitializationException.secureDecoderRequired) { + errorCode = "2012"; errorString = getResources().getString(R.string.error_no_secure_decoder, decoderInitializationException.mimeType); } else { + errorCode = "2013"; errorString = getResources().getString(R.string.error_no_decoder, decoderInitializationException.mimeType); } } else { + errorCode = "2014"; errorString = getResources().getString(R.string.error_instantiating_decoder, decoderInitializationException.codecInfo.name); } } } else if (e.type == ExoPlaybackException.TYPE_SOURCE) { - errorString = getResources().getString(R.string.unrecognized_media_format); + Exception cause = e.getSourceException(); + if (cause instanceof DefaultDrmSessionManager.MissingSchemeDataException) { + errorCode = "3004"; + errorString = getResources().getString(R.string.unrecognized_media_format); + } else if(cause instanceof MediaDrmCallbackException) { + errorCode = "3005"; + errorString = getResources().getString(R.string.unrecognized_media_format); + } else { + errorCode = "2021"; + errorString = getResources().getString(R.string.unrecognized_media_format); + } + if (cause != null) { + Throwable rootCause = cause.getCause(); + if (rootCause instanceof MediaDrmCallbackException) { + errorCode = "3005"; + errorString = getResources().getString(R.string.unrecognized_media_format); + } + } } - eventEmitter.error(errorString, ex); + eventEmitter.error(errorString, ex, errorCode); playerNeedsSource = true; if (isBehindLiveWindow(e)) { clearResumePosition(); @@ -1491,7 +1526,7 @@ class ReactExoplayerView extends FrameLayout implements @Override public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, Exception e) { Log.d("DRM Info", "onDrmSessionManagerError"); - eventEmitter.error("onDrmSessionManagerError", e); + eventEmitter.error("onDrmSessionManagerError", e, "3002"); } @Override diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java index a6d3b6c8..266d2538 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java @@ -15,6 +15,8 @@ import com.google.android.exoplayer2.metadata.id3.TextInformationFrame; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.io.StringWriter; +import java.io.PrintWriter; class VideoEventEmitter { @@ -130,6 +132,8 @@ class VideoEventEmitter { private static final String EVENT_PROP_ERROR = "error"; private static final String EVENT_PROP_ERROR_STRING = "errorString"; private static final String EVENT_PROP_ERROR_EXCEPTION = "errorException"; + private static final String EVENT_PROP_ERROR_TRACE = "errorStackTrace"; + private static final String EVENT_PROP_ERROR_CODE = "errorCode"; private static final String EVENT_PROP_TIMED_METADATA = "metadata"; @@ -243,9 +247,25 @@ class VideoEventEmitter { } void error(String errorString, Exception exception) { + _error(errorString, exception, "0001"); + } + + void error(String errorString, Exception exception, String errorCode) { + _error(errorString, exception, errorCode); + } + + void _error(String errorString, Exception exception, String errorCode) { + // Prepare stack trace + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + exception.printStackTrace(pw); + String stackTrace = sw.toString(); + WritableMap error = Arguments.createMap(); error.putString(EVENT_PROP_ERROR_STRING, errorString); error.putString(EVENT_PROP_ERROR_EXCEPTION, exception.toString()); + error.putString(EVENT_PROP_ERROR_CODE, errorCode); + error.putString(EVENT_PROP_ERROR_TRACE, stackTrace); WritableMap event = Arguments.createMap(); event.putMap(EVENT_PROP_ERROR, error); receiveEvent(EVENT_ERROR, event); From 3bcf2c60614c822c979dd550ae96672be291f67a Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Wed, 13 Oct 2021 18:03:29 +0300 Subject: [PATCH 11/76] VEX-5798: Implement DRM L1 to L3 fallback (#9) Add support for L1 to L3 Widevine fallback if playback fails initially. --- .../exoplayer/ReactExoplayerView.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 4bb7f43b..24dda940 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -31,6 +31,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.ExoPlaybackException; +import com.google.android.exoplayer2.drm.MediaDrmCallbackException; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.PlaybackParameters; @@ -134,6 +135,7 @@ class ReactExoplayerView extends FrameLayout implements private int minLoadRetryCount = 3; private int maxBitRate = 0; private long seekTime = C.TIME_UNSET; + private boolean hasDrmFailed = false; private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS; private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS; @@ -572,8 +574,13 @@ class ReactExoplayerView extends FrameLayout implements keyRequestPropertiesArray[i + 1]); } } + FrameworkMediaDrm mediaDrm = FrameworkMediaDrm.newInstance(uuid); + if (hasDrmFailed) { + // When DRM fails using L1 we want to switch to L3 + mediaDrm.setPropertyString("securityLevel", "L3"); + } return new DefaultDrmSessionManager(uuid, - FrameworkMediaDrm.newInstance(uuid), drmCallback, null, false, 3); + mediaDrm, drmCallback, null, false, 3); } private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) { @@ -1029,6 +1036,7 @@ class ReactExoplayerView extends FrameLayout implements public void onPlayerError(ExoPlaybackException e) { String errorString = "ExoPlaybackException type : " + e.type; String errorCode = "2001"; // Playback error code 2xxx (2001 - unknown playback exception) + boolean needsReInitialization = false; Exception ex = e; if (e.type == ExoPlaybackException.TYPE_RENDERER) { Exception cause = e.getRendererException(); @@ -1057,6 +1065,9 @@ class ReactExoplayerView extends FrameLayout implements } } else if (e.type == ExoPlaybackException.TYPE_SOURCE) { + // Re-initialization improves recovery speed and properly resumes + needsReInitialization = true; + errorString = getResources().getString(R.string.unrecognized_media_format); Exception cause = e.getSourceException(); if (cause instanceof DefaultDrmSessionManager.MissingSchemeDataException) { errorCode = "3004"; @@ -1073,6 +1084,14 @@ class ReactExoplayerView extends FrameLayout implements if (rootCause instanceof MediaDrmCallbackException) { errorCode = "3005"; errorString = getResources().getString(R.string.unrecognized_media_format); + if (!hasDrmFailed) { + // When DRM fails to reach the app level certificate server it will fail with a source error so we assume that it is DRM related and try one more time + hasDrmFailed = true; + playerNeedsSource = true; + updateResumePosition(); + initializePlayer(); + return; + } } } } @@ -1083,6 +1102,9 @@ class ReactExoplayerView extends FrameLayout implements initializePlayer(); } else { updateResumePosition(); + if (needsReInitialization) { + initializePlayer(); + } } } From 8b7543814854b12b81b007cd56d8f68f5b119055 Mon Sep 17 00:00:00 2001 From: Nick Fujita Date: Wed, 27 Oct 2021 10:35:07 +0900 Subject: [PATCH 12/76] VEX-5682: iOS Swift Conversion (#11) Converts ios implementation from objective-c to swift. --- .gitignore | 3 + examples/basic/yarn.lock | 23 +- ios/RCTVideo.xcodeproj/project.pbxproj | 91 +- ios/Video/DataStructures/DRMParams.swift | 30 + .../SelectedTrackCriteria.swift | 18 + ios/Video/DataStructures/TextTrack.swift | 25 + ios/Video/DataStructures/VideoSource.swift | 31 + ios/Video/Features/RCTPictureInPicture.swift | 75 + ios/Video/Features/RCTPlayerObserver.swift | 212 ++ ios/Video/Features/RCTPlayerOperations.swift | 160 ++ .../Features/RCTResourceLoaderDelegate.swift | 258 +++ .../Features/RCTVideoErrorHandling.swift | 83 + ios/Video/Features/RCTVideoSave.swift | 75 + ios/Video/Features/RCTVideoUtils.swift | 143 ++ ios/Video/RCTSwiftLog/RCTSwiftLog.h | 11 + ios/Video/RCTSwiftLog/RCTSwiftLog.m | 32 + ios/Video/RCTSwiftLog/RCTSwiftLog.swift | 53 + ios/Video/RCTVideo-Bridging-Header.h | 7 + ios/Video/RCTVideo.h | 67 - ios/Video/RCTVideo.m | 2013 ----------------- ios/Video/RCTVideo.swift | 1147 ++++++++++ ios/Video/RCTVideoManager.h | 6 - ios/Video/RCTVideoManager.m | 84 +- ios/Video/RCTVideoManager.swift | 63 + ios/Video/RCTVideoPlayerViewController.h | 20 - ios/Video/RCTVideoPlayerViewController.m | 43 - ios/Video/RCTVideoPlayerViewController.swift | 44 + .../RCTVideoPlayerViewControllerDelegate.h | 7 - ...RCTVideoPlayerViewControllerDelegate.swift | 7 + ios/Video/UIView+FindUIViewController.h | 15 - ios/Video/UIView+FindUIViewController.m | 21 - ios/Video/UIView+FindUIViewController.swift | 18 + ios/VideoCaching/RCTVideoCachingHandler.swift | 81 + react-native-video.podspec | 8 +- 34 files changed, 2661 insertions(+), 2313 deletions(-) create mode 100644 ios/Video/DataStructures/DRMParams.swift create mode 100644 ios/Video/DataStructures/SelectedTrackCriteria.swift create mode 100644 ios/Video/DataStructures/TextTrack.swift create mode 100644 ios/Video/DataStructures/VideoSource.swift create mode 100644 ios/Video/Features/RCTPictureInPicture.swift create mode 100644 ios/Video/Features/RCTPlayerObserver.swift create mode 100644 ios/Video/Features/RCTPlayerOperations.swift create mode 100644 ios/Video/Features/RCTResourceLoaderDelegate.swift create mode 100644 ios/Video/Features/RCTVideoErrorHandling.swift create mode 100644 ios/Video/Features/RCTVideoSave.swift create mode 100644 ios/Video/Features/RCTVideoUtils.swift create mode 100644 ios/Video/RCTSwiftLog/RCTSwiftLog.h create mode 100644 ios/Video/RCTSwiftLog/RCTSwiftLog.m create mode 100644 ios/Video/RCTSwiftLog/RCTSwiftLog.swift create mode 100644 ios/Video/RCTVideo-Bridging-Header.h delete mode 100644 ios/Video/RCTVideo.h delete mode 100644 ios/Video/RCTVideo.m create mode 100644 ios/Video/RCTVideo.swift delete mode 100644 ios/Video/RCTVideoManager.h create mode 100644 ios/Video/RCTVideoManager.swift delete mode 100644 ios/Video/RCTVideoPlayerViewController.h delete mode 100644 ios/Video/RCTVideoPlayerViewController.m create mode 100644 ios/Video/RCTVideoPlayerViewController.swift delete mode 100644 ios/Video/RCTVideoPlayerViewControllerDelegate.h create mode 100644 ios/Video/RCTVideoPlayerViewControllerDelegate.swift delete mode 100644 ios/Video/UIView+FindUIViewController.h delete mode 100644 ios/Video/UIView+FindUIViewController.m create mode 100644 ios/Video/UIView+FindUIViewController.swift create mode 100644 ios/VideoCaching/RCTVideoCachingHandler.swift diff --git a/.gitignore b/.gitignore index d477ec05..2ca6ff0b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,9 @@ project.xcworkspace .gradle local.properties *.hprof +.project +.settings +.classpath # node.js # diff --git a/examples/basic/yarn.lock b/examples/basic/yarn.lock index 50d8d52e..ec0a6edd 100644 --- a/examples/basic/yarn.lock +++ b/examples/basic/yarn.lock @@ -2093,6 +2093,11 @@ ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" +eme-encryption-scheme-polyfill@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.0.3.tgz#2ca6e06480e06cceb5e50efd27943ac46c959878" + integrity sha512-44CNFMsqzHdKHrzWxlS7xZ8KUHn5XutBqpmCuWzNIynmAyFInHrrD3ozv/RvK9ZhgV6QY6Easx8EWAmxteNodg== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -5160,7 +5165,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.3" -prop-types@^15.5.10, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -5238,11 +5243,11 @@ react-is@^16.8.4, react-is@^16.8.6: integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== "react-native-video@file:../..": - version "5.0.1" + version "5.1.1" dependencies: keymirror "^0.1.1" - prop-types "^15.5.10" - shaka-player "^2.4.4" + prop-types "^15.7.2" + shaka-player "^2.5.9" react-native@0.60.5: version "0.60.5" @@ -5774,10 +5779,12 @@ setprototypeof@1.1.1: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== -shaka-player@^2.4.4: - version "2.5.2" - resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-2.5.2.tgz#3e639f8f5dfdb1a0afff2ff1f87cddf481f93fe4" - integrity sha512-gpRfXVLAZi33kw9Egop18MkZ/EVRS0soeN6ocR+Btq/J5IoCC56MxwwHzAGna+PgBW9Ox458HRefJ6HB0BoADA== +shaka-player@^2.5.9: + version "2.5.23" + resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-2.5.23.tgz#db92d1c6cf2314f0180a2cec11b0e2f2560336f5" + integrity sha512-3MC9k0OXJGw8AZ4n/ZNCZS2yDxx+3as5KgH6Tx4Q5TRboTBBCu6dYPI5vp1DxKeyU12MBN1Zcbs7AKzXv2EnCg== + dependencies: + eme-encryption-scheme-polyfill "^2.0.1" shebang-command@^1.2.0: version "1.2.0" diff --git a/ios/RCTVideo.xcodeproj/project.pbxproj b/ios/RCTVideo.xcodeproj/project.pbxproj index 4e675ac7..e601ea80 100644 --- a/ios/RCTVideo.xcodeproj/project.pbxproj +++ b/ios/RCTVideo.xcodeproj/project.pbxproj @@ -7,14 +7,12 @@ objects = { /* Begin PBXBuildFile section */ - D1107C0A2110259000073188 /* UIView+FindUIViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C032110259000073188 /* UIView+FindUIViewController.m */; }; - D1107C0B2110259000073188 /* UIView+FindUIViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C032110259000073188 /* UIView+FindUIViewController.m */; }; - D1107C0C2110259000073188 /* RCTVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C052110259000073188 /* RCTVideo.m */; }; - D1107C0D2110259000073188 /* RCTVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C052110259000073188 /* RCTVideo.m */; }; - D1107C0E2110259000073188 /* RCTVideoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C062110259000073188 /* RCTVideoManager.m */; }; - D1107C0F2110259000073188 /* RCTVideoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C062110259000073188 /* RCTVideoManager.m */; }; - D1107C102110259000073188 /* RCTVideoPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C082110259000073188 /* RCTVideoPlayerViewController.m */; }; - D1107C112110259000073188 /* RCTVideoPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C082110259000073188 /* RCTVideoPlayerViewController.m */; }; + 0177D39A27170A7A00F5BE18 /* RCTVideoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0177D39227170A7A00F5BE18 /* RCTVideoManager.swift */; }; + 0177D39B27170A7A00F5BE18 /* UIView+FindUIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0177D39327170A7A00F5BE18 /* UIView+FindUIViewController.swift */; }; + 0177D39C27170A7A00F5BE18 /* RCTVideoPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0177D39427170A7A00F5BE18 /* RCTVideoPlayerViewController.swift */; }; + 0177D39D27170A7A00F5BE18 /* RCTVideoPlayerViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0177D39627170A7A00F5BE18 /* RCTVideoPlayerViewControllerDelegate.swift */; }; + 0177D39E27170A7A00F5BE18 /* RCTVideoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0177D39727170A7A00F5BE18 /* RCTVideoManager.m */; }; + 0177D39F27170A7A00F5BE18 /* RCTVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0177D39927170A7A00F5BE18 /* RCTVideo.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -39,17 +37,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 01450CB5271D5738005D8F6B /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTVideo.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 01489050272001A100E69940 /* DataStructures */ = {isa = PBXFileReference; lastKnownFileType = folder; name = DataStructures; path = Video/DataStructures; sourceTree = ""; }; + 01489051272001A100E69940 /* Features */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Features; path = Video/Features; sourceTree = ""; }; + 0177D39227170A7A00F5BE18 /* RCTVideoManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RCTVideoManager.swift; path = Video/RCTVideoManager.swift; sourceTree = ""; }; + 0177D39327170A7A00F5BE18 /* UIView+FindUIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+FindUIViewController.swift"; path = "Video/UIView+FindUIViewController.swift"; sourceTree = ""; }; + 0177D39427170A7A00F5BE18 /* RCTVideoPlayerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RCTVideoPlayerViewController.swift; path = Video/RCTVideoPlayerViewController.swift; sourceTree = ""; }; + 0177D39527170A7A00F5BE18 /* RCTSwiftLog */ = {isa = PBXFileReference; lastKnownFileType = folder; name = RCTSwiftLog; path = Video/RCTSwiftLog; sourceTree = ""; }; + 0177D39627170A7A00F5BE18 /* RCTVideoPlayerViewControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RCTVideoPlayerViewControllerDelegate.swift; path = Video/RCTVideoPlayerViewControllerDelegate.swift; sourceTree = ""; }; + 0177D39727170A7A00F5BE18 /* RCTVideoManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTVideoManager.m; path = Video/RCTVideoManager.m; sourceTree = ""; }; + 0177D39827170A7A00F5BE18 /* RCTVideo-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "RCTVideo-Bridging-Header.h"; path = "Video/RCTVideo-Bridging-Header.h"; sourceTree = ""; }; + 0177D39927170A7A00F5BE18 /* RCTVideo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RCTVideo.swift; path = Video/RCTVideo.swift; sourceTree = ""; }; 134814201AA4EA6300B7C361 /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTVideo.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 641E28441F0EEC8500443AF6 /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTVideo.a; sourceTree = BUILT_PRODUCTS_DIR; }; - D1107C012110259000073188 /* RCTVideoPlayerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideoPlayerViewController.h; path = Video/RCTVideoPlayerViewController.h; sourceTree = ""; }; - D1107C022110259000073188 /* RCTVideoPlayerViewControllerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideoPlayerViewControllerDelegate.h; path = Video/RCTVideoPlayerViewControllerDelegate.h; sourceTree = ""; }; - D1107C032110259000073188 /* UIView+FindUIViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+FindUIViewController.m"; path = "Video/UIView+FindUIViewController.m"; sourceTree = ""; }; - D1107C042110259000073188 /* UIView+FindUIViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+FindUIViewController.h"; path = "Video/UIView+FindUIViewController.h"; sourceTree = ""; }; - D1107C052110259000073188 /* RCTVideo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTVideo.m; path = Video/RCTVideo.m; sourceTree = ""; }; - D1107C062110259000073188 /* RCTVideoManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTVideoManager.m; path = Video/RCTVideoManager.m; sourceTree = ""; }; - D1107C072110259000073188 /* RCTVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideo.h; path = Video/RCTVideo.h; sourceTree = ""; }; - D1107C082110259000073188 /* RCTVideoPlayerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTVideoPlayerViewController.m; path = Video/RCTVideoPlayerViewController.m; sourceTree = ""; }; - D1107C092110259000073188 /* RCTVideoManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideoManager.h; path = Video/RCTVideoManager.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -88,18 +87,19 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( - D1107C072110259000073188 /* RCTVideo.h */, - D1107C052110259000073188 /* RCTVideo.m */, - D1107C092110259000073188 /* RCTVideoManager.h */, - D1107C062110259000073188 /* RCTVideoManager.m */, - D1107C012110259000073188 /* RCTVideoPlayerViewController.h */, - D1107C082110259000073188 /* RCTVideoPlayerViewController.m */, - D1107C022110259000073188 /* RCTVideoPlayerViewControllerDelegate.h */, - D1107C042110259000073188 /* UIView+FindUIViewController.h */, - D1107C032110259000073188 /* UIView+FindUIViewController.m */, + 01489050272001A100E69940 /* DataStructures */, + 01489051272001A100E69940 /* Features */, + 0177D39527170A7A00F5BE18 /* RCTSwiftLog */, + 0177D39927170A7A00F5BE18 /* RCTVideo.swift */, + 0177D39727170A7A00F5BE18 /* RCTVideoManager.m */, + 0177D39227170A7A00F5BE18 /* RCTVideoManager.swift */, + 0177D39427170A7A00F5BE18 /* RCTVideoPlayerViewController.swift */, + 0177D39627170A7A00F5BE18 /* RCTVideoPlayerViewControllerDelegate.swift */, + 0177D39327170A7A00F5BE18 /* UIView+FindUIViewController.swift */, + 0177D39827170A7A00F5BE18 /* RCTVideo-Bridging-Header.h */, 134814211AA4EA7D00B7C361 /* Products */, - 641E28441F0EEC8500443AF6 /* libRCTVideo.a */, 49E995712048B4CE00EA7890 /* Frameworks */, + 01450CB5271D5738005D8F6B /* libRCTVideo.a */, ); sourceTree = ""; }; @@ -137,7 +137,7 @@ ); name = "RCTVideo-tvOS"; productName = "RCTVideo-tvOS"; - productReference = 641E28441F0EEC8500443AF6 /* libRCTVideo.a */; + productReference = 01450CB5271D5738005D8F6B /* libRCTVideo.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ @@ -151,9 +151,11 @@ TargetAttributes = { 58B511DA1A9E6C8500147676 = { CreatedOnToolsVersion = 6.1.1; + LastSwiftMigration = 1300; }; 641E28431F0EEC8500443AF6 = { CreatedOnToolsVersion = 8.3.3; + LastSwiftMigration = 1300; ProvisioningStyle = Automatic; }; }; @@ -163,6 +165,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 58B511D21A9E6C8500147676; @@ -181,10 +184,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D1107C0A2110259000073188 /* UIView+FindUIViewController.m in Sources */, - D1107C102110259000073188 /* RCTVideoPlayerViewController.m in Sources */, - D1107C0E2110259000073188 /* RCTVideoManager.m in Sources */, - D1107C0C2110259000073188 /* RCTVideo.m in Sources */, + 0177D39D27170A7A00F5BE18 /* RCTVideoPlayerViewControllerDelegate.swift in Sources */, + 0177D39C27170A7A00F5BE18 /* RCTVideoPlayerViewController.swift in Sources */, + 0177D39B27170A7A00F5BE18 /* UIView+FindUIViewController.swift in Sources */, + 0177D39F27170A7A00F5BE18 /* RCTVideo.swift in Sources */, + 0177D39E27170A7A00F5BE18 /* RCTVideoManager.m in Sources */, + 0177D39A27170A7A00F5BE18 /* RCTVideoManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -192,10 +197,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D1107C0B2110259000073188 /* UIView+FindUIViewController.m in Sources */, - D1107C112110259000073188 /* RCTVideoPlayerViewController.m in Sources */, - D1107C0F2110259000073188 /* RCTVideoManager.m in Sources */, - D1107C0D2110259000073188 /* RCTVideo.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -280,30 +281,39 @@ 58B511F01A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/Vendor/SPTPersistentCache/include/**", ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTVideo; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "Video/RCTVideo-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; 58B511F11A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/Vendor/SPTPersistentCache/include/**", ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTVideo; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "Video/RCTVideo-Bridging-Header.h"; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -312,15 +322,19 @@ buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; DEBUG_INFORMATION_FORMAT = dwarf; GCC_NO_COMMON_BLOCKS = YES; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTVideo; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 10.2; }; name = Debug; @@ -330,16 +344,19 @@ buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_NO_COMMON_BLOCKS = YES; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTVideo; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 10.2; }; name = Release; diff --git a/ios/Video/DataStructures/DRMParams.swift b/ios/Video/DataStructures/DRMParams.swift new file mode 100644 index 00000000..64add7e0 --- /dev/null +++ b/ios/Video/DataStructures/DRMParams.swift @@ -0,0 +1,30 @@ +struct DRMParams { + let type: String? + let licenseServer: String? + let headers: Dictionary? + let contentId: String? + let certificateUrl: String? + let base64Certificate: Bool? + + let json: NSDictionary? + + init(_ json: NSDictionary!) { + guard json != nil else { + self.json = nil + self.type = nil + self.licenseServer = nil + self.contentId = nil + self.certificateUrl = nil + self.base64Certificate = nil + self.headers = nil + return + } + self.json = json + self.type = json["type"] as? String + self.licenseServer = json["licenseServer"] as? String + self.contentId = json["contentId"] as? String + self.certificateUrl = json["certificateUrl"] as? String + self.base64Certificate = json["base64Certificate"] as? Bool + self.headers = json["headers"] as? Dictionary + } +} diff --git a/ios/Video/DataStructures/SelectedTrackCriteria.swift b/ios/Video/DataStructures/SelectedTrackCriteria.swift new file mode 100644 index 00000000..7d97b8f2 --- /dev/null +++ b/ios/Video/DataStructures/SelectedTrackCriteria.swift @@ -0,0 +1,18 @@ +struct SelectedTrackCriteria { + let type: String + let value: Any? + + let json: NSDictionary? + + init(_ json: NSDictionary!) { + guard json != nil else { + self.json = nil + self.type = "" + self.value = nil + return + } + self.json = json + self.type = json["type"] as? String ?? "" + self.value = json["value"] + } +} diff --git a/ios/Video/DataStructures/TextTrack.swift b/ios/Video/DataStructures/TextTrack.swift new file mode 100644 index 00000000..b0bdad80 --- /dev/null +++ b/ios/Video/DataStructures/TextTrack.swift @@ -0,0 +1,25 @@ + +struct TextTrack { + let type: String + let language: String + let title: String + let uri: String + + let json: NSDictionary? + + init(_ json: NSDictionary!) { + guard json != nil else { + self.json = nil + self.type = "" + self.language = "" + self.title = "" + self.uri = "" + return + } + self.json = json + self.type = json["type"] as? String ?? "" + self.language = json["language"] as? String ?? "" + self.title = json["title"] as? String ?? "" + self.uri = json["uri"] as? String ?? "" + } +} diff --git a/ios/Video/DataStructures/VideoSource.swift b/ios/Video/DataStructures/VideoSource.swift new file mode 100644 index 00000000..20ab7158 --- /dev/null +++ b/ios/Video/DataStructures/VideoSource.swift @@ -0,0 +1,31 @@ + +struct VideoSource { + let type: String? + let uri: String? + let isNetwork: Bool + let isAsset: Bool + let shouldCache: Bool + let requestHeaders: Dictionary? + + let json: NSDictionary? + + init(_ json: NSDictionary!) { + guard json != nil else { + self.json = nil + self.type = nil + self.uri = nil + self.isNetwork = false + self.isAsset = false + self.shouldCache = false + self.requestHeaders = nil + return + } + self.json = json + self.type = json["type"] as? String + self.uri = json["uri"] as? String + self.isNetwork = json["isNetwork"] as? Bool ?? false + self.isAsset = json["isAsset"] as? Bool ?? false + self.shouldCache = json["shouldCache"] as? Bool ?? false + self.requestHeaders = json["requestHeaders"] as? Dictionary + } +} diff --git a/ios/Video/Features/RCTPictureInPicture.swift b/ios/Video/Features/RCTPictureInPicture.swift new file mode 100644 index 00000000..be9812c4 --- /dev/null +++ b/ios/Video/Features/RCTPictureInPicture.swift @@ -0,0 +1,75 @@ +import AVFoundation +import AVKit +import MediaAccessibility +import React +import Foundation + +#if TARGET_OS_IOS +class RCTPictureInPicture: NSObject, AVPictureInPictureControllerDelegate { + private var _onPictureInPictureStatusChanged: RCTDirectEventBlock? + private var _onRestoreUserInterfaceForPictureInPictureStop: RCTDirectEventBlock? + private var _restoreUserInterfaceForPIPStopCompletionHandler:((Bool) -> Void)? = nil + private var _pipController:AVPictureInPictureController? + private var _isActive:Bool = false + + init(_ onPictureInPictureStatusChanged: @escaping RCTDirectEventBlock, _ onRestoreUserInterfaceForPictureInPictureStop: @escaping RCTDirectEventBlock) { + _onPictureInPictureStatusChanged = onPictureInPictureStatusChanged + _onRestoreUserInterfaceForPictureInPictureStop = onRestoreUserInterfaceForPictureInPictureStop + } + + func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + guard let _onPictureInPictureStatusChanged = _onPictureInPictureStatusChanged else { return } + + _onPictureInPictureStatusChanged([ "isActive": NSNumber(value: true)]) + } + + func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + guard let _onPictureInPictureStatusChanged = _onPictureInPictureStatusChanged else { return } + + _onPictureInPictureStatusChanged([ "isActive": NSNumber(value: false)]) + } + + func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) { + + assert(_restoreUserInterfaceForPIPStopCompletionHandler == nil, "restoreUserInterfaceForPIPStopCompletionHandler was not called after picture in picture was exited.") + + guard let _onRestoreUserInterfaceForPictureInPictureStop = _onRestoreUserInterfaceForPictureInPictureStop else { return } + + _onRestoreUserInterfaceForPictureInPictureStop([:]) + + _restoreUserInterfaceForPIPStopCompletionHandler = completionHandler + } + + func setRestoreUserInterfaceForPIPStopCompletionHandler(_ restore:Bool) { + guard let _restoreUserInterfaceForPIPStopCompletionHandler = _restoreUserInterfaceForPIPStopCompletionHandler else { return } + _restoreUserInterfaceForPIPStopCompletionHandler(restore) + self._restoreUserInterfaceForPIPStopCompletionHandler = nil + } + + func setupPipController(_ playerLayer: AVPlayerLayer?) { + guard playerLayer != nil && AVPictureInPictureController.isPictureInPictureSupported() else { return } + // Create new controller passing reference to the AVPlayerLayer + _pipController = AVPictureInPictureController(playerLayer:playerLayer!) + _pipController?.delegate = self + } + + func setPictureInPicture(_ isActive:Bool) { + if _isActive == isActive { + return + } + _isActive = isActive + + guard let _pipController = _pipController else { return } + + if _isActive && !_pipController.isPictureInPictureActive { + DispatchQueue.main.async(execute: { + _pipController.startPictureInPicture() + }) + } else if !_isActive && _pipController.isPictureInPictureActive { + DispatchQueue.main.async(execute: { + _pipController.stopPictureInPicture() + }) + } + } +} +#endif diff --git a/ios/Video/Features/RCTPlayerObserver.swift b/ios/Video/Features/RCTPlayerObserver.swift new file mode 100644 index 00000000..d17b27da --- /dev/null +++ b/ios/Video/Features/RCTPlayerObserver.swift @@ -0,0 +1,212 @@ +import AVFoundation +import AVKit +import Foundation + +@objc +protocol RCTPlayerObserverHandlerObjc { + func handleDidFailToFinishPlaying(notification:NSNotification!) + func handlePlaybackStalled(notification:NSNotification!) + func handlePlayerItemDidReachEnd(notification:NSNotification!) + // unused +// func handleAVPlayerAccess(notification:NSNotification!) +} + +protocol RCTPlayerObserverHandler: RCTPlayerObserverHandlerObjc { + func handleTimeUpdate(time:CMTime) + func handleReadyForDisplay(changeObject: Any, change:NSKeyValueObservedChange) + func handleTimeMetadataChange(playerItem:AVPlayerItem, change:NSKeyValueObservedChange<[AVMetadataItem]?>) + func handlePlayerItemStatusChange(playerItem:AVPlayerItem, change:NSKeyValueObservedChange) + func handlePlaybackBufferKeyEmpty(playerItem:AVPlayerItem, change:NSKeyValueObservedChange) + func handlePlaybackLikelyToKeepUp(playerItem:AVPlayerItem, change:NSKeyValueObservedChange) + func handlePlaybackRateChange(player: AVPlayer, change: NSKeyValueObservedChange) + func handleExternalPlaybackActiveChange(player: AVPlayer, change: NSKeyValueObservedChange) + func handleViewControllerOverlayViewFrameChange(overlayView:UIView, change:NSKeyValueObservedChange) +} + +class RCTPlayerObserver: NSObject { + + var _handlers: RCTPlayerObserverHandler! + + var player:AVPlayer? { + didSet { + if player == nil { + removePlayerObservers() + removePlayerTimeObserver() + } else { + addPlayerObservers() + addPlayerTimeObserver() + } + } + } + var playerItem:AVPlayerItem? { + didSet { + if playerItem == nil { + removePlayerItemObservers() + } else { + addPlayerItemObservers() + } + } + } + var playerViewController:AVPlayerViewController? { + didSet { + if playerViewController == nil { + removePlayerViewControllerObservers() + } else { + addPlayerViewControllerObservers() + } + } + } + var playerLayer:AVPlayerLayer? { + didSet { + if playerLayer == nil { + removePlayerLayerObserver() + } else { + addPlayerLayerObserver() + } + } + } + + private var _progressUpdateInterval:TimeInterval = 250 + private var _timeObserver:Any? + + private var _playerRateChangeObserver:NSKeyValueObservation? + private var _playerExpernalPlaybackActiveObserver:NSKeyValueObservation? + private var _playerItemStatusObserver:NSKeyValueObservation? + private var _playerPlaybackBufferEmptyObserver:NSKeyValueObservation? + private var _playerPlaybackLikelyToKeepUpObserver:NSKeyValueObservation? + private var _playerTimedMetadataObserver:NSKeyValueObservation? + private var _playerViewControllerReadyForDisplayObserver:NSKeyValueObservation? + private var _playerLayerReadyForDisplayObserver:NSKeyValueObservation? + private var _playerViewControllerOverlayFrameObserver:NSKeyValueObservation? + + deinit { + NotificationCenter.default.removeObserver(_handlers) + } + + func addPlayerObservers() { + guard let player = player else { + return + } + + _playerRateChangeObserver = player.observe(\.rate, changeHandler: _handlers.handlePlaybackRateChange) + _playerExpernalPlaybackActiveObserver = player.observe(\.isExternalPlaybackActive, changeHandler: _handlers.handleExternalPlaybackActiveChange) + } + + func removePlayerObservers() { + _playerRateChangeObserver?.invalidate() + _playerExpernalPlaybackActiveObserver?.invalidate() + } + + func addPlayerItemObservers() { + guard let playerItem = playerItem else { return } + + _playerItemStatusObserver = playerItem.observe(\.status, options: [.new, .old], changeHandler: _handlers.handlePlayerItemStatusChange) + _playerPlaybackBufferEmptyObserver = playerItem.observe(\.isPlaybackBufferEmpty, options: [.new, .old], changeHandler: _handlers.handlePlaybackBufferKeyEmpty) + _playerPlaybackLikelyToKeepUpObserver = playerItem.observe(\.isPlaybackLikelyToKeepUp, options: [.new, .old], changeHandler: _handlers.handlePlaybackLikelyToKeepUp) + _playerTimedMetadataObserver = playerItem.observe(\.timedMetadata, options: [.new], changeHandler: _handlers.handleTimeMetadataChange) + } + + func removePlayerItemObservers() { + _playerItemStatusObserver?.invalidate() + _playerPlaybackBufferEmptyObserver?.invalidate() + _playerPlaybackLikelyToKeepUpObserver?.invalidate() + _playerTimedMetadataObserver?.invalidate() + } + + func addPlayerViewControllerObservers() { + guard let playerViewController = playerViewController else { return } + + _playerViewControllerReadyForDisplayObserver = playerViewController.observe(\.isReadyForDisplay, options: [.new], changeHandler: _handlers.handleReadyForDisplay) + + _playerViewControllerOverlayFrameObserver = playerViewController.contentOverlayView?.observe(\.frame, options: [.new, .old], changeHandler: _handlers.handleViewControllerOverlayViewFrameChange) + } + + func removePlayerViewControllerObservers() { + _playerViewControllerReadyForDisplayObserver?.invalidate() + _playerViewControllerOverlayFrameObserver?.invalidate() + } + + func addPlayerLayerObserver() { + _playerLayerReadyForDisplayObserver = playerLayer?.observe(\.isReadyForDisplay, options: [.new], changeHandler: _handlers.handleReadyForDisplay) + } + + func removePlayerLayerObserver() { + _playerLayerReadyForDisplayObserver?.invalidate() + } + + func addPlayerTimeObserver() { + removePlayerTimeObserver() + let progressUpdateIntervalMS:Float64 = _progressUpdateInterval / 1000 + // @see endScrubbing in AVPlayerDemoPlaybackViewController.m + // of https://developer.apple.com/library/ios/samplecode/AVPlayerDemo/Introduction/Intro.html + _timeObserver = player?.addPeriodicTimeObserver( + forInterval: CMTimeMakeWithSeconds(progressUpdateIntervalMS, preferredTimescale: Int32(NSEC_PER_SEC)), + queue:nil, + using:_handlers.handleTimeUpdate + ) + } + + /* Cancels the previously registered time observer. */ + func removePlayerTimeObserver() { + if let timeObserver = _timeObserver { + player?.removeTimeObserver(timeObserver) + _timeObserver = nil + } + } + + func addTimeObserverIfNotSet() { + if (_timeObserver == nil) { + addPlayerTimeObserver() + } + } + + func replaceTimeObserverIfSet(_ newUpdateInterval:Float64? = nil) { + if let newUpdateInterval = newUpdateInterval { + _progressUpdateInterval = newUpdateInterval + } + if (_timeObserver != nil) { + addPlayerTimeObserver() + } + } + + func attachPlayerEventListeners() { + + NotificationCenter.default.removeObserver(_handlers, + name:NSNotification.Name.AVPlayerItemDidPlayToEndTime, + object:player?.currentItem) + NotificationCenter.default.addObserver(_handlers, + selector:#selector(RCTPlayerObserverHandler.handlePlayerItemDidReachEnd(notification:)), + name:NSNotification.Name.AVPlayerItemDidPlayToEndTime, + object:player?.currentItem) + + NotificationCenter.default.removeObserver(_handlers, + name:NSNotification.Name.AVPlayerItemPlaybackStalled, + object:nil) + NotificationCenter.default.addObserver(_handlers, + selector:#selector(RCTPlayerObserverHandler.handlePlaybackStalled(notification:)), + name:NSNotification.Name.AVPlayerItemPlaybackStalled, + object:nil) + NotificationCenter.default.removeObserver(_handlers, + name: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime, + object:nil) + NotificationCenter.default.addObserver(_handlers, + selector:#selector(RCTPlayerObserverHandler.handleDidFailToFinishPlaying(notification:)), + name: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime, + object:nil) + // Unused +// NotificationCenter.default.removeObserver(_handlers, +// name:NSNotification.Name.AVPlayerItemNewAccessLogEntry, +// object:nil) +// NotificationCenter.default.addObserver(_handlers, +// selector: #selector(RCTPlayerObserverHandler.handleAVPlayerAccess(notification:)), +// name:NSNotification.Name.AVPlayerItemNewAccessLogEntry, +// object:nil) + + } + + func clearPlayer() { + player = nil + playerItem = nil + NotificationCenter.default.removeObserver(_handlers) + } +} diff --git a/ios/Video/Features/RCTPlayerOperations.swift b/ios/Video/Features/RCTPlayerOperations.swift new file mode 100644 index 00000000..8d70f46d --- /dev/null +++ b/ios/Video/Features/RCTPlayerOperations.swift @@ -0,0 +1,160 @@ +import AVFoundation +import MediaAccessibility + +let RCTVideoUnset = -1 + +/*! + * Collection of mutating functions + */ +enum RCTPlayerOperations { + + static func setSideloadedText(player:AVPlayer?, textTracks:[TextTrack]?, criteria:SelectedTrackCriteria?) { + let type = criteria?.type + let textTracks:[TextTrack]! = textTracks ?? RCTVideoUtils.getTextTrackInfo(player) + + // The first few tracks will be audio & video track + let firstTextIndex:Int = 0 + for firstTextIndex in 0..<(player?.currentItem?.tracks.count ?? 0) { + if player?.currentItem?.tracks[firstTextIndex].assetTrack?.hasMediaCharacteristic(.legible) ?? false { + break + } + } + + var selectedTrackIndex:Int = RCTVideoUnset + + if (type == "disabled") { + // Do nothing. We want to ensure option is nil + } else if (type == "language") { + let selectedValue = criteria?.value as? String + for i in 0.. index { + selectedTrackIndex = index + } + } + } + + // in the situation that a selected text track is not available (eg. specifies a textTrack not available) + if (type != "disabled") && selectedTrackIndex == RCTVideoUnset { + let captioningMediaCharacteristics = MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics(.user) as! CFArray + let captionSettings = captioningMediaCharacteristics as? [AnyHashable] + if ((captionSettings?.contains(AVMediaCharacteristic.transcribesSpokenDialogForAccessibility)) != nil) { + selectedTrackIndex = 0 // If we can't find a match, use the first available track + let systemLanguage = NSLocale.preferredLanguages.first + for i in 0.. index { + mediaOption = group.options[index] + } + } + } else { // default. invalid type or "system" + player?.currentItem?.selectMediaOptionAutomatically(in: group) + return + } + + // If a match isn't found, option will be nil and text tracks will be disabled + player?.currentItem?.select(mediaOption, in:group) + } + + static func setMediaSelectionTrackForCharacteristic(player:AVPlayer?, characteristic:AVMediaCharacteristic, criteria:SelectedTrackCriteria?) { + let type = criteria?.type + let group:AVMediaSelectionGroup! = player?.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: characteristic) + var mediaOption:AVMediaSelectionOption! + + if (type == "disabled") { + // Do nothing. We want to ensure option is nil + } else if (type == "language") || (type == "title") { + let value = criteria?.value as? String + for i in 0.. index { + mediaOption = group.options[index] + } + } + } else if let group = group { // default. invalid type or "system" + player?.currentItem?.selectMediaOptionAutomatically(in: group) + return + } + + if let group = group { + // If a match isn't found, option will be nil and text tracks will be disabled + player?.currentItem?.select(mediaOption, in:group) + } + + } +} diff --git a/ios/Video/Features/RCTResourceLoaderDelegate.swift b/ios/Video/Features/RCTResourceLoaderDelegate.swift new file mode 100644 index 00000000..5ead7fef --- /dev/null +++ b/ios/Video/Features/RCTResourceLoaderDelegate.swift @@ -0,0 +1,258 @@ +import AVFoundation + +class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSessionDelegate { + + private var _loadingRequest:AVAssetResourceLoadingRequest? + private var _requestingCertificate:Bool = false + private var _requestingCertificateErrored:Bool = false + private var _drm: DRMParams? + private var _reactTag: NSNumber? + private var _onVideoError: RCTDirectEventBlock? + private var _onGetLicense: RCTDirectEventBlock? + + + init( + asset: AVURLAsset, + drm: DRMParams?, + onVideoError: RCTDirectEventBlock?, + onGetLicense: RCTDirectEventBlock?, + reactTag: NSNumber + ) { + super.init() + let queue = DispatchQueue(label: "assetQueue") + asset.resourceLoader.setDelegate(self, queue: queue) + _reactTag = reactTag + _onVideoError = onVideoError + _onGetLicense = onGetLicense + _drm = drm + } + + deinit { + _loadingRequest?.finishLoading() + } + + func resourceLoader(_ resourceLoader:AVAssetResourceLoader, shouldWaitForRenewalOfRequestedResource renewalRequest:AVAssetResourceRenewalRequest) -> Bool { + return loadingRequestHandling(renewalRequest) + } + + func resourceLoader(_ resourceLoader:AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest:AVAssetResourceLoadingRequest) -> Bool { + return loadingRequestHandling(loadingRequest) + } + + func resourceLoader(_ resourceLoader:AVAssetResourceLoader, didCancel loadingRequest:AVAssetResourceLoadingRequest) { + NSLog("didCancelLoadingRequest") + } + + func base64DataFromBase64String(base64String:String?) -> Data? { + if let base64String = base64String { + return Data(base64Encoded:base64String) + } + return nil + } + + func setLicenseResult(_ license:String!) { + guard let respondData = self.base64DataFromBase64String(base64String: license), + let _loadingRequest = _loadingRequest else { + setLicenseResultError("No data from JS license response") + return + } + let dataRequest:AVAssetResourceLoadingDataRequest! = _loadingRequest.dataRequest + dataRequest.respond(with: respondData) + _loadingRequest.finishLoading() + } + + func setLicenseResultError(_ error:String!) { + if _loadingRequest != nil { + self.finishLoadingWithError(error: RCTVideoErrorHandler.fromJSPart(error)) + } + } + + func finishLoadingWithError(error:NSError!) -> Bool { + if let _loadingRequest = _loadingRequest, let error = error { + let licenseError:NSError! = error + _loadingRequest.finishLoading(with: licenseError) + + _onVideoError?([ + "error": [ + "code": NSNumber(value: error.code), + "localizedDescription": error.localizedDescription == nil ? "" : error.localizedDescription, + "localizedFailureReason": ((error as NSError).localizedFailureReason == nil ? "" : (error as NSError).localizedFailureReason) ?? "", + "localizedRecoverySuggestion": ((error as NSError).localizedRecoverySuggestion == nil ? "" : (error as NSError).localizedRecoverySuggestion) ?? "", + "domain": (error as NSError).domain + ], + "target": _reactTag + ]) + + } + return false + } + + func loadingRequestHandling(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool { + if _requestingCertificate { + return true + } else if _requestingCertificateErrored { + return false + } + _loadingRequest = loadingRequest + + let url = loadingRequest.request.url + guard let _drm = _drm else { + return finishLoadingWithError(error: RCTVideoErrorHandler.noDRMData) + } + + var contentId:String! + let contentIdOverride:String! = _drm.contentId + if contentIdOverride != nil { + contentId = contentIdOverride + } else if (_onGetLicense != nil) { + contentId = url?.host + } else { + contentId = url?.absoluteString.replacingOccurrences(of: "skd://", with:"") + } + + let drmType:String! = _drm.type + guard drmType == "fairplay" else { + return finishLoadingWithError(error: RCTVideoErrorHandler.noDRMData) + } + + let certificateStringUrl:String! = _drm.certificateUrl + guard let certificateStringUrl = certificateStringUrl, let certificateURL = URL(string: certificateStringUrl.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) ?? "") else { + return finishLoadingWithError(error: RCTVideoErrorHandler.noCertificateURL) + } + DispatchQueue.global().async { [weak self] in + guard let self = self else { return } + var certificateData:Data? + if (_drm.base64Certificate != nil) { + certificateData = Data(base64Encoded: certificateData! as Data, options: .ignoreUnknownCharacters) + } else { + do { + certificateData = try Data(contentsOf: certificateURL) + } catch {} + } + + guard let certificateData = certificateData else { + self.finishLoadingWithError(error: RCTVideoErrorHandler.noCertificateData) + self._requestingCertificateErrored = true + return + } + + var contentIdData:NSData! + if self._onGetLicense != nil { + contentIdData = contentId.data(using: .utf8) as NSData? + } else { + contentIdData = NSData(bytes: contentId.cString(using: String.Encoding.utf8), length:contentId.lengthOfBytes(using: String.Encoding.utf8)) + } + + let dataRequest:AVAssetResourceLoadingDataRequest! = loadingRequest.dataRequest + guard dataRequest != nil else { + self.finishLoadingWithError(error: RCTVideoErrorHandler.noCertificateData) + self._requestingCertificateErrored = true + return + } + + var spcError:NSError! + var spcData: Data? + do { + spcData = try loadingRequest.streamingContentKeyRequestData(forApp: certificateData, contentIdentifier: contentIdData as Data, options: nil) + } catch let spcError { + print("SPC error") + } + // Request CKC to the server + var licenseServer:String! = _drm.licenseServer + if spcError != nil { + self.finishLoadingWithError(error: spcError) + self._requestingCertificateErrored = true + } + + guard spcData != nil else { + self.finishLoadingWithError(error: RCTVideoErrorHandler.noSPC) + self._requestingCertificateErrored = true + return + } + + // js client has a onGetLicense callback and will handle license fetching + if let _onGetLicense = self._onGetLicense { + let base64Encoded = spcData?.base64EncodedString(options: []) + self._requestingCertificate = true + if licenseServer == nil { + licenseServer = "" + } + _onGetLicense(["licenseUrl": licenseServer, + "contentId": contentId, + "spcBase64": base64Encoded, + "target": self._reactTag]) + + + } else if licenseServer != nil { + self.fetchLicense( + licenseServer: licenseServer, + spcData: spcData, + contentId: contentId, + dataRequest: dataRequest + ) + } + } + return true + } + + func fetchLicense( + licenseServer: String, + spcData: Data?, + contentId: String, + dataRequest: AVAssetResourceLoadingDataRequest! + ) { + var request = URLRequest(url: URL(string: licenseServer)!) + request.httpMethod = "POST" + + // HEADERS + if let headers = _drm?.headers { + for item in headers { + guard let key = item.key as? String, let value = item.value as? String else { + continue + } + request.setValue(value, forHTTPHeaderField: key) + } + } + + if (_onGetLicense != nil) { + request.httpBody = spcData + } else { + let spcEncoded = spcData?.base64EncodedString(options: []) + let spcUrlEncoded = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, spcEncoded as? CFString? as! CFString, nil, "?=&+" as CFString, CFStringBuiltInEncodings.UTF8.rawValue) as? String + let post = String(format:"spc=%@&%@", spcUrlEncoded as! CVarArg, contentId) + let postData = post.data(using: String.Encoding.utf8, allowLossyConversion:true) + request.httpBody = postData + } + + let postDataTask = URLSession.shared.dataTask(with: request as URLRequest, completionHandler:{ [weak self] (data:Data!,response:URLResponse!,error:Error!) in + guard let self = self else { return } + let httpResponse:HTTPURLResponse! = response as! HTTPURLResponse + guard error == nil else { + print("Error getting license from \(licenseServer), HTTP status code \(httpResponse.statusCode)") + self.finishLoadingWithError(error: error as NSError?) + self._requestingCertificateErrored = true + return + } + guard httpResponse.statusCode == 200 else { + print("Error getting license from \(licenseServer), HTTP status code \(httpResponse.statusCode)") + self.finishLoadingWithError(error: RCTVideoErrorHandler.licenseRequestNotOk(httpResponse.statusCode)) + self._requestingCertificateErrored = true + return + } + + guard data != nil else { + self.finishLoadingWithError(error: RCTVideoErrorHandler.noDataFromLicenseRequest) + self._requestingCertificateErrored = true + return + } + + if (self._onGetLicense != nil) { + dataRequest.respond(with: data) + } else if let decodedData = Data(base64Encoded: data, options: []) { + dataRequest.respond(with: decodedData) + } + self._loadingRequest?.finishLoading() + }) + postDataTask.resume() + } +} diff --git a/ios/Video/Features/RCTVideoErrorHandling.swift b/ios/Video/Features/RCTVideoErrorHandling.swift new file mode 100644 index 00000000..2b65a969 --- /dev/null +++ b/ios/Video/Features/RCTVideoErrorHandling.swift @@ -0,0 +1,83 @@ +enum RCTVideoError : Int { + case fromJSPart + case licenseRequestNotOk + case noDataFromLicenseRequest + case noSPC + case noDataRequest + case noCertificateData + case noCertificateURL + case noFairplayDRM + case noDRMData +} + +enum RCTVideoErrorHandler { + + static let noDRMData: NSError = NSError( + domain: "RCTVideo", + code: RCTVideoError.noDRMData.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining DRM license.", + NSLocalizedFailureReasonErrorKey: "No drm object found.", + NSLocalizedRecoverySuggestionErrorKey: "Have you specified the 'drm' prop?" + ]) + + static let noCertificateURL: NSError = NSError( + domain: "RCTVideo", + code: RCTVideoError.noCertificateURL.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining DRM License.", + NSLocalizedFailureReasonErrorKey: "No certificate URL has been found.", + NSLocalizedRecoverySuggestionErrorKey: "Did you specified the prop certificateUrl?" + ]) + + static let noCertificateData: NSError = NSError( + domain: "RCTVideo", + code: RCTVideoError.noCertificateData.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining DRM license.", + NSLocalizedFailureReasonErrorKey: "No certificate data obtained from the specificied url.", + NSLocalizedRecoverySuggestionErrorKey: "Have you specified a valid 'certificateUrl'?" + ]) + + static let noSPC:NSError! = NSError( + domain: "RCTVideo", + code: RCTVideoError.noSPC.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining license.", + NSLocalizedFailureReasonErrorKey: "No spc received.", + NSLocalizedRecoverySuggestionErrorKey: "Check your DRM config." + ]) + + static let noDataFromLicenseRequest:NSError! = NSError( + domain: "RCTVideo", + code: RCTVideoError.noDataFromLicenseRequest.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining DRM license.", + NSLocalizedFailureReasonErrorKey: "No data received from the license server.", + NSLocalizedRecoverySuggestionErrorKey: "Is the licenseServer ok?." + ]) + + static func licenseRequestNotOk(_ statusCode: Int) -> NSError { + return NSError( + domain: "RCTVideo", + code: RCTVideoError.licenseRequestNotOk.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining license.", + NSLocalizedFailureReasonErrorKey: String( + format:"License server responded with status code %li", + (statusCode) + ), + NSLocalizedRecoverySuggestionErrorKey: "Did you send the correct data to the license Server? Is the server ok?" + ]) + } + + static func fromJSPart(_ error: String) -> NSError { + return NSError(domain: "RCTVideo", + code: RCTVideoError.fromJSPart.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: error, + NSLocalizedFailureReasonErrorKey: error, + NSLocalizedRecoverySuggestionErrorKey: error + ]) + } +} diff --git a/ios/Video/Features/RCTVideoSave.swift b/ios/Video/Features/RCTVideoSave.swift new file mode 100644 index 00000000..ff8155ec --- /dev/null +++ b/ios/Video/Features/RCTVideoSave.swift @@ -0,0 +1,75 @@ +import AVFoundation + +enum RCTVideoSave { + + static func save( + options:NSDictionary!, + resolve: @escaping RCTPromiseResolveBlock, + reject:@escaping RCTPromiseRejectBlock, + + playerItem: AVPlayerItem? + ) { + let asset:AVAsset! = playerItem?.asset + + guard asset != nil else { + reject("ERROR_ASSET_NIL", "Asset is nil", nil) + return + } + + guard let exportSession = AVAssetExportSession(asset: asset, presetName:AVAssetExportPresetHighestQuality) else { + reject("ERROR_COULD_NOT_CREATE_EXPORT_SESSION", "Could not create export session", nil) + return + } + var path:String! = nil + path = RCTVideoSave.generatePathInDirectory( + directory: URL(fileURLWithPath: RCTVideoSave.cacheDirectoryPath() ?? "").appendingPathComponent("Videos").path, + withExtension: ".mp4") + let url:NSURL! = NSURL.fileURL(withPath: path) as NSURL + exportSession.outputFileType = AVFileType.mp4 + exportSession.outputURL = url as URL? + exportSession.videoComposition = playerItem?.videoComposition + exportSession.shouldOptimizeForNetworkUse = true + exportSession.exportAsynchronously(completionHandler: { + + switch (exportSession.status) { + case .failed: + reject("ERROR_COULD_NOT_EXPORT_VIDEO", "Could not export video", exportSession.error) + break + case .cancelled: + reject("ERROR_EXPORT_SESSION_CANCELLED", "Export session was cancelled", exportSession.error) + break + default: + resolve(["uri": url.absoluteString]) + break + } + + }) + } + + static func generatePathInDirectory(directory: String?, withExtension `extension`: String?) -> String? { + let fileName = UUID().uuidString + (`extension` ?? "") + RCTVideoSave.ensureDirExists(withPath: directory) + return URL(fileURLWithPath: directory ?? "").appendingPathComponent(fileName).path + } + + static func cacheDirectoryPath() -> String? { + let array = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).map(\.path) + return array[0] + } + + static func ensureDirExists(withPath path: String?) -> Bool { + var isDir: ObjCBool = false + var error: Error? + let exists = FileManager.default.fileExists(atPath: path ?? "", isDirectory: &isDir) + if !(exists && isDir.boolValue) { + do { + try FileManager.default.createDirectory(atPath: path ?? "", withIntermediateDirectories: true, attributes: nil) + } catch { + } + if error != nil { + return false + } + } + return true + } +} diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift new file mode 100644 index 00000000..51a6aae7 --- /dev/null +++ b/ios/Video/Features/RCTVideoUtils.swift @@ -0,0 +1,143 @@ +import AVFoundation + +/*! + * Collection of pure functions + */ +enum RCTVideoUtils { + + /*! + * Calculates and returns the playable duration of the current player item using its loaded time ranges. + * + * \returns The playable duration of the current player item in seconds. + */ + static func calculatePlayableDuration(_ player:AVPlayer?) -> NSNumber { + guard let player = player, + let video:AVPlayerItem = player.currentItem, + video.status == AVPlayerItem.Status.readyToPlay else { + return 0 + } + + var effectiveTimeRange:CMTimeRange? + for (_, value) in video.loadedTimeRanges.enumerated() { + let timeRange:CMTimeRange = value.timeRangeValue + if CMTimeRangeContainsTime(timeRange, time: video.currentTime()) { + effectiveTimeRange = timeRange + break + } + } + + if let effectiveTimeRange = effectiveTimeRange { + let playableDuration:Float64 = CMTimeGetSeconds(CMTimeRangeGetEnd(effectiveTimeRange)) + if playableDuration > 0 { + return playableDuration as NSNumber + } + } + + return 0 + } + + static func urlFilePath(filepath:NSString!) -> NSURL! { + if filepath.contains("file://") { + return NSURL(string: filepath as String) + } + + // if no file found, check if the file exists in the Document directory + let paths:[String]! = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) + var relativeFilePath:String! = filepath.lastPathComponent + // the file may be multiple levels below the documents directory + let fileComponents:[String]! = filepath.components(separatedBy: "Documents/") + if fileComponents.count > 1 { + relativeFilePath = fileComponents[1] + } + + let path:String! = (paths.first! as NSString).appendingPathComponent(relativeFilePath) + if FileManager.default.fileExists(atPath: path) { + return NSURL.fileURL(withPath: path) as NSURL + } + return nil + } + + static func playerItemSeekableTimeRange(_ player:AVPlayer?) -> CMTimeRange { + if let playerItem = player?.currentItem, + playerItem.status == .readyToPlay, + let firstItem = playerItem.seekableTimeRanges.first { + return firstItem.timeRangeValue + } + + return (CMTimeRange.zero) + } + + static func playerItemDuration(_ player:AVPlayer?) -> CMTime { + if let playerItem = player?.currentItem, + playerItem.status == .readyToPlay { + return(playerItem.duration) + } + + return(CMTime.invalid) + } + + static func calculateSeekableDuration(_ player:AVPlayer?) -> NSNumber { + let timeRange:CMTimeRange = RCTVideoUtils.playerItemSeekableTimeRange(player) + if CMTIME_IS_NUMERIC(timeRange.duration) + { + return NSNumber(value: CMTimeGetSeconds(timeRange.duration)) + } + return 0 + } + + static func getAudioTrackInfo(_ player:AVPlayer?) -> [AnyObject]! { + guard let player = player else { + return [] + } + + let audioTracks:NSMutableArray! = NSMutableArray() + let group = player.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: .audible) + for i in 0..<(group?.options.count ?? 0) { + let currentOption = group?.options[i] + var title = "" + let values = currentOption?.commonMetadata.map(\.value) + if (values?.count ?? 0) > 0, let value = values?[0] { + title = value as! String + } + let language:String! = currentOption?.extendedLanguageTag ?? "" + let audioTrack = [ + "index": NSNumber(value: i), + "title": title, + "language": language + ] as [String : Any] + audioTracks.add(audioTrack) + } + return audioTracks as [AnyObject]? + } + + static func getTextTrackInfo(_ player:AVPlayer?) -> [TextTrack]! { + guard let player = player else { + return [] + } + + // if streaming video, we extract the text tracks + var textTracks:[TextTrack] = [] + let group = player.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: .legible) + for i in 0..<(group?.options.count ?? 0) { + let currentOption = group?.options[i] + var title = "" + let values = currentOption?.commonMetadata.map(\.value) + if (values?.count ?? 0) > 0, let value = values?[0] { + title = value as! String + } + let language:String! = currentOption?.extendedLanguageTag ?? "" + let textTrack = TextTrack([ + "index": NSNumber(value: i), + "title": title, + "language": language + ]) + textTracks.append(textTrack) + } + return textTracks + } + + // UNUSED + static func getCurrentTime(playerItem:AVPlayerItem?) -> Float { + return Float(CMTimeGetSeconds(playerItem?.currentTime() ?? .zero)) + } +} diff --git a/ios/Video/RCTSwiftLog/RCTSwiftLog.h b/ios/Video/RCTSwiftLog/RCTSwiftLog.h new file mode 100644 index 00000000..8f9deab7 --- /dev/null +++ b/ios/Video/RCTSwiftLog/RCTSwiftLog.h @@ -0,0 +1,11 @@ +#import + +@interface RCTSwiftLog : NSObject + ++ (void)error:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; ++ (void)warn:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; ++ (void)info:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; ++ (void)log:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; ++ (void)trace:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; + +@end diff --git a/ios/Video/RCTSwiftLog/RCTSwiftLog.m b/ios/Video/RCTSwiftLog/RCTSwiftLog.m new file mode 100644 index 00000000..4e153846 --- /dev/null +++ b/ios/Video/RCTSwiftLog/RCTSwiftLog.m @@ -0,0 +1,32 @@ +#import + +#import "RCTSwiftLog.h" + +@implementation RCTSwiftLog + ++ (void)info:(NSString *)message file:(NSString *)file line:(NSUInteger)line +{ + _RCTLogNativeInternal(RCTLogLevelInfo, file.UTF8String, (int)line, @"%@", message); +} + ++ (void)warn:(NSString *)message file:(NSString *)file line:(NSUInteger)line +{ + _RCTLogNativeInternal(RCTLogLevelWarning, file.UTF8String, (int)line, @"%@", message); +} + ++ (void)error:(NSString *)message file:(NSString *)file line:(NSUInteger)line +{ + _RCTLogNativeInternal(RCTLogLevelError, file.UTF8String, (int)line, @"%@", message); +} + ++ (void)log:(NSString *)message file:(NSString *)file line:(NSUInteger)line +{ + _RCTLogNativeInternal(RCTLogLevelInfo, file.UTF8String, (int)line, @"%@", message); +} + ++ (void)trace:(NSString *)message file:(NSString *)file line:(NSUInteger)line +{ + _RCTLogNativeInternal(RCTLogLevelTrace, file.UTF8String, (int)line, @"%@", message); +} + +@end diff --git a/ios/Video/RCTSwiftLog/RCTSwiftLog.swift b/ios/Video/RCTSwiftLog/RCTSwiftLog.swift new file mode 100644 index 00000000..ec39d9b1 --- /dev/null +++ b/ios/Video/RCTSwiftLog/RCTSwiftLog.swift @@ -0,0 +1,53 @@ +// +// RCTLog.swift +// WebViewExample +// +// Created by Jimmy Dee on 4/5/17. +// Copyright © 2017 Branch Metrics. All rights reserved. +// + +/* + * Under at least some conditions, output from NSLog has been unavailable in the RNBranch module. + * Hence that module uses the RCTLog macros from . The React logger is nicer than + * NSLog anyway, since it provides log levels with runtime filtering, file and line context and + * an identifier for the thread that logged the message. + * + * This wrapper lets you use functions with the same name in Swift. For example: + * + * RCTLogInfo("application launched") + * + * generates + * + * 2017-04-06 12:31:09.611 [info][tid:main][AppDelegate.swift:18] application launched + * + * This is currently part of this sample app. There may be some issues integrating it into an + * Objective-C library, either react-native-branch or react-native itself, but it may find its + * way into one or the other eventually. Feel free to reuse it as desired. + */ + +func RCTLogError(_ message: String, _ file: String=#file, _ line: UInt=#line) { + RCTSwiftLog.error(message, file: file, line: line) +} + +func RCTLogWarn(_ message: String, _ file: String=#file, _ line: UInt=#line) { + RCTSwiftLog.warn(message, file: file, line: line) +} + +func RCTLogInfo(_ message: String, _ file: String=#file, _ line: UInt=#line) { + RCTSwiftLog.info(message, file: file, line: line) +} + +func RCTLog(_ message: String, _ file: String=#file, _ line: UInt=#line) { + RCTSwiftLog.log(message, file: file, line: line) +} + +func RCTLogTrace(_ message: String, _ file: String=#file, _ line: UInt=#line) { + RCTSwiftLog.trace(message, file: file, line: line) +} + +func DebugLog(_ message: String) { +#if DEBUG + print(message) +#endif +} + diff --git a/ios/Video/RCTVideo-Bridging-Header.h b/ios/Video/RCTVideo-Bridging-Header.h new file mode 100644 index 00000000..ad6089be --- /dev/null +++ b/ios/Video/RCTVideo-Bridging-Header.h @@ -0,0 +1,7 @@ +#import +#import "RCTSwiftLog.h" + +#if __has_include() +#import "RCTVideoCache.h" +#endif + diff --git a/ios/Video/RCTVideo.h b/ios/Video/RCTVideo.h deleted file mode 100644 index 6fee2996..00000000 --- a/ios/Video/RCTVideo.h +++ /dev/null @@ -1,67 +0,0 @@ -#import -#import "AVKit/AVKit.h" -#import "UIView+FindUIViewController.h" -#import "RCTVideoPlayerViewController.h" -#import "RCTVideoPlayerViewControllerDelegate.h" -#import -#import - -#if __has_include() -#import -#import -#import -#endif - -@class RCTEventDispatcher; -#if __has_include() -@interface RCTVideo : UIView -#elif TARGET_OS_TV -@interface RCTVideo : UIView -#else -@interface RCTVideo : UIView -#endif - -@property (nonatomic, copy) RCTDirectEventBlock onVideoLoadStart; -@property (nonatomic, copy) RCTDirectEventBlock onVideoLoad; -@property (nonatomic, copy) RCTDirectEventBlock onVideoBuffer; -@property (nonatomic, copy) RCTDirectEventBlock onVideoError; -@property (nonatomic, copy) RCTDirectEventBlock onVideoProgress; -@property (nonatomic, copy) RCTDirectEventBlock onBandwidthUpdate; -@property (nonatomic, copy) RCTDirectEventBlock onVideoSeek; -@property (nonatomic, copy) RCTDirectEventBlock onVideoEnd; -@property (nonatomic, copy) RCTDirectEventBlock onTimedMetadata; -@property (nonatomic, copy) RCTDirectEventBlock onVideoAudioBecomingNoisy; -@property (nonatomic, copy) RCTDirectEventBlock onVideoFullscreenPlayerWillPresent; -@property (nonatomic, copy) RCTDirectEventBlock onVideoFullscreenPlayerDidPresent; -@property (nonatomic, copy) RCTDirectEventBlock onVideoFullscreenPlayerWillDismiss; -@property (nonatomic, copy) RCTDirectEventBlock onVideoFullscreenPlayerDidDismiss; -@property (nonatomic, copy) RCTDirectEventBlock onReadyForDisplay; -@property (nonatomic, copy) RCTDirectEventBlock onPlaybackStalled; -@property (nonatomic, copy) RCTDirectEventBlock onPlaybackResume; -@property (nonatomic, copy) RCTDirectEventBlock onPlaybackRateChange; -@property (nonatomic, copy) RCTDirectEventBlock onVideoExternalPlaybackChange; -@property (nonatomic, copy) RCTDirectEventBlock onPictureInPictureStatusChanged; -@property (nonatomic, copy) RCTDirectEventBlock onRestoreUserInterfaceForPictureInPictureStop; -@property (nonatomic, copy) RCTDirectEventBlock onGetLicense; - -typedef NS_ENUM(NSInteger, RCTVideoError) { - RCTVideoErrorFromJSPart, - RCTVideoErrorLicenseRequestNotOk, - RCTVideoErrorNoDataFromLicenseRequest, - RCTVideoErrorNoSPC, - RCTVideoErrorNoDataRequest, - RCTVideoErrorNoCertificateData, - RCTVideoErrorNoCertificateURL, - RCTVideoErrorNoFairplayDRM, - RCTVideoErrorNoDRMData -}; - -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - -- (AVPlayerViewController*)createPlayerViewController:(AVPlayer*)player withPlayerItem:(AVPlayerItem*)playerItem; - -- (void)save:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject; -- (void)setLicenseResult:(NSString * )license; -- (BOOL)setLicenseResultError:(NSString * )error; - -@end diff --git a/ios/Video/RCTVideo.m b/ios/Video/RCTVideo.m deleted file mode 100644 index 989167a6..00000000 --- a/ios/Video/RCTVideo.m +++ /dev/null @@ -1,2013 +0,0 @@ -#import -#import "RCTVideo.h" -#import -#import -#import -#include -#include - -static NSString *const statusKeyPath = @"status"; -static NSString *const playbackLikelyToKeepUpKeyPath = @"playbackLikelyToKeepUp"; -static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; -static NSString *const readyForDisplayKeyPath = @"readyForDisplay"; -static NSString *const playbackRate = @"rate"; -static NSString *const timedMetadata = @"timedMetadata"; -static NSString *const externalPlaybackActive = @"externalPlaybackActive"; - -static int const RCTVideoUnset = -1; - -#ifdef DEBUG - #define DebugLog(...) NSLog(__VA_ARGS__) -#else - #define DebugLog(...) (void)0 -#endif - -@implementation RCTVideo -{ - AVPlayer *_player; - AVPlayerItem *_playerItem; - NSDictionary *_source; - BOOL _playerItemObserversSet; - BOOL _playerBufferEmpty; - AVPlayerLayer *_playerLayer; - BOOL _playerLayerObserverSet; - RCTVideoPlayerViewController *_playerViewController; - NSURL *_videoURL; - BOOL _requestingCertificate; - BOOL _requestingCertificateErrored; - - /* DRM */ - NSDictionary *_drm; - AVAssetResourceLoadingRequest *_loadingRequest; - - /* Required to publish events */ - RCTEventDispatcher *_eventDispatcher; - BOOL _playbackRateObserverRegistered; - BOOL _isExternalPlaybackActiveObserverRegistered; - BOOL _videoLoadStarted; - - bool _pendingSeek; - float _pendingSeekTime; - float _lastSeekTime; - - /* For sending videoProgress events */ - Float64 _progressUpdateInterval; - BOOL _controls; - id _timeObserver; - - /* Keep track of any modifiers, need to be applied after each play */ - float _volume; - float _rate; - float _maxBitRate; - - BOOL _automaticallyWaitsToMinimizeStalling; - BOOL _muted; - BOOL _paused; - BOOL _repeat; - BOOL _allowsExternalPlayback; - NSArray * _textTracks; - NSDictionary * _selectedTextTrack; - NSDictionary * _selectedAudioTrack; - BOOL _playbackStalled; - BOOL _playInBackground; - BOOL _preventsDisplaySleepDuringVideoPlayback; - float _preferredForwardBufferDuration; - BOOL _playWhenInactive; - BOOL _pictureInPicture; - NSString * _ignoreSilentSwitch; - NSString * _mixWithOthers; - NSString * _resizeMode; - BOOL _fullscreen; - BOOL _fullscreenAutorotate; - NSString * _fullscreenOrientation; - BOOL _fullscreenPlayerPresented; - NSString *_filterName; - BOOL _filterEnabled; - UIViewController * _presentingViewController; -#if __has_include() - RCTVideoCache * _videoCache; -#endif -#if TARGET_OS_IOS - void (^__strong _Nonnull _restoreUserInterfaceForPIPStopCompletionHandler)(BOOL); - AVPictureInPictureController *_pipController; -#endif -} - -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher -{ - if ((self = [super init])) { - _eventDispatcher = eventDispatcher; - _automaticallyWaitsToMinimizeStalling = YES; - _playbackRateObserverRegistered = NO; - _isExternalPlaybackActiveObserverRegistered = NO; - _playbackStalled = NO; - _rate = 1.0; - _volume = 1.0; - _resizeMode = @"AVLayerVideoGravityResizeAspectFill"; - _fullscreenAutorotate = YES; - _fullscreenOrientation = @"all"; - _pendingSeek = false; - _pendingSeekTime = 0.0f; - _lastSeekTime = 0.0f; - _progressUpdateInterval = 250; - _controls = NO; - _playerBufferEmpty = YES; - _playInBackground = false; - _preventsDisplaySleepDuringVideoPlayback = true; - _preferredForwardBufferDuration = 0.0f; - _allowsExternalPlayback = YES; - _playWhenInactive = false; - _pictureInPicture = false; - _ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey - _mixWithOthers = @"inherit"; // inherit, mix, duck -#if TARGET_OS_IOS - _restoreUserInterfaceForPIPStopCompletionHandler = NULL; -#endif -#if __has_include() - _videoCache = [RCTVideoCache sharedInstance]; -#endif - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationWillResignActive:) - name:UIApplicationWillResignActiveNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationDidEnterBackground:) - name:UIApplicationDidEnterBackgroundNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationWillEnterForeground:) - name:UIApplicationWillEnterForegroundNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(audioRouteChanged:) - name:AVAudioSessionRouteChangeNotification - object:nil]; - } - - return self; -} - -- (RCTVideoPlayerViewController*)createPlayerViewController:(AVPlayer*)player - withPlayerItem:(AVPlayerItem*)playerItem { - RCTVideoPlayerViewController* viewController = [[RCTVideoPlayerViewController alloc] init]; - viewController.showsPlaybackControls = YES; - viewController.rctDelegate = self; - viewController.preferredOrientation = _fullscreenOrientation; - - viewController.view.frame = self.bounds; - viewController.player = player; - return viewController; -} - -/* --------------------------------------------------------- - ** Get the duration for a AVPlayerItem. - ** ------------------------------------------------------- */ - -- (CMTime)playerItemDuration -{ - AVPlayerItem *playerItem = [_player currentItem]; - if (playerItem.status == AVPlayerItemStatusReadyToPlay) - { - return([playerItem duration]); - } - - return(kCMTimeInvalid); -} - -- (CMTimeRange)playerItemSeekableTimeRange -{ - AVPlayerItem *playerItem = [_player currentItem]; - if (playerItem.status == AVPlayerItemStatusReadyToPlay) - { - return [playerItem seekableTimeRanges].firstObject.CMTimeRangeValue; - } - - return (kCMTimeRangeZero); -} - --(void)addPlayerTimeObserver -{ - const Float64 progressUpdateIntervalMS = _progressUpdateInterval / 1000; - // @see endScrubbing in AVPlayerDemoPlaybackViewController.m - // of https://developer.apple.com/library/ios/samplecode/AVPlayerDemo/Introduction/Intro.html - __weak RCTVideo *weakSelf = self; - _timeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(progressUpdateIntervalMS, NSEC_PER_SEC) - queue:NULL - usingBlock:^(CMTime time) { [weakSelf sendProgressUpdate]; } - ]; -} - -/* Cancels the previously registered time observer. */ --(void)removePlayerTimeObserver -{ - if (_timeObserver) - { - [_player removeTimeObserver:_timeObserver]; - _timeObserver = nil; - } -} - -#pragma mark - Progress - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [self removePlayerLayer]; - [self removePlayerItemObservers]; - [_player removeObserver:self forKeyPath:playbackRate context:nil]; - [_player removeObserver:self forKeyPath:externalPlaybackActive context: nil]; -} - -#pragma mark - App lifecycle handlers - -- (void)applicationWillResignActive:(NSNotification *)notification -{ - if (_playInBackground || _playWhenInactive || _paused) return; - - [_player pause]; - [_player setRate:0.0]; -} - -- (void)applicationDidEnterBackground:(NSNotification *)notification -{ - if (_playInBackground) { - // Needed to play sound in background. See https://developer.apple.com/library/ios/qa/qa1668/_index.html - [_playerLayer setPlayer:nil]; - [_playerViewController setPlayer:nil]; - } -} - -- (void)applicationWillEnterForeground:(NSNotification *)notification -{ - [self applyModifiers]; - if (_playInBackground) { - [_playerLayer setPlayer:_player]; - [_playerViewController setPlayer:_player]; - } -} - -#pragma mark - Audio events - -- (void)audioRouteChanged:(NSNotification *)notification -{ - NSNumber *reason = [[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey]; - NSNumber *previousRoute = [[notification userInfo] objectForKey:AVAudioSessionRouteChangePreviousRouteKey]; - if (reason.unsignedIntValue == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) { - self.onVideoAudioBecomingNoisy(@{@"target": self.reactTag}); - } -} - -#pragma mark - Progress - -- (void)sendProgressUpdate -{ - AVPlayerItem *video = [_player currentItem]; - if (video == nil || video.status != AVPlayerItemStatusReadyToPlay) { - return; - } - - CMTime playerDuration = [self playerItemDuration]; - if (CMTIME_IS_INVALID(playerDuration)) { - return; - } - - CMTime currentTime = _player.currentTime; - NSDate *currentPlaybackTime = _player.currentItem.currentDate; - const Float64 duration = CMTimeGetSeconds(playerDuration); - const Float64 currentTimeSecs = CMTimeGetSeconds(currentTime); - - [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTVideo_progress" object:nil userInfo:@{@"progress": [NSNumber numberWithDouble: currentTimeSecs / duration]}]; - - if( currentTimeSecs >= 0 && self.onVideoProgress) { - self.onVideoProgress(@{ - @"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(currentTime)], - @"playableDuration": [self calculatePlayableDuration], - @"atValue": [NSNumber numberWithLongLong:currentTime.value], - @"atTimescale": [NSNumber numberWithInt:currentTime.timescale], - @"currentPlaybackTime": [NSNumber numberWithLongLong:[@(floor([currentPlaybackTime timeIntervalSince1970] * 1000)) longLongValue]], - @"target": self.reactTag, - @"seekableDuration": [self calculateSeekableDuration], - }); - } -} - -/*! - * Calculates and returns the playable duration of the current player item using its loaded time ranges. - * - * \returns The playable duration of the current player item in seconds. - */ -- (NSNumber *)calculatePlayableDuration -{ - AVPlayerItem *video = _player.currentItem; - if (video.status == AVPlayerItemStatusReadyToPlay) { - __block CMTimeRange effectiveTimeRange; - [video.loadedTimeRanges enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - CMTimeRange timeRange = [obj CMTimeRangeValue]; - if (CMTimeRangeContainsTime(timeRange, video.currentTime)) { - effectiveTimeRange = timeRange; - *stop = YES; - } - }]; - Float64 playableDuration = CMTimeGetSeconds(CMTimeRangeGetEnd(effectiveTimeRange)); - if (playableDuration > 0) { - return [NSNumber numberWithFloat:playableDuration]; - } - } - return [NSNumber numberWithInteger:0]; -} - -- (NSNumber *)calculateSeekableDuration -{ - CMTimeRange timeRange = [self playerItemSeekableTimeRange]; - if (CMTIME_IS_NUMERIC(timeRange.duration)) - { - return [NSNumber numberWithFloat:CMTimeGetSeconds(timeRange.duration)]; - } - return [NSNumber numberWithInteger:0]; -} - -- (void)addPlayerItemObservers -{ - [_playerItem addObserver:self forKeyPath:statusKeyPath options:0 context:nil]; - [_playerItem addObserver:self forKeyPath:playbackBufferEmptyKeyPath options:0 context:nil]; - [_playerItem addObserver:self forKeyPath:playbackLikelyToKeepUpKeyPath options:0 context:nil]; - [_playerItem addObserver:self forKeyPath:timedMetadata options:NSKeyValueObservingOptionNew context:nil]; - _playerItemObserversSet = YES; -} - -/* Fixes https://github.com/brentvatne/react-native-video/issues/43 - * Crashes caused when trying to remove the observer when there is no - * observer set */ -- (void)removePlayerItemObservers -{ - if (_playerItemObserversSet) { - [_playerItem removeObserver:self forKeyPath:statusKeyPath]; - [_playerItem removeObserver:self forKeyPath:playbackBufferEmptyKeyPath]; - [_playerItem removeObserver:self forKeyPath:playbackLikelyToKeepUpKeyPath]; - [_playerItem removeObserver:self forKeyPath:timedMetadata]; - _playerItemObserversSet = NO; - } -} - -#pragma mark - Player and source - -- (void)setSrc:(NSDictionary *)source -{ - _source = source; - [self removePlayerLayer]; - [self removePlayerTimeObserver]; - [self removePlayerItemObservers]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) 0), dispatch_get_main_queue(), ^{ - - // perform on next run loop, otherwise other passed react-props may not be set - [self playerItemForSource:self->_source withCallback:^(AVPlayerItem * playerItem) { - self->_playerItem = playerItem; - _playerItem = playerItem; - [self setPreferredForwardBufferDuration:_preferredForwardBufferDuration]; - [self addPlayerItemObservers]; - [self setFilter:self->_filterName]; - [self setMaxBitRate:self->_maxBitRate]; - - [_player pause]; - - if (_playbackRateObserverRegistered) { - [_player removeObserver:self forKeyPath:playbackRate context:nil]; - _playbackRateObserverRegistered = NO; - } - if (self->_isExternalPlaybackActiveObserverRegistered) { - [self->_player removeObserver:self forKeyPath:externalPlaybackActive context:nil]; - self->_isExternalPlaybackActiveObserverRegistered = NO; - } - - self->_player = [AVPlayer playerWithPlayerItem:self->_playerItem]; - self->_player.actionAtItemEnd = AVPlayerActionAtItemEndNone; - - [self->_player addObserver:self forKeyPath:playbackRate options:0 context:nil]; - self->_playbackRateObserverRegistered = YES; - - [self->_player addObserver:self forKeyPath:externalPlaybackActive options:0 context:nil]; - self->_isExternalPlaybackActiveObserverRegistered = YES; - - [self addPlayerTimeObserver]; - if (@available(iOS 10.0, *)) { - [self setAutomaticallyWaitsToMinimizeStalling:_automaticallyWaitsToMinimizeStalling]; - } - - //Perform on next run loop, otherwise onVideoLoadStart is nil - if (self.onVideoLoadStart) { - id uri = [self->_source objectForKey:@"uri"]; - id type = [self->_source objectForKey:@"type"]; - self.onVideoLoadStart(@{@"src": @{ - @"uri": uri ? uri : [NSNull null], - @"type": type ? type : [NSNull null], - @"isNetwork": [NSNumber numberWithBool:(bool)[self->_source objectForKey:@"isNetwork"]]}, - @"drm": self->_drm ? self->_drm : [NSNull null], - @"target": self.reactTag - }); - } - }]; - }); - _videoLoadStarted = YES; -} - -- (void)setDrm:(NSDictionary *)drm { - _drm = drm; -} - -- (NSURL*) urlFilePath:(NSString*) filepath { - if ([filepath containsString:@"file://"]) { - return [NSURL URLWithString:filepath]; - } - - // if no file found, check if the file exists in the Document directory - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString* relativeFilePath = [filepath lastPathComponent]; - // the file may be multiple levels below the documents directory - NSArray* fileComponents = [filepath componentsSeparatedByString:@"Documents/"]; - if (fileComponents.count > 1) { - relativeFilePath = [fileComponents objectAtIndex:1]; - } - - NSString *path = [paths.firstObject stringByAppendingPathComponent:relativeFilePath]; - if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { - return [NSURL fileURLWithPath:path]; - } - return nil; -} - -- (void)playerItemPrepareText:(AVAsset *)asset assetOptions:(NSDictionary * __nullable)assetOptions withCallback:(void(^)(AVPlayerItem *))handler -{ - if (!_textTracks || _textTracks.count==0) { - handler([AVPlayerItem playerItemWithAsset:asset]); - return; - } - - // AVPlayer can't airplay AVMutableCompositions - _allowsExternalPlayback = NO; - - // sideload text tracks - AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init]; - - AVAssetTrack *videoAsset = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject; - AVMutableCompositionTrack *videoCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; - [videoCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) - ofTrack:videoAsset - atTime:kCMTimeZero - error:nil]; - - AVAssetTrack *audioAsset = [asset tracksWithMediaType:AVMediaTypeAudio].firstObject; - AVMutableCompositionTrack *audioCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; - [audioCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) - ofTrack:audioAsset - atTime:kCMTimeZero - error:nil]; - - NSMutableArray* validTextTracks = [NSMutableArray array]; - for (int i = 0; i < _textTracks.count; ++i) { - AVURLAsset *textURLAsset; - NSString *textUri = [_textTracks objectAtIndex:i][@"uri"]; - if ([[textUri lowercaseString] hasPrefix:@"http"]) { - textURLAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:textUri] options:assetOptions]; - } else { - textURLAsset = [AVURLAsset URLAssetWithURL:[self urlFilePath:textUri] options:nil]; - } - AVAssetTrack *textTrackAsset = [textURLAsset tracksWithMediaType:AVMediaTypeText].firstObject; - if (!textTrackAsset) continue; // fix when there's no textTrackAsset - [validTextTracks addObject:[_textTracks objectAtIndex:i]]; - AVMutableCompositionTrack *textCompTrack = [mixComposition - addMutableTrackWithMediaType:AVMediaTypeText - preferredTrackID:kCMPersistentTrackID_Invalid]; - [textCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) - ofTrack:textTrackAsset - atTime:kCMTimeZero - error:nil]; - } - if (validTextTracks.count != _textTracks.count) { - [self setTextTracks:validTextTracks]; - } - - handler([AVPlayerItem playerItemWithAsset:mixComposition]); -} - -- (void)playerItemForSource:(NSDictionary *)source withCallback:(void(^)(AVPlayerItem *))handler -{ - bool isNetwork = [RCTConvert BOOL:[source objectForKey:@"isNetwork"]]; - bool isAsset = [RCTConvert BOOL:[source objectForKey:@"isAsset"]]; - bool shouldCache = [RCTConvert BOOL:[source objectForKey:@"shouldCache"]]; - NSString *uri = [source objectForKey:@"uri"]; - NSString *type = [source objectForKey:@"type"]; - AVURLAsset *asset; - if (!uri || [uri isEqualToString:@""]) { - DebugLog(@"Could not find video URL in source '%@'", source); - return; - } - - NSURL *url = isNetwork || isAsset - ? [NSURL URLWithString:uri] - : [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]]; - NSMutableDictionary *assetOptions = [[NSMutableDictionary alloc] init]; - - if (isNetwork) { - NSDictionary *headers = [source objectForKey:@"requestHeaders"]; - if ([headers count] > 0) { - [assetOptions setObject:headers forKey:@"AVURLAssetHTTPHeaderFieldsKey"]; - } - NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; - [assetOptions setObject:cookies forKey:AVURLAssetHTTPCookiesKey]; - -#if __has_include() - if (shouldCache && (!_textTracks || !_textTracks.count)) { - /* The DVURLAsset created by cache doesn't have a tracksWithMediaType property, so trying - * to bring in the text track code will crash. I suspect this is because the asset hasn't fully loaded. - * Until this is fixed, we need to bypass caching when text tracks are specified. - */ - DebugLog(@"Caching is not supported for uri '%@' because text tracks are not compatible with the cache. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md", uri); - [self playerItemForSourceUsingCache:uri assetOptions:assetOptions withCallback:handler]; - return; - } -#endif - - asset = [AVURLAsset URLAssetWithURL:url options:assetOptions]; - } else if (isAsset) { - asset = [AVURLAsset URLAssetWithURL:url options:nil]; - } else { - asset = [AVURLAsset URLAssetWithURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]] options:nil]; - } - // Reset _loadingRequest - if (_loadingRequest != nil) { - [_loadingRequest finishLoading]; - } - _requestingCertificate = NO; - _requestingCertificateErrored = NO; - // End Reset _loadingRequest - if (self->_drm != nil) { - dispatch_queue_t queue = dispatch_queue_create("assetQueue", nil); - [asset.resourceLoader setDelegate:self queue:queue]; - } - - [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler]; -} - -#if __has_include() - -- (void)playerItemForSourceUsingCache:(NSString *)uri assetOptions:(NSDictionary *)options withCallback:(void(^)(AVPlayerItem *))handler { - NSURL *url = [NSURL URLWithString:uri]; - [_videoCache getItemForUri:uri withCallback:^(RCTVideoCacheStatus videoCacheStatus, AVAsset * _Nullable cachedAsset) { - switch (videoCacheStatus) { - case RCTVideoCacheStatusMissingFileExtension: { - DebugLog(@"Could not generate cache key for uri '%@'. It is currently not supported to cache urls that do not include a file extension. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md", uri); - AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:options]; - [self playerItemPrepareText:asset assetOptions:options withCallback:handler]; - return; - } - case RCTVideoCacheStatusUnsupportedFileExtension: { - DebugLog(@"Could not generate cache key for uri '%@'. The file extension of that uri is currently not supported. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md", uri); - AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:options]; - [self playerItemPrepareText:asset assetOptions:options withCallback:handler]; - return; - } - default: - if (cachedAsset) { - DebugLog(@"Playing back uri '%@' from cache", uri); - // See note in playerItemForSource about not being able to support text tracks & caching - handler([AVPlayerItem playerItemWithAsset:cachedAsset]); - return; - } - } - - DVURLAsset *asset = [[DVURLAsset alloc] initWithURL:url options:options networkTimeout:10000]; - asset.loaderDelegate = self; - - /* More granular code to have control over the DVURLAsset - DVAssetLoaderDelegate *resourceLoaderDelegate = [[DVAssetLoaderDelegate alloc] initWithURL:url]; - resourceLoaderDelegate.delegate = self; - NSURLComponents *components = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO]; - components.scheme = [DVAssetLoaderDelegate scheme]; - AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[components URL] options:options]; - [asset.resourceLoader setDelegate:resourceLoaderDelegate queue:dispatch_get_main_queue()]; - */ - - handler([AVPlayerItem playerItemWithAsset:asset]); - }]; -} - -#pragma mark - DVAssetLoaderDelegate - -- (void)dvAssetLoaderDelegate:(DVAssetLoaderDelegate *)loaderDelegate - didLoadData:(NSData *)data - forURL:(NSURL *)url { - [_videoCache storeItem:data forUri:[url absoluteString] withCallback:^(BOOL success) { - DebugLog(@"Cache data stored successfully 🎉"); - }]; -} - -#endif - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ - - if([keyPath isEqualToString:readyForDisplayKeyPath] && [change objectForKey:NSKeyValueChangeNewKey] && self.onReadyForDisplay) { - self.onReadyForDisplay(@{@"target": self.reactTag}); - return; - } - if (object == _playerItem) { - // When timeMetadata is read the event onTimedMetadata is triggered - if ([keyPath isEqualToString:timedMetadata]) { - NSArray *items = [change objectForKey:@"new"]; - if (items && ![items isEqual:[NSNull null]] && items.count > 0) { - NSMutableArray *array = [NSMutableArray new]; - for (AVMetadataItem *item in items) { - NSString *value = (NSString *)item.value; - NSString *identifier = item.identifier; - - if (![value isEqual: [NSNull null]]) { - NSDictionary *dictionary = [[NSDictionary alloc] initWithObjects:@[value, identifier] forKeys:@[@"value", @"identifier"]]; - - [array addObject:dictionary]; - } - } - - self.onTimedMetadata(@{ - @"target": self.reactTag, - @"metadata": array - }); - } - } - - if ([keyPath isEqualToString:statusKeyPath]) { - // Handle player item status change. - if (_playerItem.status == AVPlayerItemStatusReadyToPlay) { - float duration = CMTimeGetSeconds(_playerItem.asset.duration); - - if (isnan(duration)) { - duration = 0.0; - } - - NSObject *width = @"undefined"; - NSObject *height = @"undefined"; - NSString *orientation = @"undefined"; - - if ([_playerItem.asset tracksWithMediaType:AVMediaTypeVideo].count > 0) { - AVAssetTrack *videoTrack = [[_playerItem.asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; - width = [NSNumber numberWithFloat:videoTrack.naturalSize.width]; - height = [NSNumber numberWithFloat:videoTrack.naturalSize.height]; - CGAffineTransform preferredTransform = [videoTrack preferredTransform]; - - if ((videoTrack.naturalSize.width == preferredTransform.tx - && videoTrack.naturalSize.height == preferredTransform.ty) - || (preferredTransform.tx == 0 && preferredTransform.ty == 0)) - { - orientation = @"landscape"; - } else { - orientation = @"portrait"; - } - } else if (_playerItem.presentationSize.height) { - width = [NSNumber numberWithFloat:_playerItem.presentationSize.width]; - height = [NSNumber numberWithFloat:_playerItem.presentationSize.height]; - orientation = _playerItem.presentationSize.width > _playerItem.presentationSize.height ? @"landscape" : @"portrait"; - } - - if (_pendingSeek) { - [self setCurrentTime:_pendingSeekTime]; - _pendingSeek = false; - } - - if (self.onVideoLoad && _videoLoadStarted) { - self.onVideoLoad(@{@"duration": [NSNumber numberWithFloat:duration], - @"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(_playerItem.currentTime)], - @"canPlayReverse": [NSNumber numberWithBool:_playerItem.canPlayReverse], - @"canPlayFastForward": [NSNumber numberWithBool:_playerItem.canPlayFastForward], - @"canPlaySlowForward": [NSNumber numberWithBool:_playerItem.canPlaySlowForward], - @"canPlaySlowReverse": [NSNumber numberWithBool:_playerItem.canPlaySlowReverse], - @"canStepBackward": [NSNumber numberWithBool:_playerItem.canStepBackward], - @"canStepForward": [NSNumber numberWithBool:_playerItem.canStepForward], - @"naturalSize": @{ - @"width": width, - @"height": height, - @"orientation": orientation - }, - @"audioTracks": [self getAudioTrackInfo], - @"textTracks": [self getTextTrackInfo], - @"target": self.reactTag}); - } - _videoLoadStarted = NO; - - [self attachListeners]; - [self applyModifiers]; - } else if (_playerItem.status == AVPlayerItemStatusFailed && self.onVideoError) { - self.onVideoError(@{@"error": @{@"code": [NSNumber numberWithInteger: _playerItem.error.code], - @"localizedDescription": [_playerItem.error localizedDescription] == nil ? @"" : [_playerItem.error localizedDescription], - @"localizedFailureReason": [_playerItem.error localizedFailureReason] == nil ? @"" : [_playerItem.error localizedFailureReason], - @"localizedRecoverySuggestion": [_playerItem.error localizedRecoverySuggestion] == nil ? @"" : [_playerItem.error localizedRecoverySuggestion], - @"domain": _playerItem != nil && _playerItem.error != nil ? _playerItem.error.domain : @"RTCVideo"}, - @"target": self.reactTag}); - } - } else if ([keyPath isEqualToString:playbackBufferEmptyKeyPath]) { - _playerBufferEmpty = YES; - self.onVideoBuffer(@{@"isBuffering": @(YES), @"target": self.reactTag}); - } else if ([keyPath isEqualToString:playbackLikelyToKeepUpKeyPath]) { - // Continue playing (or not if paused) after being paused due to hitting an unbuffered zone. - if ((!(_controls || _fullscreenPlayerPresented) || _playerBufferEmpty) && _playerItem.playbackLikelyToKeepUp) { - [self setPaused:_paused]; - } - _playerBufferEmpty = NO; - self.onVideoBuffer(@{@"isBuffering": @(NO), @"target": self.reactTag}); - } - } else if (object == _player) { - if([keyPath isEqualToString:playbackRate]) { - if(self.onPlaybackRateChange) { - self.onPlaybackRateChange(@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], - @"target": self.reactTag}); - } - if(_playbackStalled && _player.rate > 0) { - if(self.onPlaybackResume) { - self.onPlaybackResume(@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], - @"target": self.reactTag}); - } - _playbackStalled = NO; - } - } - else if([keyPath isEqualToString:externalPlaybackActive]) { - if(self.onVideoExternalPlaybackChange) { - self.onVideoExternalPlaybackChange(@{@"isExternalPlaybackActive": [NSNumber numberWithBool:_player.isExternalPlaybackActive], - @"target": self.reactTag}); - } - } - } else if (object == _playerViewController.contentOverlayView) { - // when controls==true, this is a hack to reset the rootview when rotation happens in fullscreen - if ([keyPath isEqualToString:@"frame"]) { - - CGRect oldRect = [change[NSKeyValueChangeOldKey] CGRectValue]; - CGRect newRect = [change[NSKeyValueChangeNewKey] CGRectValue]; - - if (!CGRectEqualToRect(oldRect, newRect)) { - if (CGRectEqualToRect(newRect, [UIScreen mainScreen].bounds)) { - NSLog(@"in fullscreen"); - - [self.reactViewController.view setFrame:[UIScreen mainScreen].bounds]; - [self.reactViewController.view setNeedsLayout]; - } else NSLog(@"not fullscreen"); - } - - return; - } - } -} - -- (void)attachListeners -{ - // listen for end of file - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVPlayerItemDidPlayToEndTimeNotification - object:[_player currentItem]]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(playerItemDidReachEnd:) - name:AVPlayerItemDidPlayToEndTimeNotification - object:[_player currentItem]]; - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVPlayerItemPlaybackStalledNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(playbackStalled:) - name:AVPlayerItemPlaybackStalledNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVPlayerItemNewAccessLogEntryNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleAVPlayerAccess:) - name:AVPlayerItemNewAccessLogEntryNotification - object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self - name: AVPlayerItemFailedToPlayToEndTimeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(didFailToFinishPlaying:) - name: AVPlayerItemFailedToPlayToEndTimeNotification - object:nil]; - -} - -- (void)handleAVPlayerAccess:(NSNotification *)notification { - AVPlayerItemAccessLog *accessLog = [((AVPlayerItem *)notification.object) accessLog]; - AVPlayerItemAccessLogEvent *lastEvent = accessLog.events.lastObject; - - /* TODO: get this working - if (self.onBandwidthUpdate) { - self.onBandwidthUpdate(@{@"bitrate": [NSNumber numberWithFloat:lastEvent.observedBitrate]}); - } - */ -} - -- (void)didFailToFinishPlaying:(NSNotification *)notification { - NSError *error = notification.userInfo[AVPlayerItemFailedToPlayToEndTimeErrorKey]; - self.onVideoError(@{@"error": @{@"code": [NSNumber numberWithInteger: error.code], - @"localizedDescription": [error localizedDescription] == nil ? @"" : [error localizedDescription], - @"localizedFailureReason": [error localizedFailureReason] == nil ? @"" : [error localizedFailureReason], - @"localizedRecoverySuggestion": [error localizedRecoverySuggestion] == nil ? @"" : [error localizedRecoverySuggestion], - @"domain": error.domain}, - @"target": self.reactTag}); -} - -- (void)playbackStalled:(NSNotification *)notification -{ - if(self.onPlaybackStalled) { - self.onPlaybackStalled(@{@"target": self.reactTag}); - } - _playbackStalled = YES; -} - -- (void)playerItemDidReachEnd:(NSNotification *)notification -{ - if(self.onVideoEnd) { - self.onVideoEnd(@{@"target": self.reactTag}); - } - - if (_repeat) { - AVPlayerItem *item = [notification object]; - [item seekToTime:kCMTimeZero]; - [self applyModifiers]; - } else { - [self removePlayerTimeObserver]; - } -} - -#pragma mark - Prop setters - -- (void)setResizeMode:(NSString*)mode -{ - if( _controls ) - { - _playerViewController.videoGravity = mode; - } - else - { - _playerLayer.videoGravity = mode; - } - _resizeMode = mode; -} - -- (void)setPlayInBackground:(BOOL)playInBackground -{ - _playInBackground = playInBackground; -} - -- (void)setPreventsDisplaySleepDuringVideoPlayback:(BOOL)preventsDisplaySleepDuringVideoPlayback -{ - _preventsDisplaySleepDuringVideoPlayback = preventsDisplaySleepDuringVideoPlayback; - [self applyModifiers]; -} - -- (void)setAllowsExternalPlayback:(BOOL)allowsExternalPlayback -{ - _allowsExternalPlayback = allowsExternalPlayback; - _player.allowsExternalPlayback = _allowsExternalPlayback; -} - -- (void)setPlayWhenInactive:(BOOL)playWhenInactive -{ - _playWhenInactive = playWhenInactive; -} - -- (void)setPictureInPicture:(BOOL)pictureInPicture -{ - #if TARGET_OS_IOS - if (_pictureInPicture == pictureInPicture) { - return; - } - - _pictureInPicture = pictureInPicture; - if (_pipController && _pictureInPicture && ![_pipController isPictureInPictureActive]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [_pipController startPictureInPicture]; - }); - } else if (_pipController && !_pictureInPicture && [_pipController isPictureInPictureActive]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [_pipController stopPictureInPicture]; - }); - } - #endif -} - -#if TARGET_OS_IOS -- (void)setRestoreUserInterfaceForPIPStopCompletionHandler:(BOOL)restore -{ - if (_restoreUserInterfaceForPIPStopCompletionHandler != NULL) { - _restoreUserInterfaceForPIPStopCompletionHandler(restore); - _restoreUserInterfaceForPIPStopCompletionHandler = NULL; - } -} - -- (void)setupPipController { - if (!_pipController && _playerLayer && [AVPictureInPictureController isPictureInPictureSupported]) { - // Create new controller passing reference to the AVPlayerLayer - _pipController = [[AVPictureInPictureController alloc] initWithPlayerLayer:_playerLayer]; - _pipController.delegate = self; - } -} -#endif - -- (void)setIgnoreSilentSwitch:(NSString *)ignoreSilentSwitch -{ - _ignoreSilentSwitch = ignoreSilentSwitch; - [self applyModifiers]; -} - -- (void)setMixWithOthers:(NSString *)mixWithOthers -{ - _mixWithOthers = mixWithOthers; - [self applyModifiers]; -} - -- (void)setPaused:(BOOL)paused -{ - if (paused) { - [_player pause]; - [_player setRate:0.0]; - } else { - AVAudioSession *session = [AVAudioSession sharedInstance]; - AVAudioSessionCategory category = nil; - AVAudioSessionCategoryOptions options = nil; - - if([_ignoreSilentSwitch isEqualToString:@"ignore"]) { - category = AVAudioSessionCategoryPlayback; - } else if([_ignoreSilentSwitch isEqualToString:@"obey"]) { - category = AVAudioSessionCategoryAmbient; - } - - if([_mixWithOthers isEqualToString:@"mix"]) { - options = AVAudioSessionCategoryOptionMixWithOthers; - } else if([_mixWithOthers isEqualToString:@"duck"]) { - options = AVAudioSessionCategoryOptionDuckOthers; - } - - if (category != nil && options != nil) { - [session setCategory:category withOptions:options error:nil]; - } else if (category != nil && options == nil) { - [session setCategory:category error:nil]; - } else if (category == nil && options != nil) { - [session setCategory:session.category withOptions:options error:nil]; - } - - if (@available(iOS 10.0, *) && !_automaticallyWaitsToMinimizeStalling) { - [_player playImmediatelyAtRate:_rate]; - } else { - [_player play]; - [_player setRate:_rate]; - } - [_player setRate:_rate]; - } - - _paused = paused; -} - -- (float)getCurrentTime -{ - return _playerItem != NULL ? CMTimeGetSeconds(_playerItem.currentTime) : 0; -} - -- (void)setCurrentTime:(float)currentTime -{ - NSDictionary *info = @{ - @"time": [NSNumber numberWithFloat:currentTime], - @"tolerance": [NSNumber numberWithInt:100] - }; - [self setSeek:info]; -} - -- (void)setSeek:(NSDictionary *)info -{ - NSNumber *seekTime = info[@"time"]; - NSNumber *seekTolerance = info[@"tolerance"]; - - int timeScale = 1000; - - AVPlayerItem *item = _player.currentItem; - if (item && item.status == AVPlayerItemStatusReadyToPlay) { - // TODO check loadedTimeRanges - - CMTime cmSeekTime = CMTimeMakeWithSeconds([seekTime floatValue], timeScale); - CMTime current = item.currentTime; - // TODO figure out a good tolerance level - CMTime tolerance = CMTimeMake([seekTolerance floatValue], timeScale); - BOOL wasPaused = _paused; - - if (CMTimeCompare(current, cmSeekTime) != 0) { - if (!wasPaused) [_player pause]; - [_player seekToTime:cmSeekTime toleranceBefore:tolerance toleranceAfter:tolerance completionHandler:^(BOOL finished) { - if (!_timeObserver) { - [self addPlayerTimeObserver]; - } - if (!wasPaused) { - [self setPaused:false]; - } - if(self.onVideoSeek) { - self.onVideoSeek(@{@"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(item.currentTime)], - @"seekTime": seekTime, - @"target": self.reactTag}); - } - }]; - - _pendingSeek = false; - } - - } else { - _pendingSeek = true; - _pendingSeekTime = [seekTime floatValue]; - } -} - -- (void)setRate:(float)rate -{ - _rate = rate; - [self applyModifiers]; -} - -- (void)setMuted:(BOOL)muted -{ - _muted = muted; - [self applyModifiers]; -} - -- (void)setVolume:(float)volume -{ - _volume = volume; - [self applyModifiers]; -} - -- (void)setMaxBitRate:(float) maxBitRate { - _maxBitRate = maxBitRate; - _playerItem.preferredPeakBitRate = maxBitRate; -} - -- (void)setPreferredForwardBufferDuration:(float) preferredForwardBufferDuration -{ - _preferredForwardBufferDuration = preferredForwardBufferDuration; - _playerItem.preferredForwardBufferDuration = preferredForwardBufferDuration; -} - -- (void)setAutomaticallyWaitsToMinimizeStalling:(BOOL)waits -{ - _automaticallyWaitsToMinimizeStalling = waits; - _player.automaticallyWaitsToMinimizeStalling = waits; -} - - -- (void)applyModifiers -{ - if (_muted) { - if (!_controls) { - [_player setVolume:0]; - } - [_player setMuted:YES]; - } else { - [_player setVolume:_volume]; - [_player setMuted:NO]; - } - - if (@available(iOS 12.0, *)) { - self->_player.preventsDisplaySleepDuringVideoPlayback = _preventsDisplaySleepDuringVideoPlayback; - } else { - // Fallback on earlier versions - } - - [self setMaxBitRate:_maxBitRate]; - [self setSelectedAudioTrack:_selectedAudioTrack]; - [self setSelectedTextTrack:_selectedTextTrack]; - [self setResizeMode:_resizeMode]; - [self setRepeat:_repeat]; - [self setPaused:_paused]; - [self setControls:_controls]; - [self setAllowsExternalPlayback:_allowsExternalPlayback]; -} - -- (void)setRepeat:(BOOL)repeat { - _repeat = repeat; -} - -- (void)setMediaSelectionTrackForCharacteristic:(AVMediaCharacteristic)characteristic - withCriteria:(NSDictionary *)criteria -{ - NSString *type = criteria[@"type"]; - AVMediaSelectionGroup *group = [_player.currentItem.asset - mediaSelectionGroupForMediaCharacteristic:characteristic]; - AVMediaSelectionOption *mediaOption; - - if ([type isEqualToString:@"disabled"]) { - // Do nothing. We want to ensure option is nil - } else if ([type isEqualToString:@"language"] || [type isEqualToString:@"title"]) { - NSString *value = criteria[@"value"]; - for (int i = 0; i < group.options.count; ++i) { - AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i]; - NSString *optionValue; - if ([type isEqualToString:@"language"]) { - optionValue = [currentOption extendedLanguageTag]; - } else { - optionValue = [[[currentOption commonMetadata] - valueForKey:@"value"] - objectAtIndex:0]; - } - if ([value isEqualToString:optionValue]) { - mediaOption = currentOption; - break; - } - } - //} else if ([type isEqualToString:@"default"]) { - // option = group.defaultOption; */ - } else if ([type isEqualToString:@"index"]) { - if ([criteria[@"value"] isKindOfClass:[NSNumber class]]) { - int index = [criteria[@"value"] intValue]; - if (group.options.count > index) { - mediaOption = [group.options objectAtIndex:index]; - } - } - } else { // default. invalid type or "system" - [_player.currentItem selectMediaOptionAutomaticallyInMediaSelectionGroup:group]; - return; - } - - // If a match isn't found, option will be nil and text tracks will be disabled - [_player.currentItem selectMediaOption:mediaOption inMediaSelectionGroup:group]; -} - -- (void)setSelectedAudioTrack:(NSDictionary *)selectedAudioTrack { - _selectedAudioTrack = selectedAudioTrack; - [self setMediaSelectionTrackForCharacteristic:AVMediaCharacteristicAudible - withCriteria:_selectedAudioTrack]; -} - -- (void)setSelectedTextTrack:(NSDictionary *)selectedTextTrack { - _selectedTextTrack = selectedTextTrack; - if (_textTracks) { // sideloaded text tracks - [self setSideloadedText]; - } else { // text tracks included in the HLS playlist - [self setMediaSelectionTrackForCharacteristic:AVMediaCharacteristicLegible - withCriteria:_selectedTextTrack]; - } -} - -- (void) setSideloadedText { - NSString *type = _selectedTextTrack[@"type"]; - NSArray *textTracks = [self getTextTrackInfo]; - - // The first few tracks will be audio & video track - int firstTextIndex = 0; - for (firstTextIndex = 0; firstTextIndex < _player.currentItem.tracks.count; ++firstTextIndex) { - if ([_player.currentItem.tracks[firstTextIndex].assetTrack hasMediaCharacteristic:AVMediaCharacteristicLegible]) { - break; - } - } - - int selectedTrackIndex = RCTVideoUnset; - - if ([type isEqualToString:@"disabled"]) { - // Do nothing. We want to ensure option is nil - } else if ([type isEqualToString:@"language"]) { - NSString *selectedValue = _selectedTextTrack[@"value"]; - for (int i = 0; i < textTracks.count; ++i) { - NSDictionary *currentTextTrack = [textTracks objectAtIndex:i]; - if ([selectedValue isEqualToString:currentTextTrack[@"language"]]) { - selectedTrackIndex = i; - break; - } - } - } else if ([type isEqualToString:@"title"]) { - NSString *selectedValue = _selectedTextTrack[@"value"]; - for (int i = 0; i < textTracks.count; ++i) { - NSDictionary *currentTextTrack = [textTracks objectAtIndex:i]; - if ([selectedValue isEqualToString:currentTextTrack[@"title"]]) { - selectedTrackIndex = i; - break; - } - } - } else if ([type isEqualToString:@"index"]) { - if ([_selectedTextTrack[@"value"] isKindOfClass:[NSNumber class]]) { - int index = [_selectedTextTrack[@"value"] intValue]; - if (textTracks.count > index) { - selectedTrackIndex = index; - } - } - } - - // in the situation that a selected text track is not available (eg. specifies a textTrack not available) - if (![type isEqualToString:@"disabled"] && selectedTrackIndex == RCTVideoUnset) { - CFArrayRef captioningMediaCharacteristics = MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics(kMACaptionAppearanceDomainUser); - NSArray *captionSettings = (__bridge NSArray*)captioningMediaCharacteristics; - if ([captionSettings containsObject:AVMediaCharacteristicTranscribesSpokenDialogForAccessibility]) { - selectedTrackIndex = 0; // If we can't find a match, use the first available track - NSString *systemLanguage = [[NSLocale preferredLanguages] firstObject]; - for (int i = 0; i < textTracks.count; ++i) { - NSDictionary *currentTextTrack = [textTracks objectAtIndex:i]; - if ([systemLanguage isEqualToString:currentTextTrack[@"language"]]) { - selectedTrackIndex = i; - break; - } - } - } - } - - for (int i = firstTextIndex; i < _player.currentItem.tracks.count; ++i) { - BOOL isEnabled = NO; - if (selectedTrackIndex != RCTVideoUnset) { - isEnabled = i == selectedTrackIndex + firstTextIndex; - } - [_player.currentItem.tracks[i] setEnabled:isEnabled]; - } -} - --(void) setStreamingText { - NSString *type = _selectedTextTrack[@"type"]; - AVMediaSelectionGroup *group = [_player.currentItem.asset - mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible]; - AVMediaSelectionOption *mediaOption; - - if ([type isEqualToString:@"disabled"]) { - // Do nothing. We want to ensure option is nil - } else if ([type isEqualToString:@"language"] || [type isEqualToString:@"title"]) { - NSString *value = _selectedTextTrack[@"value"]; - for (int i = 0; i < group.options.count; ++i) { - AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i]; - NSString *optionValue; - if ([type isEqualToString:@"language"]) { - optionValue = [currentOption extendedLanguageTag]; - } else { - optionValue = [[[currentOption commonMetadata] - valueForKey:@"value"] - objectAtIndex:0]; - } - if ([value isEqualToString:optionValue]) { - mediaOption = currentOption; - break; - } - } - //} else if ([type isEqualToString:@"default"]) { - // option = group.defaultOption; */ - } else if ([type isEqualToString:@"index"]) { - if ([_selectedTextTrack[@"value"] isKindOfClass:[NSNumber class]]) { - int index = [_selectedTextTrack[@"value"] intValue]; - if (group.options.count > index) { - mediaOption = [group.options objectAtIndex:index]; - } - } - } else { // default. invalid type or "system" - [_player.currentItem selectMediaOptionAutomaticallyInMediaSelectionGroup:group]; - return; - } - - // If a match isn't found, option will be nil and text tracks will be disabled - [_player.currentItem selectMediaOption:mediaOption inMediaSelectionGroup:group]; -} - -- (void)setTextTracks:(NSArray*) textTracks; -{ - _textTracks = textTracks; - - // in case textTracks was set after selectedTextTrack - if (_selectedTextTrack) [self setSelectedTextTrack:_selectedTextTrack]; -} - -- (NSArray *)getAudioTrackInfo -{ - NSMutableArray *audioTracks = [[NSMutableArray alloc] init]; - AVMediaSelectionGroup *group = [_player.currentItem.asset - mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible]; - for (int i = 0; i < group.options.count; ++i) { - AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i]; - NSString *title = @""; - NSArray *values = [[currentOption commonMetadata] valueForKey:@"value"]; - if (values.count > 0) { - title = [values objectAtIndex:0]; - } - NSString *language = [currentOption extendedLanguageTag] ? [currentOption extendedLanguageTag] : @""; - NSDictionary *audioTrack = @{ - @"index": [NSNumber numberWithInt:i], - @"title": title, - @"language": language - }; - [audioTracks addObject:audioTrack]; - } - return audioTracks; -} - -- (NSArray *)getTextTrackInfo -{ - // if sideloaded, textTracks will already be set - if (_textTracks) return _textTracks; - - // if streaming video, we extract the text tracks - NSMutableArray *textTracks = [[NSMutableArray alloc] init]; - AVMediaSelectionGroup *group = [_player.currentItem.asset - mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible]; - for (int i = 0; i < group.options.count; ++i) { - AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i]; - NSString *title = @""; - NSArray *values = [[currentOption commonMetadata] valueForKey:@"value"]; - if (values.count > 0) { - title = [values objectAtIndex:0]; - } - NSString *language = [currentOption extendedLanguageTag] ? [currentOption extendedLanguageTag] : @""; - NSDictionary *textTrack = @{ - @"index": [NSNumber numberWithInt:i], - @"title": title, - @"language": language - }; - [textTracks addObject:textTrack]; - } - return textTracks; -} - -- (BOOL)getFullscreen -{ - return _fullscreenPlayerPresented; -} - -- (void)setFullscreen:(BOOL) fullscreen { - if( fullscreen && !_fullscreenPlayerPresented && _player ) - { - // Ensure player view controller is not null - if( !_playerViewController ) - { - [self usePlayerViewController]; - } - // Set presentation style to fullscreen - [_playerViewController setModalPresentationStyle:UIModalPresentationFullScreen]; - - // Find the nearest view controller - UIViewController *viewController = [self firstAvailableUIViewController]; - if( !viewController ) - { - UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; - viewController = keyWindow.rootViewController; - if( viewController.childViewControllers.count > 0 ) - { - viewController = viewController.childViewControllers.lastObject; - } - } - if( viewController ) - { - _presentingViewController = viewController; - if(self.onVideoFullscreenPlayerWillPresent) { - self.onVideoFullscreenPlayerWillPresent(@{@"target": self.reactTag}); - } - [viewController presentViewController:_playerViewController animated:true completion:^{ - _playerViewController.showsPlaybackControls = YES; - _fullscreenPlayerPresented = fullscreen; - _playerViewController.autorotate = _fullscreenAutorotate; - if(self.onVideoFullscreenPlayerDidPresent) { - self.onVideoFullscreenPlayerDidPresent(@{@"target": self.reactTag}); - } - }]; - } - } - else if ( !fullscreen && _fullscreenPlayerPresented ) - { - [self videoPlayerViewControllerWillDismiss:_playerViewController]; - [_presentingViewController dismissViewControllerAnimated:true completion:^{ - [self videoPlayerViewControllerDidDismiss:_playerViewController]; - }]; - } -} - -- (void)setFullscreenAutorotate:(BOOL)autorotate { - _fullscreenAutorotate = autorotate; - if (_fullscreenPlayerPresented) { - _playerViewController.autorotate = autorotate; - } -} - -- (void)setFullscreenOrientation:(NSString *)orientation { - _fullscreenOrientation = orientation; - if (_fullscreenPlayerPresented) { - _playerViewController.preferredOrientation = orientation; - } -} - -- (void)usePlayerViewController -{ - if( _player ) - { - if (!_playerViewController) { - _playerViewController = [self createPlayerViewController:_player withPlayerItem:_playerItem]; - } - // to prevent video from being animated when resizeMode is 'cover' - // resize mode must be set before subview is added - [self setResizeMode:_resizeMode]; - - if (_controls) { - UIViewController *viewController = [self reactViewController]; - [viewController addChildViewController:_playerViewController]; - [self addSubview:_playerViewController.view]; - } - - [_playerViewController addObserver:self forKeyPath:readyForDisplayKeyPath options:NSKeyValueObservingOptionNew context:nil]; - - [_playerViewController.contentOverlayView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL]; - } -} - -- (void)usePlayerLayer -{ - if( _player ) - { - _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player]; - _playerLayer.frame = self.bounds; - _playerLayer.needsDisplayOnBoundsChange = YES; - - // to prevent video from being animated when resizeMode is 'cover' - // resize mode must be set before layer is added - [self setResizeMode:_resizeMode]; - [_playerLayer addObserver:self forKeyPath:readyForDisplayKeyPath options:NSKeyValueObservingOptionNew context:nil]; - _playerLayerObserverSet = YES; - - [self.layer addSublayer:_playerLayer]; - self.layer.needsDisplayOnBoundsChange = YES; - #if TARGET_OS_IOS - [self setupPipController]; - #endif - } -} - -- (void)setControls:(BOOL)controls -{ - if( _controls != controls || (!_playerLayer && !_playerViewController) ) - { - _controls = controls; - if( _controls ) - { - [self removePlayerLayer]; - [self usePlayerViewController]; - } - else - { - [_playerViewController.view removeFromSuperview]; - _playerViewController = nil; - [self usePlayerLayer]; - } - } -} - -- (void)setProgressUpdateInterval:(float)progressUpdateInterval -{ - _progressUpdateInterval = progressUpdateInterval; - - if (_timeObserver) { - [self removePlayerTimeObserver]; - [self addPlayerTimeObserver]; - } -} - -- (void)removePlayerLayer -{ - if (_loadingRequest != nil) { - [_loadingRequest finishLoading]; - } - _requestingCertificate = NO; - _requestingCertificateErrored = NO; - [_playerLayer removeFromSuperlayer]; - if (_playerLayerObserverSet) { - [_playerLayer removeObserver:self forKeyPath:readyForDisplayKeyPath]; - _playerLayerObserverSet = NO; - } - _playerLayer = nil; -} - -#pragma mark - RCTVideoPlayerViewControllerDelegate - -- (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController -{ - if (_playerViewController == playerViewController && _fullscreenPlayerPresented && self.onVideoFullscreenPlayerWillDismiss) - { - @try{ - [_playerViewController.contentOverlayView removeObserver:self forKeyPath:@"frame"]; - [_playerViewController removeObserver:self forKeyPath:readyForDisplayKeyPath]; - }@catch(id anException){ - } - self.onVideoFullscreenPlayerWillDismiss(@{@"target": self.reactTag}); - } -} - -- (void)videoPlayerViewControllerDidDismiss:(AVPlayerViewController *)playerViewController -{ - if (_playerViewController == playerViewController && _fullscreenPlayerPresented) - { - _fullscreenPlayerPresented = false; - _presentingViewController = nil; - _playerViewController = nil; - [self applyModifiers]; - if(self.onVideoFullscreenPlayerDidDismiss) { - self.onVideoFullscreenPlayerDidDismiss(@{@"target": self.reactTag}); - } - } -} - -- (void)setFilter:(NSString *)filterName { - _filterName = filterName; - - if (!_filterEnabled) { - return; - } else if ([[_source objectForKey:@"uri"] rangeOfString:@"m3u8"].location != NSNotFound) { - return; // filters don't work for HLS... return - } else if (!_playerItem.asset) { - return; - } - - CIFilter *filter = [CIFilter filterWithName:filterName]; - _playerItem.videoComposition = [AVVideoComposition - videoCompositionWithAsset:_playerItem.asset - applyingCIFiltersWithHandler:^(AVAsynchronousCIImageFilteringRequest *_Nonnull request) { - if (filter == nil) { - [request finishWithImage:request.sourceImage context:nil]; - } else { - CIImage *image = request.sourceImage.imageByClampingToExtent; - [filter setValue:image forKey:kCIInputImageKey]; - CIImage *output = [filter.outputImage imageByCroppingToRect:request.sourceImage.extent]; - [request finishWithImage:output context:nil]; - } - }]; -} - -- (void)setFilterEnabled:(BOOL)filterEnabled { - _filterEnabled = filterEnabled; -} - -#pragma mark - React View Management - -- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex -{ - // We are early in the game and somebody wants to set a subview. - // That can only be in the context of playerViewController. - if( !_controls && !_playerLayer && !_playerViewController ) - { - [self setControls:true]; - } - - if( _controls ) - { - view.frame = self.bounds; - [_playerViewController.contentOverlayView insertSubview:view atIndex:atIndex]; - } - else - { - RCTLogError(@"video cannot have any subviews"); - } - return; -} - -- (void)removeReactSubview:(UIView *)subview -{ - if( _controls ) - { - [subview removeFromSuperview]; - } - else - { - RCTLogError(@"video cannot have any subviews"); - } - return; -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - if( _controls ) - { - _playerViewController.view.frame = self.bounds; - - // also adjust all subviews of contentOverlayView - for (UIView* subview in _playerViewController.contentOverlayView.subviews) { - subview.frame = self.bounds; - } - } - else - { - [CATransaction begin]; - [CATransaction setAnimationDuration:0]; - _playerLayer.frame = self.bounds; - [CATransaction commit]; - } -} - -#pragma mark - Lifecycle - -- (void)removeFromSuperview -{ - [_player pause]; - if (_playbackRateObserverRegistered) { - [_player removeObserver:self forKeyPath:playbackRate context:nil]; - _playbackRateObserverRegistered = NO; - } - if (_isExternalPlaybackActiveObserverRegistered) { - [_player removeObserver:self forKeyPath:externalPlaybackActive context:nil]; - _isExternalPlaybackActiveObserverRegistered = NO; - } - _player = nil; - - [self removePlayerLayer]; - - [_playerViewController.contentOverlayView removeObserver:self forKeyPath:@"frame"]; - [_playerViewController removeObserver:self forKeyPath:readyForDisplayKeyPath]; - [_playerViewController.view removeFromSuperview]; - _playerViewController.rctDelegate = nil; - _playerViewController.player = nil; - _playerViewController = nil; - - [self removePlayerTimeObserver]; - [self removePlayerItemObservers]; - - _eventDispatcher = nil; - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - [super removeFromSuperview]; -} - -#pragma mark - Export - -- (void)save:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { - - AVAsset *asset = _playerItem.asset; - - if (asset != nil) { - - AVAssetExportSession *exportSession = [AVAssetExportSession - exportSessionWithAsset:asset presetName:AVAssetExportPresetHighestQuality]; - - if (exportSession != nil) { - NSString *path = nil; - NSArray *array = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - path = [self generatePathInDirectory:[[self cacheDirectoryPath] stringByAppendingPathComponent:@"Videos"] - withExtension:@".mp4"]; - NSURL *url = [NSURL fileURLWithPath:path]; - exportSession.outputFileType = AVFileTypeMPEG4; - exportSession.outputURL = url; - exportSession.videoComposition = _playerItem.videoComposition; - exportSession.shouldOptimizeForNetworkUse = true; - [exportSession exportAsynchronouslyWithCompletionHandler:^{ - - switch ([exportSession status]) { - case AVAssetExportSessionStatusFailed: - reject(@"ERROR_COULD_NOT_EXPORT_VIDEO", @"Could not export video", exportSession.error); - break; - case AVAssetExportSessionStatusCancelled: - reject(@"ERROR_EXPORT_SESSION_CANCELLED", @"Export session was cancelled", exportSession.error); - break; - default: - resolve(@{@"uri": url.absoluteString}); - break; - } - - }]; - - } else { - - reject(@"ERROR_COULD_NOT_CREATE_EXPORT_SESSION", @"Could not create export session", nil); - - } - - } else { - - reject(@"ERROR_ASSET_NIL", @"Asset is nil", nil); - - } -} - -- (void)setLicenseResult:(NSString *)license { - NSData *respondData = [self base64DataFromBase64String:license]; - if (_loadingRequest != nil && respondData != nil) { - AVAssetResourceLoadingDataRequest *dataRequest = [_loadingRequest dataRequest]; - [dataRequest respondWithData:respondData]; - [_loadingRequest finishLoading]; - } else { - [self setLicenseResultError:@"No data from JS license response"]; - } -} - -- (BOOL)setLicenseResultError:(NSString *)error { - if (_loadingRequest != nil) { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorFromJSPart - userInfo: @{ - NSLocalizedDescriptionKey: error, - NSLocalizedFailureReasonErrorKey: error, - NSLocalizedRecoverySuggestionErrorKey: error - } - ]; - [self finishLoadingWithError:licenseError]; - } - return NO; -} - -- (BOOL)finishLoadingWithError:(NSError *)error { - if (_loadingRequest && error != nil) { - NSError *licenseError = error; - [_loadingRequest finishLoadingWithError:licenseError]; - if (self.onVideoError) { - self.onVideoError(@{@"error": @{@"code": [NSNumber numberWithInteger: error.code], - @"localizedDescription": [error localizedDescription] == nil ? @"" : [error localizedDescription], - @"localizedFailureReason": [error localizedFailureReason] == nil ? @"" : [error localizedFailureReason], - @"localizedRecoverySuggestion": [error localizedRecoverySuggestion] == nil ? @"" : [error localizedRecoverySuggestion], - @"domain": _playerItem.error == nil ? @"RCTVideo" : _playerItem.error.domain}, - @"target": self.reactTag}); - } - } - return NO; -} - -- (BOOL)ensureDirExistsWithPath:(NSString *)path { - BOOL isDir = NO; - NSError *error; - BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]; - if (!(exists && isDir)) { - [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; - if (error) { - return NO; - } - } - return YES; -} - -- (NSString *)generatePathInDirectory:(NSString *)directory withExtension:(NSString *)extension { - NSString *fileName = [[[NSUUID UUID] UUIDString] stringByAppendingString:extension]; - [self ensureDirExistsWithPath:directory]; - return [directory stringByAppendingPathComponent:fileName]; -} - -- (NSString *)cacheDirectoryPath { - NSArray *array = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - return array[0]; -} - -#pragma mark - AVAssetResourceLoaderDelegate - -- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForRenewalOfRequestedResource:(AVAssetResourceRenewalRequest *)renewalRequest { - return [self loadingRequestHandling:renewalRequest]; -} - -- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest { - return [self loadingRequestHandling:loadingRequest]; -} - -- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader -didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest { - NSLog(@"didCancelLoadingRequest"); -} - -- (BOOL)loadingRequestHandling:(AVAssetResourceLoadingRequest *)loadingRequest { - if (self->_requestingCertificate) { - return YES; - } else if (self->_requestingCertificateErrored) { - return NO; - } - _loadingRequest = loadingRequest; - NSURL *url = loadingRequest.request.URL; - if (self->_drm != nil) { - NSString *contentId; - NSString *contentIdOverride = (NSString *)[self->_drm objectForKey:@"contentId"]; - if (contentIdOverride != nil) { - contentId = contentIdOverride; - } else if (self.onGetLicense) { - contentId = url.host; - } else { - contentId = [url.absoluteString stringByReplacingOccurrencesOfString:@"skd://" withString:@""]; - } - NSString *drmType = (NSString *)[self->_drm objectForKey:@"type"]; - if ([drmType isEqualToString:@"fairplay"]) { - NSString *certificateStringUrl = (NSString *)[self->_drm objectForKey:@"certificateUrl"]; - if (certificateStringUrl != nil) { - NSURL *certificateURL = [NSURL URLWithString:[certificateStringUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSData *certificateData = [NSData dataWithContentsOfURL:certificateURL]; - if ([self->_drm objectForKey:@"base64Certificate"]) { - certificateData = [[NSData alloc] initWithBase64EncodedData:certificateData options:NSDataBase64DecodingIgnoreUnknownCharacters]; - } - - if (certificateData != nil) { - NSData *contentIdData; - if(self.onGetLicense) { - contentIdData = [contentId dataUsingEncoding:NSUTF8StringEncoding]; - } else { - contentIdData = [NSData dataWithBytes: [contentId cStringUsingEncoding:NSUTF8StringEncoding] length:[contentId lengthOfBytesUsingEncoding:NSUTF8StringEncoding]]; - } - AVAssetResourceLoadingDataRequest *dataRequest = [loadingRequest dataRequest]; - if (dataRequest != nil) { - NSError *spcError = nil; - NSData *spcData = [loadingRequest streamingContentKeyRequestDataForApp:certificateData contentIdentifier:contentIdData options:nil error:&spcError]; - // Request CKC to the server - NSString *licenseServer = (NSString *)[self->_drm objectForKey:@"licenseServer"]; - if (spcError != nil) { - [self finishLoadingWithError:spcError]; - self->_requestingCertificateErrored = YES; - } - if (spcData != nil) { - if(self.onGetLicense) { - NSString *base64Encoded = [spcData base64EncodedStringWithOptions:0]; - self->_requestingCertificate = YES; - if (licenseServer == nil) { - licenseServer = @""; - } - self.onGetLicense(@{@"licenseUrl": licenseServer, - @"contentId": contentId, - @"spcBase64": base64Encoded, - @"target": self.reactTag} - ); - } else if(licenseServer != nil) { - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; - [request setHTTPMethod:@"POST"]; - [request setURL:[NSURL URLWithString:licenseServer]]; - // HEADERS - NSDictionary *headers = (NSDictionary *)[self->_drm objectForKey:@"headers"]; - if (headers != nil) { - for (NSString *key in headers) { - NSString *value = headers[key]; - [request setValue:value forHTTPHeaderField:key]; - } - } - - if(self.onGetLicense) { - [request setHTTPBody: spcData]; - } else { - NSString *spcEncoded = [spcData base64EncodedStringWithOptions:0]; - NSString *spcUrlEncoded = (NSString *) CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)spcEncoded, NULL, CFSTR("?=&+"), kCFStringEncodingUTF8)); - NSString *post = [NSString stringWithFormat:@"spc=%@&%@", spcUrlEncoded, contentId]; - NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES]; - [request setHTTPBody: postData]; - } - - NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; - NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; - NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; - if (error != nil) { - NSLog(@"Error getting license from %@, HTTP status code %li", url, (long)[httpResponse statusCode]); - [self finishLoadingWithError:error]; - self->_requestingCertificateErrored = YES; - } else { - if([httpResponse statusCode] != 200){ - NSLog(@"Error getting license from %@, HTTP status code %li", url, (long)[httpResponse statusCode]); - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorLicenseRequestNotOk - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining license.", - NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"License server responded with status code %li", (long)[httpResponse statusCode]], - NSLocalizedRecoverySuggestionErrorKey: @"Did you send the correct data to the license Server? Is the server ok?" - } - ]; - [self finishLoadingWithError:licenseError]; - self->_requestingCertificateErrored = YES; - } else if (data != nil) { - if(self.onGetLicense) { - [dataRequest respondWithData:data]; - } else { - NSData *decodedData = [[NSData alloc] initWithBase64EncodedData:data options:0]; - [dataRequest respondWithData:decodedData]; - } - [loadingRequest finishLoading]; - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoDataFromLicenseRequest - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining DRM license.", - NSLocalizedFailureReasonErrorKey: @"No data received from the license server.", - NSLocalizedRecoverySuggestionErrorKey: @"Is the licenseServer ok?." - } - ]; - [self finishLoadingWithError:licenseError]; - self->_requestingCertificateErrored = YES; - } - - } - }]; - [postDataTask resume]; - } - - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoSPC - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining license.", - NSLocalizedFailureReasonErrorKey: @"No spc received.", - NSLocalizedRecoverySuggestionErrorKey: @"Check your DRM config." - } - ]; - [self finishLoadingWithError:licenseError]; - self->_requestingCertificateErrored = YES; - } - - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoDataRequest - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining DRM license.", - NSLocalizedFailureReasonErrorKey: @"No dataRequest found.", - NSLocalizedRecoverySuggestionErrorKey: @"Check your DRM configuration." - } - ]; - [self finishLoadingWithError:licenseError]; - self->_requestingCertificateErrored = YES; - } - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoCertificateData - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining DRM license.", - NSLocalizedFailureReasonErrorKey: @"No certificate data obtained from the specificied url.", - NSLocalizedRecoverySuggestionErrorKey: @"Have you specified a valid 'certificateUrl'?" - } - ]; - [self finishLoadingWithError:licenseError]; - self->_requestingCertificateErrored = YES; - } - }); - return YES; - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoCertificateURL - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining DRM License.", - NSLocalizedFailureReasonErrorKey: @"No certificate URL has been found.", - NSLocalizedRecoverySuggestionErrorKey: @"Did you specified the prop certificateUrl?" - } - ]; - return [self finishLoadingWithError:licenseError]; - } - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoFairplayDRM - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining DRM license.", - NSLocalizedFailureReasonErrorKey: @"Not a valid DRM Scheme has found", - NSLocalizedRecoverySuggestionErrorKey: @"Have you specified the 'drm' 'type' as fairplay?" - } - ]; - return [self finishLoadingWithError:licenseError]; - } - - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoDRMData - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining DRM license.", - NSLocalizedFailureReasonErrorKey: @"No drm object found.", - NSLocalizedRecoverySuggestionErrorKey: @"Have you specified the 'drm' prop?" - } - ]; - return [self finishLoadingWithError:licenseError]; - } - - - return NO; -} - -- (NSData *)base64DataFromBase64String: (NSString *)base64String { - if (base64String != nil) { - // NSData from the Base64 encoded str - NSData *base64Data = [[NSData alloc] initWithBase64EncodedString:base64String options:NSASCIIStringEncoding]; - return base64Data; - } - return nil; -} -#pragma mark - Picture in Picture - -#if TARGET_OS_IOS -- (void)pictureInPictureControllerDidStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { - if (self.onPictureInPictureStatusChanged) { - self.onPictureInPictureStatusChanged(@{ - @"isActive": [NSNumber numberWithBool:false] - }); - } -} - -- (void)pictureInPictureControllerDidStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { - if (self.onPictureInPictureStatusChanged) { - self.onPictureInPictureStatusChanged(@{ - @"isActive": [NSNumber numberWithBool:true] - }); - } -} - -- (void)pictureInPictureControllerWillStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { - -} - -- (void)pictureInPictureControllerWillStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { - -} - -- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController failedToStartPictureInPictureWithError:(NSError *)error { - -} - -- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL))completionHandler { - NSAssert(_restoreUserInterfaceForPIPStopCompletionHandler == NULL, @"restoreUserInterfaceForPIPStopCompletionHandler was not called after picture in picture was exited."); - if (self.onRestoreUserInterfaceForPictureInPictureStop) { - self.onRestoreUserInterfaceForPictureInPictureStop(@{}); - } - _restoreUserInterfaceForPIPStopCompletionHandler = completionHandler; -} -#endif - -@end diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift new file mode 100644 index 00000000..fb5b66ba --- /dev/null +++ b/ios/Video/RCTVideo.swift @@ -0,0 +1,1147 @@ +import AVFoundation +import AVKit +import Foundation +import React + +class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverHandler { + + private var _player:AVPlayer? + private var _playerItem:AVPlayerItem? + private var _source:VideoSource? + private var _playerBufferEmpty:Bool = true + private var _playerLayer:AVPlayerLayer? + + private var _playerViewController:RCTVideoPlayerViewController? + private var _videoURL:NSURL? + + /* DRM */ + private var _drm:DRMParams? + + /* Required to publish events */ + private var _eventDispatcher:RCTEventDispatcher? + private var _videoLoadStarted:Bool = false + + private var _pendingSeek:Bool = false + private var _pendingSeekTime:Float = 0.0 + private var _lastSeekTime:Float = 0.0 + + /* For sending videoProgress events */ + private var _controls:Bool = false + + /* Keep track of any modifiers, need to be applied after each play */ + private var _volume:Float = 1.0 + private var _rate:Float = 1.0 + private var _maxBitRate:Float? + + private var _automaticallyWaitsToMinimizeStalling:Bool = true + private var _muted:Bool = false + private var _paused:Bool = false + private var _repeat:Bool = false + private var _allowsExternalPlayback:Bool = true + private var _textTracks:[TextTrack]? + private var _selectedTextTrackCriteria:SelectedTrackCriteria? + private var _selectedAudioTrackCriteria:SelectedTrackCriteria? + private var _playbackStalled:Bool = false + private var _playInBackground:Bool = false + private var _preventsDisplaySleepDuringVideoPlayback:Bool = true + private var _preferredForwardBufferDuration:Float = 0.0 + private var _playWhenInactive:Bool = false + private var _ignoreSilentSwitch:String! = "inherit" // inherit, ignore, obey + private var _mixWithOthers:String! = "inherit" // inherit, mix, duck + private var _resizeMode:String! = "AVLayerVideoGravityResizeAspectFill" + private var _fullscreen:Bool = false + private var _fullscreenAutorotate:Bool = true + private var _fullscreenOrientation:String! = "all" + private var _fullscreenPlayerPresented:Bool = false + private var _filterName:String! + private var _filterEnabled:Bool = false + private var _presentingViewController:UIViewController? + + private var _resouceLoaderDelegate: RCTResourceLoaderDelegate? + private var _playerObserver: RCTPlayerObserver = RCTPlayerObserver() + +#if canImport(RCTVideoCache) + private var _videoCache:RCTVideoCachingHandler = RCTVideoCachingHandler(self.playerItemPrepareText) +#endif + +#if TARGET_OS_IOS + private let _pip:RCTPictureInPicture = RCTPictureInPicture(self.onPictureInPictureStatusChanged, self.onRestoreUserInterfaceForPictureInPictureStop) +#endif + + // Events + @objc var onVideoLoadStart: RCTDirectEventBlock? + @objc var onVideoLoad: RCTDirectEventBlock? + @objc var onVideoBuffer: RCTDirectEventBlock? + @objc var onVideoError: RCTDirectEventBlock? + @objc var onVideoProgress: RCTDirectEventBlock? + @objc var onBandwidthUpdate: RCTDirectEventBlock? + @objc var onVideoSeek: RCTDirectEventBlock? + @objc var onVideoEnd: RCTDirectEventBlock? + @objc var onTimedMetadata: RCTDirectEventBlock? + @objc var onVideoAudioBecomingNoisy: RCTDirectEventBlock? + @objc var onVideoFullscreenPlayerWillPresent: RCTDirectEventBlock? + @objc var onVideoFullscreenPlayerDidPresent: RCTDirectEventBlock? + @objc var onVideoFullscreenPlayerWillDismiss: RCTDirectEventBlock? + @objc var onVideoFullscreenPlayerDidDismiss: RCTDirectEventBlock? + @objc var onReadyForDisplay: RCTDirectEventBlock? + @objc var onPlaybackStalled: RCTDirectEventBlock? + @objc var onPlaybackResume: RCTDirectEventBlock? + @objc var onPlaybackRateChange: RCTDirectEventBlock? + @objc var onVideoExternalPlaybackChange: RCTDirectEventBlock? + @objc var onPictureInPictureStatusChanged: RCTDirectEventBlock? + @objc var onRestoreUserInterfaceForPictureInPictureStop: RCTDirectEventBlock? + @objc var onGetLicense: RCTDirectEventBlock? + + init(eventDispatcher:RCTEventDispatcher!) { + super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) + + _eventDispatcher = eventDispatcher + + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationWillResignActive(notification:)), + name: UIApplication.willResignActiveNotification, + object: nil + ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationDidEnterBackground(notification:)), + name: UIApplication.didEnterBackgroundNotification, + object: nil + ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationWillEnterForeground(notification:)), + name: UIApplication.willEnterForegroundNotification, + object: nil + ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(audioRouteChanged(notification:)), + name: AVAudioSession.routeChangeNotification, + object: nil + ) + _playerObserver._handlers = self + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + deinit { + NotificationCenter.default.removeObserver(self) + self.removePlayerLayer() + _playerObserver.clearPlayer() + } + + // MARK: - App lifecycle handlers + + @objc func applicationWillResignActive(notification:NSNotification!) { + if _playInBackground || _playWhenInactive || _paused {return} + + _player?.pause() + _player?.rate = 0.0 + } + + @objc func applicationDidEnterBackground(notification:NSNotification!) { + if _playInBackground { + // Needed to play sound in background. See https://developer.apple.com/library/ios/qa/qa1668/_index.html + _playerLayer?.player = nil + _playerViewController?.player = nil + } + } + + @objc func applicationWillEnterForeground(notification:NSNotification!) { + self.applyModifiers() + if _playInBackground { + _playerLayer?.player = _player + _playerViewController?.player = _player + } + } + + // MARK: - Audio events + + @objc func audioRouteChanged(notification:NSNotification!) { + if let userInfo = notification.userInfo { + let reason:AVAudioSession.RouteChangeReason! = userInfo[AVAudioSessionRouteChangeReasonKey] as? AVAudioSession.RouteChangeReason + // let previousRoute:NSNumber! = userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? NSNumber + if reason == .oldDeviceUnavailable, let onVideoAudioBecomingNoisy = onVideoAudioBecomingNoisy { + onVideoAudioBecomingNoisy(["target": reactTag as Any]) + } + } + } + + // MARK: - Progress + + func sendProgressUpdate() { + if let video = _player?.currentItem, + video == nil || video.status != AVPlayerItem.Status.readyToPlay { + return + } + + let playerDuration:CMTime = RCTVideoUtils.playerItemDuration(_player) + if CMTIME_IS_INVALID(playerDuration) { + return + } + + let currentTime = _player?.currentTime() + let currentPlaybackTime = _player?.currentItem?.currentDate() + let duration = CMTimeGetSeconds(playerDuration) + let currentTimeSecs = CMTimeGetSeconds(currentTime ?? .zero) + + NotificationCenter.default.post(name: NSNotification.Name("RCTVideo_progress"), object: nil, userInfo: [ + "progress": NSNumber(value: currentTimeSecs / duration) + ]) + + if currentTimeSecs >= 0 { + onVideoProgress?([ + "currentTime": NSNumber(value: Float(currentTimeSecs)), + "playableDuration": RCTVideoUtils.calculatePlayableDuration(_player), + "atValue": NSNumber(value: currentTime?.value ?? .zero), + "currentPlaybackTime": NSNumber(value: NSNumber(value: floor(currentPlaybackTime?.timeIntervalSince1970 ?? 0 * 1000)).int64Value), + "target": reactTag, + "seekableDuration": RCTVideoUtils.calculateSeekableDuration(_player) + ]) + } + } + + // MARK: - Player and source + + @objc + func setSrc(_ source:NSDictionary!) { + _source = VideoSource(source) + removePlayerLayer() + _playerObserver.player = nil + _playerObserver.playerItem = nil + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(0)) / Double(NSEC_PER_SEC), execute: { [weak self] in + guard let self = self else {return} + // perform on next run loop, otherwise other passed react-props may not be set + self.playerItemForSource(withCallback:{ (playerItem:AVPlayerItem!) in + self._player?.pause() + self._playerItem = playerItem + self._playerObserver.playerItem = self._playerItem + self.setPreferredForwardBufferDuration(self._preferredForwardBufferDuration) + self.setFilter(self._filterName) + if let maxBitRate = self._maxBitRate { + self._playerItem?.preferredPeakBitRate = Double(maxBitRate) + } + + self._player = AVPlayer(playerItem: self._playerItem) + self._playerObserver.player = self._player + + self._player?.actionAtItemEnd = .none + + if #available(iOS 10.0, *) { + self.setAutomaticallyWaitsToMinimizeStalling(self._automaticallyWaitsToMinimizeStalling) + } + + //Perform on next run loop, otherwise onVideoLoadStart is nil + self.onVideoLoadStart?([ + "src": [ + "uri": self._source?.uri ?? NSNull(), + "type": self._source?.type ?? NSNull(), + "isNetwork": NSNumber(value: self._source?.isNetwork ?? false) + ], + "drm": self._drm?.json ?? NSNull(), + "target": self.reactTag + ]) + + }) + }) + _videoLoadStarted = true + } + + @objc + func setDrm(_ drm:NSDictionary!) { + _drm = DRMParams(drm) + } + + func playerItemPrepareText(asset:AVAsset!, assetOptions:NSDictionary?, withCallback handler:(AVPlayerItem?)->Void) { + if (_textTracks == nil) || _textTracks?.count==0 { + handler(AVPlayerItem(asset: asset)) + return + } + + // AVPlayer can't airplay AVMutableCompositions + _allowsExternalPlayback = false + + // sideload text tracks + let mixComposition:AVMutableComposition! = AVMutableComposition() + + let videoAsset:AVAssetTrack! = asset.tracks(withMediaType: AVMediaType.video).first + let videoCompTrack:AVMutableCompositionTrack! = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID:kCMPersistentTrackID_Invalid) + do { + try videoCompTrack.insertTimeRange( + CMTimeRangeMake(start: .zero, duration: videoAsset.timeRange.duration), + of: videoAsset, + at: .zero) + } catch { + } + + let audioAsset:AVAssetTrack! = asset.tracks(withMediaType: AVMediaType.audio).first + let audioCompTrack:AVMutableCompositionTrack! = mixComposition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID:kCMPersistentTrackID_Invalid) + do { + try audioCompTrack.insertTimeRange( + CMTimeRangeMake(start: .zero, duration: videoAsset.timeRange.duration), + of: audioAsset, + at: .zero) + } catch { + } + + var validTextTracks:[TextTrack] = [] + if let textTracks = _textTracks, let textTrackCount = _textTracks?.count { + for i in 0..Void) { + var asset:AVURLAsset! + guard let source = _source, source.uri != nil && source.uri != "" else { + DebugLog("Could not find video URL in source '\(_source)'") + return + } + + let bundlePath = Bundle.main.path(forResource: source.uri, ofType: source.type) ?? "" + let url = source.isNetwork || source.isAsset + ? URL(string: source.uri ?? "") + : URL(fileURLWithPath: bundlePath) + + let assetOptions:NSMutableDictionary! = NSMutableDictionary() + + if url != nil && source.isNetwork { + if let headers = source.requestHeaders, headers.count > 0 { + assetOptions.setObject(headers, forKey:"AVURLAssetHTTPHeaderFieldsKey" as NSCopying) + } + let cookies:[AnyObject]! = HTTPCookieStorage.shared.cookies + assetOptions.setObject(cookies, forKey:AVURLAssetHTTPCookiesKey as NSCopying) +#if canImport(RCTVideoCache) + if _videoCache.playerItemForSourceUsingCache(shouldCache:shouldCache, textTracks:_textTracks, uri:uri, assetOptions:assetOptions, handler:handler) { + return + } +#endif + + asset = AVURLAsset(url: url!, options:assetOptions as! [String : Any]) + } else { + asset = AVURLAsset(url: url!) + } + + if _drm != nil { + _resouceLoaderDelegate = RCTResourceLoaderDelegate( + asset: asset, + drm: _drm, + onVideoError: onVideoError, + onGetLicense: onGetLicense, + reactTag: reactTag + ) + } + + self.playerItemPrepareText(asset: asset, assetOptions:assetOptions, withCallback:handler) + } + + // MARK: - Prop setters + + @objc + func setResizeMode(_ mode: String?) { + if _controls { + _playerViewController?.videoGravity = AVLayerVideoGravity(rawValue: mode ?? "") + } else { + _playerLayer?.videoGravity = AVLayerVideoGravity(rawValue: mode ?? "") + } + _resizeMode = mode + } + + @objc + func setPlayInBackground(_ playInBackground:Bool) { + _playInBackground = playInBackground + } + + @objc + func setPreventsDisplaySleepDuringVideoPlayback(_ preventsDisplaySleepDuringVideoPlayback:Bool) { + _preventsDisplaySleepDuringVideoPlayback = preventsDisplaySleepDuringVideoPlayback + self.applyModifiers() + } + + @objc + func setAllowsExternalPlayback(_ allowsExternalPlayback:Bool) { + _allowsExternalPlayback = allowsExternalPlayback + _player?.allowsExternalPlayback = _allowsExternalPlayback + } + + @objc + func setPlayWhenInactive(_ playWhenInactive:Bool) { + _playWhenInactive = playWhenInactive + } + + @objc + func setPictureInPicture(_ pictureInPicture:Bool) { +#if TARGET_OS_IOS + _pip.setPictureInPicture(pictureInPicture) +#endif + } + + @objc + func setRestoreUserInterfaceForPIPStopCompletionHandler(_ restore:Bool) { +#if TARGET_OS_IOS + _pip.setRestoreUserInterfaceForPIPStopCompletionHandler(restore) +#endif + } + + @objc + func setIgnoreSilentSwitch(_ ignoreSilentSwitch:String!) { + _ignoreSilentSwitch = ignoreSilentSwitch + self.applyModifiers() + } + + @objc + func setMixWithOthers(_ mixWithOthers:String!) { + _mixWithOthers = mixWithOthers + self.applyModifiers() + } + + @objc + func setPaused(_ paused:Bool) { + if paused { + _player?.pause() + _player?.rate = 0.0 + } else { + let session:AVAudioSession! = AVAudioSession.sharedInstance() + var category:AVAudioSession.Category? = nil + var options:AVAudioSession.CategoryOptions? = nil + + if (_ignoreSilentSwitch == "ignore") { + category = AVAudioSession.Category.playback + } else if (_ignoreSilentSwitch == "obey") { + category = AVAudioSession.Category.ambient + } + + if (_mixWithOthers == "mix") { + options = .mixWithOthers + } else if (_mixWithOthers == "duck") { + options = .duckOthers + } + + if let category = category, let options = options { + do { + try session.setCategory(category, options: options) + } catch { + } + } else if let category = category, options == nil { + do { + try session.setCategory(category) + } catch { + } + } else if category == nil, let options = options { + do { + try session.setCategory(session.category, options: options) + } catch { + } + } + + if #available(iOS 10.0, *), !_automaticallyWaitsToMinimizeStalling { + _player?.playImmediately(atRate: _rate) + } else { + _player?.play() + _player?.rate = _rate + } + _player?.rate = _rate + } + + _paused = paused + } + + @objc + func setCurrentTime(_ currentTime:Float) { + let info:NSDictionary! = [ + "time": NSNumber(value: currentTime), + "tolerance": NSNumber(value: 100) + ] + setSeek(info) + } + + @objc + func setSeek(_ info:NSDictionary!) { + let seekTime:NSNumber! = info["time"] as! NSNumber + let seekTolerance:NSNumber! = info["tolerance"] as! NSNumber + + let timeScale:Int = 1000 + + let item:AVPlayerItem! = _player?.currentItem + guard item != nil && item.status == AVPlayerItem.Status.readyToPlay else { + _pendingSeek = true + _pendingSeekTime = seekTime.floatValue + return + } + + // TODO check loadedTimeRanges + let cmSeekTime:CMTime = CMTimeMakeWithSeconds(Float64(seekTime.floatValue), preferredTimescale: Int32(timeScale)) + let current:CMTime = item.currentTime() + // TODO figure out a good tolerance level + let tolerance:CMTime = CMTimeMake(value: Int64(seekTolerance.floatValue), timescale: Int32(timeScale)) + let wasPaused:Bool = _paused + + guard CMTimeCompare(current, cmSeekTime) != 0 else { return } + if !wasPaused { _player?.pause() } + + _player?.seek(to: cmSeekTime, toleranceBefore:tolerance, toleranceAfter:tolerance, completionHandler:{ [weak self] (finished:Bool) in + guard let self = self else { return } + + self._playerObserver.addTimeObserverIfNotSet() + if !wasPaused { + self.setPaused(false) + } + self.onVideoSeek?(["currentTime": NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))), + "seekTime": seekTime, + "target": self.reactTag]) + }) + + _pendingSeek = false + } + + @objc + func setRate(_ rate:Float) { + _rate = rate + applyModifiers() + } + + @objc + func setMuted(_ muted:Bool) { + _muted = muted + applyModifiers() + } + + @objc + func setVolume(_ volume:Float) { + _volume = volume + applyModifiers() + } + + @objc + func setMaxBitRate(_ maxBitRate:Float) { + _maxBitRate = maxBitRate + _playerItem?.preferredPeakBitRate = Double(maxBitRate) + } + + @objc + func setPreferredForwardBufferDuration(_ preferredForwardBufferDuration:Float) { + _preferredForwardBufferDuration = preferredForwardBufferDuration + if #available(iOS 10.0, *) { + _playerItem?.preferredForwardBufferDuration = TimeInterval(preferredForwardBufferDuration) + } else { + // Fallback on earlier versions + } + } + + @objc + func setAutomaticallyWaitsToMinimizeStalling(_ waits:Bool) { + _automaticallyWaitsToMinimizeStalling = waits + if #available(iOS 10.0, *) { + _player?.automaticallyWaitsToMinimizeStalling = waits + } else { + // Fallback on earlier versions + } + } + + + func applyModifiers() { + if _muted { + if !_controls { + _player?.volume = 0 + } + _player?.isMuted = true + } else { + _player?.volume = _volume + _player?.isMuted = false + } + + if #available(iOS 12.0, *) { + _player?.preventsDisplaySleepDuringVideoPlayback = _preventsDisplaySleepDuringVideoPlayback + } else { + // Fallback on earlier versions + } + + if let _maxBitRate = _maxBitRate { + setMaxBitRate(_maxBitRate) + } + + setSelectedAudioTrack(_selectedAudioTrackCriteria) + setSelectedTextTrack(_selectedTextTrackCriteria) + setResizeMode(_resizeMode) + setRepeat(_repeat) + setPaused(_paused) + setControls(_controls) + setAllowsExternalPlayback(_allowsExternalPlayback) + } + + @objc + func setRepeat(_ `repeat`: Bool) { + _repeat = `repeat` + } + + + + @objc + func setSelectedAudioTrack(_ selectedAudioTrack:NSDictionary!) { + setSelectedAudioTrack(SelectedTrackCriteria(selectedAudioTrack)) + } + + func setSelectedAudioTrack(_ selectedAudioTrack:SelectedTrackCriteria!) { + _selectedAudioTrackCriteria = selectedAudioTrack + RCTPlayerOperations.setMediaSelectionTrackForCharacteristic(player:_player, characteristic: AVMediaCharacteristic.audible, + criteria:_selectedAudioTrackCriteria) + } + + @objc + func setSelectedTextTrack(_ selectedTextTrack:NSDictionary!) { + setSelectedTextTrack(SelectedTrackCriteria(selectedTextTrack)) + } + + func setSelectedTextTrack(_ selectedTextTrack:SelectedTrackCriteria!) { + _selectedTextTrackCriteria = selectedTextTrack + if (_textTracks != nil) { // sideloaded text tracks + RCTPlayerOperations.setSideloadedText(player:_player, textTracks:_textTracks, criteria:_selectedTextTrackCriteria) + } else { // text tracks included in the HLS playlist + RCTPlayerOperations.setMediaSelectionTrackForCharacteristic(player:_player, characteristic: AVMediaCharacteristic.legible, + criteria:_selectedTextTrackCriteria) + } + } + + @objc + func setTextTracks(_ textTracks:[NSDictionary]!) { + setTextTracks(textTracks.map { TextTrack($0) }) + } + + func setTextTracks(_ textTracks:[TextTrack]!) { + _textTracks = textTracks + + // in case textTracks was set after selectedTextTrack + if (_selectedTextTrackCriteria != nil) {setSelectedTextTrack(_selectedTextTrackCriteria)} + } + + @objc + func setFullscreen(_ fullscreen:Bool) { + if fullscreen && !_fullscreenPlayerPresented && _player != nil { + // Ensure player view controller is not null + if _playerViewController == nil { + self.usePlayerViewController() + } + + // Set presentation style to fullscreen + _playerViewController?.modalPresentationStyle = .fullScreen + + // Find the nearest view controller + var viewController:UIViewController! = self.firstAvailableUIViewController() + if (viewController == nil) { + let keyWindow:UIWindow! = UIApplication.shared.keyWindow + viewController = keyWindow.rootViewController + if viewController.children.count > 0 + { + viewController = viewController.children.last + } + } + if viewController != nil { + _presentingViewController = viewController + + self.onVideoFullscreenPlayerWillPresent?(["target": reactTag as Any]) + + viewController.present(viewController, animated:true, completion:{ + self._playerViewController?.showsPlaybackControls = true + self._fullscreenPlayerPresented = fullscreen + self._playerViewController?.autorotate = self._fullscreenAutorotate + + self.onVideoFullscreenPlayerDidPresent?(["target": self.reactTag]) + + }) + } + } else if !fullscreen && _fullscreenPlayerPresented, let _playerViewController = _playerViewController { + self.videoPlayerViewControllerWillDismiss(playerViewController: _playerViewController) + _presentingViewController?.dismiss(animated: true, completion:{ + self.videoPlayerViewControllerDidDismiss(playerViewController: _playerViewController) + }) + } + } + + @objc + func setFullscreenAutorotate(_ autorotate:Bool) { + _fullscreenAutorotate = autorotate + if _fullscreenPlayerPresented { + _playerViewController?.autorotate = autorotate + } + } + + @objc + func setFullscreenOrientation(_ orientation:String!) { + _fullscreenOrientation = orientation + if _fullscreenPlayerPresented { + _playerViewController?.preferredOrientation = orientation + } + } + + func usePlayerViewController() { + guard _player != nil else { return } + + if _playerViewController == nil { + _playerViewController = createPlayerViewController(player: _player, withPlayerItem:_playerItem) + + } + // to prevent video from being animated when resizeMode is 'cover' + // resize mode must be set before subview is added + setResizeMode(_resizeMode) + + guard let _playerViewController = _playerViewController else { return } + + if _controls { + let viewController:UIViewController! = self.reactViewController() + viewController.addChild(_playerViewController) + self.addSubview(_playerViewController.view) + } + + _playerObserver.playerViewController = _playerViewController + } + + func createPlayerViewController(player:AVPlayer!, withPlayerItem playerItem:AVPlayerItem!) -> RCTVideoPlayerViewController! { + let viewController:RCTVideoPlayerViewController! = RCTVideoPlayerViewController() + viewController.showsPlaybackControls = true + viewController.rctDelegate = self + viewController.preferredOrientation = _fullscreenOrientation + + viewController.view.frame = self.bounds + viewController.player = player + return viewController + } + + func usePlayerLayer() { + if let _player = _player { + _playerLayer = AVPlayerLayer(player: _player) + _playerLayer?.frame = self.bounds + _playerLayer?.needsDisplayOnBoundsChange = true + + // to prevent video from being animated when resizeMode is 'cover' + // resize mode must be set before layer is added + setResizeMode(_resizeMode) + _playerObserver.playerLayer = _playerLayer + + if let _playerLayer = _playerLayer { + self.layer.addSublayer(_playerLayer) + } + self.layer.needsDisplayOnBoundsChange = true +#if TARGET_OS_IOS + _pip.setupPipController(_playerLayer) +#endif + } + } + + @objc + func setControls(_ controls:Bool) { + if _controls != controls || ((_playerLayer == nil) && (_playerViewController == nil)) + { + _controls = controls + if _controls + { + self.removePlayerLayer() + self.usePlayerViewController() + } + else + { + _playerViewController?.view.removeFromSuperview() + _playerViewController = nil + _playerObserver.playerViewController = nil + self.usePlayerLayer() + } + } + } + + @objc + func setProgressUpdateInterval(_ progressUpdateInterval:Float) { + _playerObserver.replaceTimeObserverIfSet(Float64(progressUpdateInterval)) + } + + func removePlayerLayer() { + _resouceLoaderDelegate = nil + _playerLayer?.removeFromSuperlayer() + _playerLayer = nil + _playerObserver.playerLayer = nil + } + + // MARK: - RCTVideoPlayerViewControllerDelegate + + func videoPlayerViewControllerWillDismiss(playerViewController:AVPlayerViewController) { + if _playerViewController == playerViewController && _fullscreenPlayerPresented, let onVideoFullscreenPlayerWillDismiss = onVideoFullscreenPlayerWillDismiss { + _playerObserver.removePlayerViewControllerObservers() + onVideoFullscreenPlayerWillDismiss(["target": reactTag as Any]) + } + } + + + func videoPlayerViewControllerDidDismiss(playerViewController:AVPlayerViewController) { + if _playerViewController == playerViewController && _fullscreenPlayerPresented { + _fullscreenPlayerPresented = false + _presentingViewController = nil + _playerViewController = nil + _playerObserver.playerViewController = nil + self.applyModifiers() + + onVideoFullscreenPlayerDidDismiss?(["target": reactTag as Any]) + } + } + + @objc + func setFilter(_ filterName:String!) { + _filterName = filterName + + if !_filterEnabled { + return + } else if let uri = _source?.uri, uri.contains("m3u8") { + return // filters don't work for HLS... return + } else if _playerItem?.asset == nil { + return + } + + let filter:CIFilter! = CIFilter(name: filterName) + if #available(iOS 9.0, *), let _playerItem = _playerItem { + self._playerItem?.videoComposition = AVVideoComposition( + asset: _playerItem.asset, + applyingCIFiltersWithHandler: { (request:AVAsynchronousCIImageFilteringRequest) in + if filter == nil { + request.finish(with: request.sourceImage, context:nil) + } else { + let image:CIImage! = request.sourceImage.clampedToExtent() + filter.setValue(image, forKey:kCIInputImageKey) + let output:CIImage! = filter.outputImage?.cropped(to: request.sourceImage.extent) + request.finish(with: output, context:nil) + } + }) + } else { + // Fallback on earlier versions + } + } + + @objc + func setFilterEnabled(_ filterEnabled:Bool) { + _filterEnabled = filterEnabled + } + + // MARK: - React View Management + + func insertReactSubview(view:UIView!, atIndex:Int) { + // We are early in the game and somebody wants to set a subview. + // That can only be in the context of playerViewController. + if !_controls && (_playerLayer == nil) && (_playerViewController == nil) { + setControls(true) + } + + if _controls { + view.frame = self.bounds + _playerViewController?.contentOverlayView?.insertSubview(view, at:atIndex) + } else { + RCTLogError("video cannot have any subviews") + } + return + } + + func removeReactSubview(subview:UIView!) { + if _controls { + subview.removeFromSuperview() + } else { + RCTLog("video cannot have any subviews") + } + return + } + + override func layoutSubviews() { + super.layoutSubviews() + if _controls, let _playerViewController = _playerViewController { + _playerViewController.view.frame = bounds + + // also adjust all subviews of contentOverlayView + for subview in _playerViewController.contentOverlayView?.subviews ?? [] { + subview.frame = bounds + } + } else { + CATransaction.begin() + CATransaction.setAnimationDuration(0) + _playerLayer?.frame = bounds + CATransaction.commit() + } + } + + // MARK: - Lifecycle + + override func removeFromSuperview() { + _player?.pause() + _player = nil + _playerObserver.clearPlayer() + + self.removePlayerLayer() + + if let _playerViewController = _playerViewController { + _playerViewController.view.removeFromSuperview() + _playerViewController.rctDelegate = nil + _playerViewController.player = nil + self._playerViewController = nil + _playerObserver.playerViewController = nil + } + + _eventDispatcher = nil + NotificationCenter.default.removeObserver(self) + + super.removeFromSuperview() + } + + // MARK: - Export + + @objc + func save(options:NSDictionary!, resolve: @escaping RCTPromiseResolveBlock, reject:@escaping RCTPromiseRejectBlock) { + RCTVideoSave.save( + options:options, + resolve:resolve, + reject:reject, + playerItem:_playerItem + ) + } + + func setLicenseResult(_ license:String!) { + _resouceLoaderDelegate?.setLicenseResult(license) + } + + func setLicenseResultError(_ error:String!) { + _resouceLoaderDelegate?.setLicenseResultError(error) + } + + // MARK: - RCTPlayerObserverHandler + + func handleTimeUpdate(time:CMTime) { + sendProgressUpdate() + } + + func handleReadyForDisplay(changeObject: Any, change:NSKeyValueObservedChange) { + onReadyForDisplay?([ + "target": reactTag + ]) + } + + // When timeMetadata is read the event onTimedMetadata is triggered + func handleTimeMetadataChange(playerItem:AVPlayerItem, change:NSKeyValueObservedChange<[AVMetadataItem]?>) { + guard let newValue = change.newValue, let _items = newValue, _items.count > 0 else { + return + } + + var metadata: [[String:String?]?] = [] + for item in _items { + let value = item.value as? String + let identifier = item.identifier?.rawValue + + if let value = value { + metadata.append(["value":value, "identifier":identifier]) + } + } + + onTimedMetadata?([ + "target": reactTag, + "metadata": metadata + ]) + } + + // Handle player item status change. + func handlePlayerItemStatusChange(playerItem:AVPlayerItem, change:NSKeyValueObservedChange) { + guard let _playerItem = _playerItem else { + return + } + + if _playerItem.status == .readyToPlay { + handleReadyToPlay() + } else if _playerItem.status == .failed { + handlePlaybackFailed() + } + } + + func handleReadyToPlay() { + guard let _playerItem = _playerItem else { return } + var duration:Float = Float(CMTimeGetSeconds(_playerItem.asset.duration)) + + if duration.isNaN { + duration = 0.0 + } + + var width: Float? = nil + var height: Float? = nil + var orientation = "undefined" + + if _playerItem.asset.tracks(withMediaType: AVMediaType.video).count > 0 { + let videoTrack = _playerItem.asset.tracks(withMediaType: .video)[0] + width = Float(videoTrack.naturalSize.width) + height = Float(videoTrack.naturalSize.height) + let preferredTransform = videoTrack.preferredTransform + + if (videoTrack.naturalSize.width == preferredTransform.tx + && videoTrack.naturalSize.height == preferredTransform.ty) + || (preferredTransform.tx == 0 && preferredTransform.ty == 0) + { + orientation = "landscape" + } else { + orientation = "portrait" + } + } else if _playerItem.presentationSize.height != 0.0 { + width = Float(_playerItem.presentationSize.width) + height = Float(_playerItem.presentationSize.height) + orientation = _playerItem.presentationSize.width > _playerItem.presentationSize.height ? "landscape" : "portrait" + } + + if _pendingSeek { + setCurrentTime(_pendingSeekTime) + _pendingSeek = false + } + + if _videoLoadStarted { + onVideoLoad?(["duration": NSNumber(value: duration), + "currentTime": NSNumber(value: Float(CMTimeGetSeconds(_playerItem.currentTime()))), + "canPlayReverse": NSNumber(value: _playerItem.canPlayReverse), + "canPlayFastForward": NSNumber(value: _playerItem.canPlayFastForward), + "canPlaySlowForward": NSNumber(value: _playerItem.canPlaySlowForward), + "canPlaySlowReverse": NSNumber(value: _playerItem.canPlaySlowReverse), + "canStepBackward": NSNumber(value: _playerItem.canStepBackward), + "canStepForward": NSNumber(value: _playerItem.canStepForward), + "naturalSize": [ + "width": width != nil ? NSNumber(value: width!) : "undefinded", + "height": width != nil ? NSNumber(value: height!) : "undefinded", + "orientation": orientation + ], + "audioTracks": RCTVideoUtils.getAudioTrackInfo(_player), + "textTracks": _textTracks ?? RCTVideoUtils.getTextTrackInfo(_player), + "target": reactTag as Any]) + } + _videoLoadStarted = false + _playerObserver.attachPlayerEventListeners() + applyModifiers() + } + + func handlePlaybackFailed() { + guard let _playerItem = _playerItem else { return } + onVideoError?( + [ + "error": [ + "code": NSNumber(value: (_playerItem.error! as NSError).code), + "localizedDescription": _playerItem.error?.localizedDescription == nil ? "" : _playerItem.error?.localizedDescription, + "localizedFailureReason": ((_playerItem.error! as NSError).localizedFailureReason == nil ? "" : (_playerItem.error! as NSError).localizedFailureReason) ?? "", + "localizedRecoverySuggestion": ((_playerItem.error! as NSError).localizedRecoverySuggestion == nil ? "" : (_playerItem.error! as NSError).localizedRecoverySuggestion) ?? "", + "domain": (_playerItem.error as! NSError).domain + ], + "target": reactTag + ]) + } + + func handlePlaybackBufferKeyEmpty(playerItem:AVPlayerItem, change:NSKeyValueObservedChange) { + _playerBufferEmpty = true + onVideoBuffer?(["isBuffering": true, "target": reactTag as Any]) + } + + // Continue playing (or not if paused) after being paused due to hitting an unbuffered zone. + func handlePlaybackLikelyToKeepUp(playerItem:AVPlayerItem, change:NSKeyValueObservedChange) { + if (!(_controls || _fullscreenPlayerPresented) || _playerBufferEmpty) && ((_playerItem?.isPlaybackLikelyToKeepUp) != nil) { + setPaused(_paused) + } + _playerBufferEmpty = false + onVideoBuffer?(["isBuffering": false, "target": reactTag as Any]) + } + + func handlePlaybackRateChange(player: AVPlayer, change: NSKeyValueObservedChange) { + guard let _player = _player else { return } + onPlaybackRateChange?(["playbackRate": NSNumber(value: _player.rate), + "target": reactTag as Any]) + if _playbackStalled && _player.rate > 0 { + onPlaybackResume?(["playbackRate": NSNumber(value: _player.rate), + "target": reactTag as Any]) + _playbackStalled = false + } + } + + func handleExternalPlaybackActiveChange(player: AVPlayer, change: NSKeyValueObservedChange) { + guard let _player = _player else { return } + onVideoExternalPlaybackChange?(["isExternalPlaybackActive": NSNumber(value: _player.isExternalPlaybackActive), + "target": reactTag as Any]) + } + + func handleViewControllerOverlayViewFrameChange(overlayView:UIView, change:NSKeyValueObservedChange) { + let oldRect = change.oldValue + let newRect = change.newValue + if !oldRect!.equalTo(newRect!) { + if newRect!.equalTo(UIScreen.main.bounds) { + NSLog("in fullscreen") + + self.reactViewController().view.frame = UIScreen.main.bounds + self.reactViewController().view.setNeedsLayout() + } else {NSLog("not fullscreen")} + } + } + + @objc func handleDidFailToFinishPlaying(notification:NSNotification!) { + let error:NSError! = notification.userInfo?[AVPlayerItemFailedToPlayToEndTimeErrorKey] as? NSError + onVideoError?( + [ + "error": [ + "code": NSNumber(value: (error as NSError).code), + "localizedDescription": error.localizedDescription ?? "", + "localizedFailureReason": (error as NSError).localizedFailureReason ?? "", + "localizedRecoverySuggestion": (error as NSError).localizedRecoverySuggestion ?? "", + "domain": (error as NSError).domain + ], + "target": reactTag + ]) + } + + @objc func handlePlaybackStalled(notification:NSNotification!) { + onPlaybackStalled?(["target": reactTag as Any]) + _playbackStalled = true + } + + @objc func handlePlayerItemDidReachEnd(notification:NSNotification!) { + onVideoEnd?(["target": reactTag as Any]) + + if _repeat { + let item:AVPlayerItem! = notification.object as? AVPlayerItem + item.seek(to: CMTime.zero) + self.applyModifiers() + } else { + _playerObserver.removePlayerTimeObserver() + } + } + + //unused +// @objc func handleAVPlayerAccess(notification:NSNotification!) { +// let accessLog:AVPlayerItemAccessLog! = (notification.object as! AVPlayerItem).accessLog() +// let lastEvent:AVPlayerItemAccessLogEvent! = accessLog.events.last +// +// /* TODO: get this working +// if (self.onBandwidthUpdate) { +// self.onBandwidthUpdate(@{@"bitrate": [NSNumber numberWithFloat:lastEvent.observedBitrate]}); +// } +// */ +// } +} diff --git a/ios/Video/RCTVideoManager.h b/ios/Video/RCTVideoManager.h deleted file mode 100644 index b3bfccb5..00000000 --- a/ios/Video/RCTVideoManager.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface RCTVideoManager : RCTViewManager - -@end diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index 000a9e83..ece0a1e3 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -1,22 +1,7 @@ -#import "RCTVideoManager.h" -#import "RCTVideo.h" #import -#import -#import +#import "React/RCTViewManager.h" -@implementation RCTVideoManager - -RCT_EXPORT_MODULE(); - -- (UIView *)view -{ - return [[RCTVideo alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; -} - -- (dispatch_queue_t)methodQueue -{ - return self.bridge.uiManager.methodQueue; -} +@interface RCT_EXTERN_MODULE(RCTVideoManager, RCTViewManager) RCT_EXPORT_VIEW_PROPERTY(src, NSDictionary); RCT_EXPORT_VIEW_PROPERTY(drm, NSDictionary); @@ -49,6 +34,7 @@ RCT_EXPORT_VIEW_PROPERTY(filter, NSString); RCT_EXPORT_VIEW_PROPERTY(filterEnabled, BOOL); RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float); RCT_EXPORT_VIEW_PROPERTY(restoreUserInterfaceForPIPStopCompletionHandler, BOOL); + /* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */ RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTDirectEventBlock); @@ -70,64 +56,18 @@ RCT_EXPORT_VIEW_PROPERTY(onPlaybackResume, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onPlaybackRateChange, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoExternalPlaybackChange, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onGetLicense, RCTDirectEventBlock); -RCT_REMAP_METHOD(save, - options:(NSDictionary *)options - reactTag:(nonnull NSNumber *)reactTag - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - [self.bridge.uiManager prependUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTVideo *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RCTVideo class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RCTVideo, got: %@", view); - } else { - [view save:options resolve:resolve reject:reject]; - } - }]; -}; -RCT_REMAP_METHOD(setLicenseResult, - license:(NSString *)license - reactTag:(nonnull NSNumber *)reactTag) -{ - [self.bridge.uiManager prependUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTVideo *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RCTVideo class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RCTVideo, got: %@", view); - } else { - [view setLicenseResult:license]; - } - }]; -}; - -RCT_REMAP_METHOD(setLicenseResultError, - error:(NSString *)error - reactTag:(nonnull NSNumber *)reactTag) -{ - [self.bridge.uiManager prependUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTVideo *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RCTVideo class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RCTVideo, got: %@", view); - } else { - [view setLicenseResultError:error]; - } - }]; -}; RCT_EXPORT_VIEW_PROPERTY(onPictureInPictureStatusChanged, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onRestoreUserInterfaceForPictureInPictureStop, RCTDirectEventBlock); -- (NSDictionary *)constantsToExport -{ - return @{ - @"ScaleNone": AVLayerVideoGravityResizeAspect, - @"ScaleToFill": AVLayerVideoGravityResize, - @"ScaleAspectFit": AVLayerVideoGravityResizeAspect, - @"ScaleAspectFill": AVLayerVideoGravityResizeAspectFill - }; -} +RCT_EXTERN_METHOD(save:(NSDictionary *)options + reactTag:(nonnull NSNumber *)reactTag + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) -+ (BOOL)requiresMainQueueSetup -{ - return YES; -} +RCT_EXTERN_METHOD(setLicenseResult:(NSString *)license + reactTag:(nonnull NSNumber *)reactTag) + +RCT_EXTERN_METHOD(setLicenseResultError(NSString *)error + reactTag:(nonnull NSNumber *)reactTag) @end diff --git a/ios/Video/RCTVideoManager.swift b/ios/Video/RCTVideoManager.swift new file mode 100644 index 00000000..bf42f707 --- /dev/null +++ b/ios/Video/RCTVideoManager.swift @@ -0,0 +1,63 @@ +import AVFoundation +import React + +@objc(RCTVideoManager) +class RCTVideoManager: RCTViewManager { + + override func view() -> UIView { + return RCTVideo(eventDispatcher: bridge.eventDispatcher()) + } + + func methodQueue() -> DispatchQueue { + return bridge.uiManager.methodQueue + } + + @objc(save:reactTag:resolver:rejecter:) + func save(options: NSDictionary, reactTag: NSNumber, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void { + bridge.uiManager.prependUIBlock({_ , viewRegistry in + let view = viewRegistry?[reactTag] + if !(view is RCTVideo) { + RCTLogError("Invalid view returned from registry, expecting RCTVideo, got: %@", String(describing: view)) + } else if let view = view as? RCTVideo { + view.save(options: options, resolve: resolve, reject: reject) + } + }) + } + + @objc(setLicenseResult:reactTag:) + func setLicenseResult(license: NSString, reactTag: NSNumber) -> Void { + bridge.uiManager.prependUIBlock({_ , viewRegistry in + let view = viewRegistry?[reactTag] + if !(view is RCTVideo) { + RCTLogError("Invalid view returned from registry, expecting RCTVideo, got: %@", String(describing: view)) + } else if let view = view as? RCTVideo { + view.setLicenseResult(license as String) + } + }) + } + + @objc(setLicenseResultError:reactTag:) + func setLicenseResultError(error: NSString, reactTag: NSNumber) -> Void { + bridge.uiManager.prependUIBlock({_ , viewRegistry in + let view = viewRegistry?[reactTag] + if !(view is RCTVideo) { + RCTLogError("Invalid view returned from registry, expecting RCTVideo, got: %@", String(describing: view)) + } else if let view = view as? RCTVideo { + view.setLicenseResultError(error as String) + } + }) + } + + override func constantsToExport() -> [AnyHashable : Any]? { + return [ + "ScaleNone": AVLayerVideoGravity.resizeAspect, + "ScaleToFill": AVLayerVideoGravity.resize, + "ScaleAspectFit": AVLayerVideoGravity.resizeAspect, + "ScaleAspectFill": AVLayerVideoGravity.resizeAspectFill + ] + } + + override class func requiresMainQueueSetup() -> Bool { + return true + } +} diff --git a/ios/Video/RCTVideoPlayerViewController.h b/ios/Video/RCTVideoPlayerViewController.h deleted file mode 100644 index ed9ebdde..00000000 --- a/ios/Video/RCTVideoPlayerViewController.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// RCTVideoPlayerViewController.h -// RCTVideo -// -// Created by Stanisław Chmiela on 31.03.2016. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import -#import "RCTVideo.h" -#import "RCTVideoPlayerViewControllerDelegate.h" - -@interface RCTVideoPlayerViewController : AVPlayerViewController -@property (nonatomic, weak) id rctDelegate; - -// Optional paramters -@property (nonatomic, weak) NSString* preferredOrientation; -@property (nonatomic) BOOL autorotate; - -@end diff --git a/ios/Video/RCTVideoPlayerViewController.m b/ios/Video/RCTVideoPlayerViewController.m deleted file mode 100644 index 548a06ce..00000000 --- a/ios/Video/RCTVideoPlayerViewController.m +++ /dev/null @@ -1,43 +0,0 @@ -#import "RCTVideoPlayerViewController.h" - -@interface RCTVideoPlayerViewController () - -@end - -@implementation RCTVideoPlayerViewController - -- (BOOL)shouldAutorotate { - - if (self.autorotate || self.preferredOrientation.lowercaseString == nil || [self.preferredOrientation.lowercaseString isEqualToString:@"all"]) - return YES; - - return NO; -} - -- (void)viewDidDisappear:(BOOL)animated -{ - [super viewDidDisappear:animated]; - [_rctDelegate videoPlayerViewControllerWillDismiss:self]; - [_rctDelegate videoPlayerViewControllerDidDismiss:self]; -} - -#if !TARGET_OS_TV -- (UIInterfaceOrientationMask)supportedInterfaceOrientations { - return UIInterfaceOrientationMaskAll; -} - -- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { - if ([self.preferredOrientation.lowercaseString isEqualToString:@"landscape"]) { - return UIInterfaceOrientationLandscapeRight; - } - else if ([self.preferredOrientation.lowercaseString isEqualToString:@"portrait"]) { - return UIInterfaceOrientationPortrait; - } - else { // default case - UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; - return orientation; - } -} -#endif - -@end diff --git a/ios/Video/RCTVideoPlayerViewController.swift b/ios/Video/RCTVideoPlayerViewController.swift new file mode 100644 index 00000000..95c926af --- /dev/null +++ b/ios/Video/RCTVideoPlayerViewController.swift @@ -0,0 +1,44 @@ +import AVKit + +class RCTVideoPlayerViewController: AVPlayerViewController { + + var rctDelegate:RCTVideoPlayerViewControllerDelegate! + + // Optional paramters + var preferredOrientation:String? + var autorotate:Bool? + + func shouldAutorotate() -> Bool { + + if autorotate! || preferredOrientation == nil || (preferredOrientation!.lowercased() == "all") { + return true + } + + return false + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + rctDelegate.videoPlayerViewControllerWillDismiss(playerViewController: self) + rctDelegate.videoPlayerViewControllerDidDismiss(playerViewController: self) + } + + #if !TARGET_OS_TV + + func supportedInterfaceOrientations() -> UIInterfaceOrientationMask { + return .all + } + + func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation { + if preferredOrientation?.lowercased() == "landscape" { + return .landscapeRight + } else if preferredOrientation?.lowercased() == "portrait" { + return .portrait + } else { + // default case + let orientation = UIApplication.shared.statusBarOrientation + return orientation + } + } + #endif +} diff --git a/ios/Video/RCTVideoPlayerViewControllerDelegate.h b/ios/Video/RCTVideoPlayerViewControllerDelegate.h deleted file mode 100644 index e84b3f52..00000000 --- a/ios/Video/RCTVideoPlayerViewControllerDelegate.h +++ /dev/null @@ -1,7 +0,0 @@ -#import -#import "AVKit/AVKit.h" - -@protocol RCTVideoPlayerViewControllerDelegate -- (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController; -- (void)videoPlayerViewControllerDidDismiss:(AVPlayerViewController *)playerViewController; -@end diff --git a/ios/Video/RCTVideoPlayerViewControllerDelegate.swift b/ios/Video/RCTVideoPlayerViewControllerDelegate.swift new file mode 100644 index 00000000..6635975f --- /dev/null +++ b/ios/Video/RCTVideoPlayerViewControllerDelegate.swift @@ -0,0 +1,7 @@ +import Foundation +import AVKit + +protocol RCTVideoPlayerViewControllerDelegate : NSObject { + func videoPlayerViewControllerWillDismiss(playerViewController:AVPlayerViewController) + func videoPlayerViewControllerDidDismiss(playerViewController:AVPlayerViewController) +} diff --git a/ios/Video/UIView+FindUIViewController.h b/ios/Video/UIView+FindUIViewController.h deleted file mode 100644 index 09214261..00000000 --- a/ios/Video/UIView+FindUIViewController.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// UIView+FindUIViewController.h -// RCTVideo -// -// Created by Stanisław Chmiela on 31.03.2016. -// Copyright © 2016 Facebook. All rights reserved. -// -// Source: http://stackoverflow.com/a/3732812/1123156 - -#import - -@interface UIView (FindUIViewController) -- (UIViewController *) firstAvailableUIViewController; -- (id) traverseResponderChainForUIViewController; -@end diff --git a/ios/Video/UIView+FindUIViewController.m b/ios/Video/UIView+FindUIViewController.m deleted file mode 100644 index c29c742a..00000000 --- a/ios/Video/UIView+FindUIViewController.m +++ /dev/null @@ -1,21 +0,0 @@ -// Source: http://stackoverflow.com/a/3732812/1123156 - -#import "UIView+FindUIViewController.h" - -@implementation UIView (FindUIViewController) -- (UIViewController *) firstAvailableUIViewController { - // convenience function for casting and to "mask" the recursive function - return (UIViewController *)[self traverseResponderChainForUIViewController]; -} - -- (id) traverseResponderChainForUIViewController { - id nextResponder = [self nextResponder]; - if ([nextResponder isKindOfClass:[UIViewController class]]) { - return nextResponder; - } else if ([nextResponder isKindOfClass:[UIView class]]) { - return [nextResponder traverseResponderChainForUIViewController]; - } else { - return nil; - } -} -@end diff --git a/ios/Video/UIView+FindUIViewController.swift b/ios/Video/UIView+FindUIViewController.swift new file mode 100644 index 00000000..2947e1e3 --- /dev/null +++ b/ios/Video/UIView+FindUIViewController.swift @@ -0,0 +1,18 @@ +// Source: http://stackoverflow.com/a/3732812/1123156 + +extension UIView { + func firstAvailableUIViewController() -> UIViewController? { + // convenience function for casting and to "mask" the recursive function + return traverseResponderChainForUIViewController() + } + + func traverseResponderChainForUIViewController() -> UIViewController? { + if let nextUIViewController = next as? UIViewController { + return nextUIViewController + } else if let nextUIView = next as? UIView { + return nextUIView.traverseResponderChainForUIViewController() + } else { + return nil + } + } +} diff --git a/ios/VideoCaching/RCTVideoCachingHandler.swift b/ios/VideoCaching/RCTVideoCachingHandler.swift new file mode 100644 index 00000000..8e37f11a --- /dev/null +++ b/ios/VideoCaching/RCTVideoCachingHandler.swift @@ -0,0 +1,81 @@ +import Foundation +import AVFoundation +import DVAssetLoaderDelegate + +class RCTVideoCachingHandler: NSObject, DVAssetLoaderDelegatesDelegate { + + private var _videoCache:RCTVideoCache! = RCTVideoCache.sharedInstance() + private var _playerItemPrepareText: (AVAsset?, NSDictionary?, (AVPlayerItem?)->Void) -> Void + + init(_ playerItemPrepareText: @escaping (AVAsset?, NSDictionary?, (AVPlayerItem?)->Void) -> Void) { + _playerItemPrepareText = playerItemPrepareText + } + + func playerItemForSourceUsingCache(shouldCache:Bool, textTracks:[AnyObject]?, uri:String, assetOptions:NSMutableDictionary, handler:@escaping (AVPlayerItem?)->Void) -> Bool { + if shouldCache && ((textTracks == nil) || (textTracks!.count == 0)) { + /* The DVURLAsset created by cache doesn't have a tracksWithMediaType property, so trying + * to bring in the text track code will crash. I suspect this is because the asset hasn't fully loaded. + * Until this is fixed, we need to bypass caching when text tracks are specified. + */ + DebugLog("Caching is not supported for uri '\(uri)' because text tracks are not compatible with the cache. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md") + playerItemForSourceUsingCache(uri: uri, assetOptions:assetOptions, withCallback:handler) + return true + } + return false + } + + func playerItemForSourceUsingCache(uri:String!, assetOptions options:NSDictionary!, withCallback handler: @escaping (AVPlayerItem?)->Void) { + let url = URL(string: uri) + _videoCache.getItemForUri(uri, withCallback:{ [weak self] (videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?) in + guard let self = self else { return } + switch (videoCacheStatus) { + case .missingFileExtension: + DebugLog("Could not generate cache key for uri '\(uri)'. It is currently not supported to cache urls that do not include a file extension. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md") + let asset:AVURLAsset! = AVURLAsset(url: url!, options:options as! [String : Any]) + self._playerItemPrepareText(asset, options, handler) + return + + case .unsupportedFileExtension: + DebugLog("Could not generate cache key for uri '\(uri)'. The file extension of that uri is currently not supported. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md") + let asset:AVURLAsset! = AVURLAsset(url: url!, options:options as! [String : Any]) + self._playerItemPrepareText(asset, options, handler) + return + + default: + if let cachedAsset = cachedAsset { + DebugLog("Playing back uri '\(uri)' from cache") + // See note in playerItemForSource about not being able to support text tracks & caching + handler(AVPlayerItem(asset: cachedAsset)) + return + } + } + + let asset:DVURLAsset! = DVURLAsset(url:url, options:options as! [String : Any], networkTimeout:10000) + asset.loaderDelegate = self + + /* More granular code to have control over the DVURLAsset + let resourceLoaderDelegate = DVAssetLoaderDelegate(url: url) + resourceLoaderDelegate.delegate = self + let components = NSURLComponents(url: url, resolvingAgainstBaseURL: false) + components?.scheme = DVAssetLoaderDelegate.scheme() + var asset: AVURLAsset? = nil + if let url = components?.url { + asset = AVURLAsset(url: url, options: options) + } + asset?.resourceLoader.setDelegate(resourceLoaderDelegate, queue: DispatchQueue.main) + */ + + handler(AVPlayerItem(asset: asset)) + }) + } + + // MARK: - DVAssetLoaderDelegate + + func dvAssetLoaderDelegate(loaderDelegate:DVAssetLoaderDelegate!, didLoadData data:NSData!, forURL url:NSURL!) { + _videoCache.storeItem(data as Data?, forUri:url.absoluteString, withCallback:{ (success:Bool) in + DebugLog("Cache data stored successfully 🎉") + }) + } + +} + diff --git a/react-native-video.podspec b/react-native-video.podspec index 7013f957..5c3f5e5d 100644 --- a/react-native-video.podspec +++ b/react-native-video.podspec @@ -16,8 +16,7 @@ Pod::Spec.new do |s| s.tvos.deployment_target = "9.0" s.subspec "Video" do |ss| - ss.source_files = "ios/Video/*.{h,m}" - s.static_framework = true + ss.source_files = "ios/Video/**/*.{h,m,swift}" end s.subspec "VideoCaching" do |ss| @@ -25,14 +24,15 @@ Pod::Spec.new do |s| ss.dependency "SPTPersistentCache", "~> 1.1.0" ss.dependency "DVAssetLoaderDelegate", "~> 0.3.1" - ss.source_files = "ios/VideoCaching/**/*.{h,m}" - s.static_framework = true + ss.source_files = "ios/VideoCaching/**/*.{h,m,swift}" end s.dependency "React-Core" s.default_subspec = "Video" + s.static_framework = true + s.xcconfig = { 'OTHER_LDFLAGS': '-ObjC', } From e27baeb065e407256b6f06e9f124b22e15342cac Mon Sep 17 00:00:00 2001 From: Nick Fujita Date: Thu, 28 Oct 2021 10:34:05 +0900 Subject: [PATCH 13/76] VEX-5938: Update resource loader to handle encrypted local files (#12) Adds offline decryption key and uses it to decrypt content during offline playback Jira: VEX-5938 https://jira.tenkasu.net/browse/VEX-5938 - Update to accept scheme for key required to play offline playback - Uses provided scheme to intercept call from player and return the key - Fixes player item observer removal pattern ### Reviews - Major reviewer (domain expert): @armadilio3 --- README.md | 13 + Video.js | 1 + ios/Video/Features/RCTPlayerObserver.swift | 32 +- ios/Video/Features/RCTPlayerOperations.swift | 20 ++ .../Features/RCTResourceLoaderDelegate.swift | 239 +++++--------- ios/Video/Features/RCTVideoDRM.swift | 168 ++++++++++ .../Features/RCTVideoErrorHandling.swift | 32 +- ios/Video/Features/RCTVideoUtils.swift | 107 +++++++ ios/Video/RCTVideo.swift | 294 +++++++----------- ios/Video/RCTVideoManager.m | 1 + ios/VideoCaching/RCTVideoCachingHandler.swift | 42 +-- react-native-video.podspec | 3 +- 12 files changed, 573 insertions(+), 379 deletions(-) create mode 100644 ios/Video/Features/RCTVideoDRM.swift diff --git a/README.md b/README.md index d46e8d39..ca6745d3 100644 --- a/README.md +++ b/README.md @@ -317,6 +317,7 @@ var styles = StyleSheet.create({ * [trackId](#trackId) * [useTextureView](#usetextureview) * [volume](#volume) +* [localSourceEncryptionKeyScheme](#localSourceEncryptionKeyScheme) ### Event props * [onAudioBecomingNoisy](#onaudiobecomingnoisy) @@ -915,6 +916,18 @@ Adjust the volume. Platforms: all +#### localSourceEncryptionKeyScheme +Set the url scheme for stream encryption key for local assets + +Type: String + +Example: +``` +localSourceEncryptionKeyScheme="my-offline-key" +``` + +Platforms: iOS + ### Event props diff --git a/Video.js b/Video.js index 88d90324..62a179a4 100644 --- a/Video.js +++ b/Video.js @@ -418,6 +418,7 @@ Video.propTypes = { certificateUrl: PropTypes.string, getLicense: PropTypes.func, }), + localSourceEncryptionKeyScheme: PropTypes.string, minLoadRetryCount: PropTypes.number, maxBitRate: PropTypes.number, resizeMode: PropTypes.string, diff --git a/ios/Video/Features/RCTPlayerObserver.swift b/ios/Video/Features/RCTPlayerObserver.swift index d17b27da..2f17a19c 100644 --- a/ios/Video/Features/RCTPlayerObserver.swift +++ b/ios/Video/Features/RCTPlayerObserver.swift @@ -28,39 +28,43 @@ class RCTPlayerObserver: NSObject { var _handlers: RCTPlayerObserverHandler! var player:AVPlayer? { + willSet { + removePlayerObservers() + removePlayerTimeObserver() + } didSet { - if player == nil { - removePlayerObservers() - removePlayerTimeObserver() - } else { + if player != nil { addPlayerObservers() addPlayerTimeObserver() } } } var playerItem:AVPlayerItem? { + willSet { + removePlayerItemObservers() + } didSet { - if playerItem == nil { - removePlayerItemObservers() - } else { + if playerItem != nil { addPlayerItemObservers() } } } var playerViewController:AVPlayerViewController? { + willSet { + removePlayerViewControllerObservers() + } didSet { - if playerViewController == nil { - removePlayerViewControllerObservers() - } else { + if playerViewController != nil { addPlayerViewControllerObservers() } } } var playerLayer:AVPlayerLayer? { + willSet { + removePlayerLayerObserver() + } didSet { if playerLayer == nil { - removePlayerLayerObserver() - } else { addPlayerLayerObserver() } } @@ -148,8 +152,8 @@ class RCTPlayerObserver: NSObject { /* Cancels the previously registered time observer. */ func removePlayerTimeObserver() { - if let timeObserver = _timeObserver { - player?.removeTimeObserver(timeObserver) + if _timeObserver != nil { + player?.removeTimeObserver(_timeObserver) _timeObserver = nil } } diff --git a/ios/Video/Features/RCTPlayerOperations.swift b/ios/Video/Features/RCTPlayerOperations.swift index 8d70f46d..094cbf44 100644 --- a/ios/Video/Features/RCTPlayerOperations.swift +++ b/ios/Video/Features/RCTPlayerOperations.swift @@ -1,5 +1,6 @@ import AVFoundation import MediaAccessibility +import Promises let RCTVideoUnset = -1 @@ -157,4 +158,23 @@ enum RCTPlayerOperations { } } + + static func seek(player: AVPlayer, playerItem:AVPlayerItem, paused:Bool, seekTime:Float, seekTolerance:Float) -> Promise { + let timeScale:Int = 1000 + let cmSeekTime:CMTime = CMTimeMakeWithSeconds(Float64(seekTime), preferredTimescale: Int32(timeScale)) + let current:CMTime = playerItem.currentTime() + let tolerance:CMTime = CMTimeMake(value: Int64(seekTolerance), timescale: Int32(timeScale)) + + return Promise(on: .global()) { fulfill, reject in + guard CMTimeCompare(current, cmSeekTime) != 0 else { + reject(NSError()) + return + } + if !paused { player.pause() } + + player.seek(to: cmSeekTime, toleranceBefore:tolerance, toleranceAfter:tolerance, completionHandler:{ (finished:Bool) in + fulfill(finished) + }) + } + } } diff --git a/ios/Video/Features/RCTResourceLoaderDelegate.swift b/ios/Video/Features/RCTResourceLoaderDelegate.swift index 5ead7fef..e1f11528 100644 --- a/ios/Video/Features/RCTResourceLoaderDelegate.swift +++ b/ios/Video/Features/RCTResourceLoaderDelegate.swift @@ -1,4 +1,5 @@ import AVFoundation +import Promises class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSessionDelegate { @@ -6,6 +7,7 @@ class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSes private var _requestingCertificate:Bool = false private var _requestingCertificateErrored:Bool = false private var _drm: DRMParams? + private var _localSourceEncryptionKeyScheme: String? private var _reactTag: NSNumber? private var _onVideoError: RCTDirectEventBlock? private var _onGetLicense: RCTDirectEventBlock? @@ -14,6 +16,7 @@ class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSes init( asset: AVURLAsset, drm: DRMParams?, + localSourceEncryptionKeyScheme: String?, onVideoError: RCTDirectEventBlock?, onGetLicense: RCTDirectEventBlock?, reactTag: NSNumber @@ -25,6 +28,7 @@ class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSes _onVideoError = onVideoError _onGetLicense = onGetLicense _drm = drm + _localSourceEncryptionKeyScheme = localSourceEncryptionKeyScheme } deinit { @@ -42,16 +46,9 @@ class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSes func resourceLoader(_ resourceLoader:AVAssetResourceLoader, didCancel loadingRequest:AVAssetResourceLoadingRequest) { NSLog("didCancelLoadingRequest") } - - func base64DataFromBase64String(base64String:String?) -> Data? { - if let base64String = base64String { - return Data(base64Encoded:base64String) - } - return nil - } - + func setLicenseResult(_ license:String!) { - guard let respondData = self.base64DataFromBase64String(base64String: license), + guard let respondData = RCTVideoUtils.base64DataFromBase64String(base64String: license), let _loadingRequest = _loadingRequest else { setLicenseResultError("No data from JS license response") return @@ -67,14 +64,13 @@ class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSes } } - func finishLoadingWithError(error:NSError!) -> Bool { + func finishLoadingWithError(error:Error!) -> Bool { if let _loadingRequest = _loadingRequest, let error = error { - let licenseError:NSError! = error - _loadingRequest.finishLoading(with: licenseError) + _loadingRequest.finishLoading(with: error as! NSError) _onVideoError?([ "error": [ - "code": NSNumber(value: error.code), + "code": NSNumber(value: (error as NSError).code), "localizedDescription": error.localizedDescription == nil ? "" : error.localizedDescription, "localizedFailureReason": ((error as NSError).localizedFailureReason == nil ? "" : (error as NSError).localizedFailureReason) ?? "", "localizedRecoverySuggestion": ((error as NSError).localizedRecoverySuggestion == nil ? "" : (error as NSError).localizedRecoverySuggestion) ?? "", @@ -88,6 +84,35 @@ class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSes } func loadingRequestHandling(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool { + if handleEmbeddedKey(loadingRequest) { + return true + } + + if _drm != nil { + return handleDrm(loadingRequest) + } + + return false + } + + func handleEmbeddedKey(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool { + guard let url = loadingRequest.request.url, + let _localSourceEncryptionKeyScheme = _localSourceEncryptionKeyScheme, + let persistentKeyData = RCTVideoUtils.extractDataFromCustomSchemeUrl(from: url, scheme: _localSourceEncryptionKeyScheme) + else { + return false + } + + loadingRequest.contentInformationRequest?.contentType = AVStreamingKeyDeliveryPersistentContentKeyType + loadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true + loadingRequest.contentInformationRequest?.contentLength = Int64(persistentKeyData.count) + loadingRequest.dataRequest?.respond(with: persistentKeyData) + loadingRequest.finishLoading() + + return true + } + + func handleDrm(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool { if _requestingCertificate { return true } else if _requestingCertificateErrored { @@ -95,164 +120,48 @@ class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSes } _loadingRequest = loadingRequest - let url = loadingRequest.request.url - guard let _drm = _drm else { + guard let _drm = _drm, let drmType = _drm.type, drmType == "fairplay" else { return finishLoadingWithError(error: RCTVideoErrorHandler.noDRMData) } - var contentId:String! - let contentIdOverride:String! = _drm.contentId - if contentIdOverride != nil { - contentId = contentIdOverride - } else if (_onGetLicense != nil) { - contentId = url?.host - } else { - contentId = url?.absoluteString.replacingOccurrences(of: "skd://", with:"") - } - - let drmType:String! = _drm.type - guard drmType == "fairplay" else { - return finishLoadingWithError(error: RCTVideoErrorHandler.noDRMData) - } - - let certificateStringUrl:String! = _drm.certificateUrl - guard let certificateStringUrl = certificateStringUrl, let certificateURL = URL(string: certificateStringUrl.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) ?? "") else { - return finishLoadingWithError(error: RCTVideoErrorHandler.noCertificateURL) - } - DispatchQueue.global().async { [weak self] in - guard let self = self else { return } - var certificateData:Data? - if (_drm.base64Certificate != nil) { - certificateData = Data(base64Encoded: certificateData! as Data, options: .ignoreUnknownCharacters) - } else { - do { - certificateData = try Data(contentsOf: certificateURL) - } catch {} - } - - guard let certificateData = certificateData else { - self.finishLoadingWithError(error: RCTVideoErrorHandler.noCertificateData) - self._requestingCertificateErrored = true - return - } - - var contentIdData:NSData! - if self._onGetLicense != nil { - contentIdData = contentId.data(using: .utf8) as NSData? - } else { - contentIdData = NSData(bytes: contentId.cString(using: String.Encoding.utf8), length:contentId.lengthOfBytes(using: String.Encoding.utf8)) - } - - let dataRequest:AVAssetResourceLoadingDataRequest! = loadingRequest.dataRequest - guard dataRequest != nil else { - self.finishLoadingWithError(error: RCTVideoErrorHandler.noCertificateData) - self._requestingCertificateErrored = true - return - } - - var spcError:NSError! - var spcData: Data? - do { - spcData = try loadingRequest.streamingContentKeyRequestData(forApp: certificateData, contentIdentifier: contentIdData as Data, options: nil) - } catch let spcError { - print("SPC error") - } - // Request CKC to the server - var licenseServer:String! = _drm.licenseServer - if spcError != nil { - self.finishLoadingWithError(error: spcError) - self._requestingCertificateErrored = true - } - - guard spcData != nil else { - self.finishLoadingWithError(error: RCTVideoErrorHandler.noSPC) - self._requestingCertificateErrored = true - return - } - - // js client has a onGetLicense callback and will handle license fetching - if let _onGetLicense = self._onGetLicense { - let base64Encoded = spcData?.base64EncodedString(options: []) + var promise: Promise + if _onGetLicense != nil { + let contentId = _drm.contentId ?? loadingRequest.request.url?.host + promise = RCTVideoDRM.handleWithOnGetLicense( + loadingRequest:loadingRequest, + contentId:contentId, + certificateUrl:_drm.certificateUrl, + base64Certificate:_drm.base64Certificate + ) .then{ spcData -> Void in self._requestingCertificate = true - if licenseServer == nil { - licenseServer = "" - } - _onGetLicense(["licenseUrl": licenseServer, - "contentId": contentId, - "spcBase64": base64Encoded, - "target": self._reactTag]) - - - } else if licenseServer != nil { - self.fetchLicense( - licenseServer: licenseServer, - spcData: spcData, - contentId: contentId, - dataRequest: dataRequest - ) + self._onGetLicense?(["licenseUrl": self._drm?.licenseServer ?? "", + "contentId": contentId, + "spcBase64": spcData.base64EncodedString(options: []), + "target": self._reactTag]) } + } else { + promise = RCTVideoDRM.handleInternalGetLicense( + loadingRequest:loadingRequest, + contentId:_drm.contentId, + licenseServer:_drm.licenseServer, + certificateUrl:_drm.certificateUrl, + base64Certificate:_drm.base64Certificate, + headers:_drm.headers + ) .then{ data -> Void in + guard let dataRequest = loadingRequest.dataRequest else { + throw RCTVideoErrorHandler.noCertificateData + } + dataRequest.respond(with:data) + loadingRequest.finishLoading() + } } + + + promise.catch{ error in + self.finishLoadingWithError(error:error) + self._requestingCertificateErrored = true + } + return true } - - func fetchLicense( - licenseServer: String, - spcData: Data?, - contentId: String, - dataRequest: AVAssetResourceLoadingDataRequest! - ) { - var request = URLRequest(url: URL(string: licenseServer)!) - request.httpMethod = "POST" - - // HEADERS - if let headers = _drm?.headers { - for item in headers { - guard let key = item.key as? String, let value = item.value as? String else { - continue - } - request.setValue(value, forHTTPHeaderField: key) - } - } - - if (_onGetLicense != nil) { - request.httpBody = spcData - } else { - let spcEncoded = spcData?.base64EncodedString(options: []) - let spcUrlEncoded = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, spcEncoded as? CFString? as! CFString, nil, "?=&+" as CFString, CFStringBuiltInEncodings.UTF8.rawValue) as? String - let post = String(format:"spc=%@&%@", spcUrlEncoded as! CVarArg, contentId) - let postData = post.data(using: String.Encoding.utf8, allowLossyConversion:true) - request.httpBody = postData - } - - let postDataTask = URLSession.shared.dataTask(with: request as URLRequest, completionHandler:{ [weak self] (data:Data!,response:URLResponse!,error:Error!) in - guard let self = self else { return } - let httpResponse:HTTPURLResponse! = response as! HTTPURLResponse - guard error == nil else { - print("Error getting license from \(licenseServer), HTTP status code \(httpResponse.statusCode)") - self.finishLoadingWithError(error: error as NSError?) - self._requestingCertificateErrored = true - return - } - guard httpResponse.statusCode == 200 else { - print("Error getting license from \(licenseServer), HTTP status code \(httpResponse.statusCode)") - self.finishLoadingWithError(error: RCTVideoErrorHandler.licenseRequestNotOk(httpResponse.statusCode)) - self._requestingCertificateErrored = true - return - } - - guard data != nil else { - self.finishLoadingWithError(error: RCTVideoErrorHandler.noDataFromLicenseRequest) - self._requestingCertificateErrored = true - return - } - - if (self._onGetLicense != nil) { - dataRequest.respond(with: data) - } else if let decodedData = Data(base64Encoded: data, options: []) { - dataRequest.respond(with: decodedData) - } - self._loadingRequest?.finishLoading() - }) - postDataTask.resume() - } } diff --git a/ios/Video/Features/RCTVideoDRM.swift b/ios/Video/Features/RCTVideoDRM.swift new file mode 100644 index 00000000..d059bbc4 --- /dev/null +++ b/ios/Video/Features/RCTVideoDRM.swift @@ -0,0 +1,168 @@ +import AVFoundation +import Promises + +struct RCTVideoDRM { + @available(*, unavailable) private init() {} + + static func fetchLicense( + licenseServer: String, + spcData: Data?, + contentId: String, + headers: [String:Any]? + ) -> Promise { + let request = createLicenseRequest(licenseServer:licenseServer, spcData:spcData, contentId:contentId, headers:headers) + + return Promise(on: .global()) { fulfill, reject in + let postDataTask = URLSession.shared.dataTask(with: request as URLRequest, completionHandler:{ (data:Data!,response:URLResponse!,error:Error!) in + + let httpResponse:HTTPURLResponse! = (response as! HTTPURLResponse) + + guard error == nil else { + print("Error getting license from \(licenseServer), HTTP status code \(httpResponse.statusCode)") + reject(error) + return + } + guard httpResponse.statusCode == 200 else { + print("Error getting license from \(licenseServer), HTTP status code \(httpResponse.statusCode)") + reject(RCTVideoErrorHandler.licenseRequestNotOk(httpResponse.statusCode)) + return + } + + guard data != nil, let decodedData = Data(base64Encoded: data, options: []) else { + reject(RCTVideoErrorHandler.noDataFromLicenseRequest) + return + } + + fulfill(decodedData) + }) + postDataTask.resume() + } + } + + static func createLicenseRequest( + licenseServer: String, + spcData: Data?, + contentId: String, + headers: [String:Any]? + ) -> URLRequest { + var request = URLRequest(url: URL(string: licenseServer)!) + request.httpMethod = "POST" + + if let headers = headers { + for item in headers { + guard let key = item.key as? String, let value = item.value as? String else { + continue + } + request.setValue(value, forHTTPHeaderField: key) + } + } + + let spcEncoded = spcData?.base64EncodedString(options: []) + let spcUrlEncoded = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, spcEncoded as? CFString? as! CFString, nil, "?=&+" as CFString, CFStringBuiltInEncodings.UTF8.rawValue) as? String + let post = String(format:"spc=%@&%@", spcUrlEncoded as! CVarArg, contentId) + let postData = post.data(using: String.Encoding.utf8, allowLossyConversion:true) + request.httpBody = postData + + return request + } + + static func fetchSpcData( + loadingRequest: AVAssetResourceLoadingRequest, + certificateData: Data, + contentIdData: Data + ) -> Promise { + return Promise(on: .global()) { fulfill, reject in + var spcError:NSError! + var spcData: Data? + do { + spcData = try loadingRequest.streamingContentKeyRequestData(forApp: certificateData, contentIdentifier: contentIdData as Data, options: nil) + } catch _ { + print("SPC error") + } + + if spcError != nil { + reject(spcError) + } + + guard let spcData = spcData else { + reject(RCTVideoErrorHandler.noSPC) + return + } + + fulfill(spcData) + } + } + + static func createCertificateData(certificateStringUrl:String?, base64Certificate:Bool?) -> Promise { + return Promise(on: .global()) { fulfill, reject in + + guard let certificateStringUrl = certificateStringUrl, + let certificateURL = URL(string: certificateStringUrl.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) ?? "") else { + reject(RCTVideoErrorHandler.noCertificateURL) + return + } + + var certificateData:Data? + do { + certificateData = try Data(contentsOf: certificateURL) + if (base64Certificate != nil) { + certificateData = Data(base64Encoded: certificateData! as Data, options: .ignoreUnknownCharacters) + } + } catch {} + + guard let certificateData = certificateData else { + reject(RCTVideoErrorHandler.noCertificateData) + return + } + + fulfill(certificateData) + } + } + + static func handleWithOnGetLicense(loadingRequest: AVAssetResourceLoadingRequest, contentId:String?, certificateUrl:String?, base64Certificate:Bool?) -> Promise { + let contentIdData = contentId?.data(using: .utf8) + + return RCTVideoDRM.createCertificateData(certificateStringUrl:certificateUrl, base64Certificate:base64Certificate) + .then{ certificateData -> Promise in + guard let contentIdData = contentIdData else { + throw RCTVideoError.invalidContentId as! Error + } + + return RCTVideoDRM.fetchSpcData( + loadingRequest:loadingRequest, + certificateData:certificateData, + contentIdData:contentIdData + ) + } + } + + static func handleInternalGetLicense(loadingRequest: AVAssetResourceLoadingRequest, contentId:String?, licenseServer:String?, certificateUrl:String?, base64Certificate:Bool?, headers: [String:Any]?) -> Promise { + let url = loadingRequest.request.url + + guard let contentId = contentId ?? url?.absoluteString.replacingOccurrences(of: "skd://", with:"") else { + return Promise(RCTVideoError.invalidContentId as! Error) + } + + let contentIdData = NSData(bytes: contentId.cString(using: String.Encoding.utf8), length:contentId.lengthOfBytes(using: String.Encoding.utf8)) as Data + + return RCTVideoDRM.createCertificateData(certificateStringUrl:certificateUrl, base64Certificate:base64Certificate) + .then{ certificateData in + return RCTVideoDRM.fetchSpcData( + loadingRequest:loadingRequest, + certificateData:certificateData, + contentIdData:contentIdData + ) + } + .then{ spcData -> Promise in + guard let licenseServer = licenseServer else { + throw RCTVideoError.noLicenseServerURL as! Error + } + return RCTVideoDRM.fetchLicense( + licenseServer: licenseServer, + spcData: spcData, + contentId: contentId, + headers: headers + ) + } + } +} diff --git a/ios/Video/Features/RCTVideoErrorHandling.swift b/ios/Video/Features/RCTVideoErrorHandling.swift index 2b65a969..e795aa28 100644 --- a/ios/Video/Features/RCTVideoErrorHandling.swift +++ b/ios/Video/Features/RCTVideoErrorHandling.swift @@ -1,5 +1,6 @@ enum RCTVideoError : Int { case fromJSPart + case noLicenseServerURL case licenseRequestNotOk case noDataFromLicenseRequest case noSPC @@ -8,11 +9,12 @@ enum RCTVideoError : Int { case noCertificateURL case noFairplayDRM case noDRMData + case invalidContentId } enum RCTVideoErrorHandler { - static let noDRMData: NSError = NSError( + static let noDRMData = NSError( domain: "RCTVideo", code: RCTVideoError.noDRMData.rawValue, userInfo: [ @@ -21,7 +23,7 @@ enum RCTVideoErrorHandler { NSLocalizedRecoverySuggestionErrorKey: "Have you specified the 'drm' prop?" ]) - static let noCertificateURL: NSError = NSError( + static let noCertificateURL = NSError( domain: "RCTVideo", code: RCTVideoError.noCertificateURL.rawValue, userInfo: [ @@ -30,7 +32,7 @@ enum RCTVideoErrorHandler { NSLocalizedRecoverySuggestionErrorKey: "Did you specified the prop certificateUrl?" ]) - static let noCertificateData: NSError = NSError( + static let noCertificateData = NSError( domain: "RCTVideo", code: RCTVideoError.noCertificateData.rawValue, userInfo: [ @@ -39,7 +41,7 @@ enum RCTVideoErrorHandler { NSLocalizedRecoverySuggestionErrorKey: "Have you specified a valid 'certificateUrl'?" ]) - static let noSPC:NSError! = NSError( + static let noSPC = NSError( domain: "RCTVideo", code: RCTVideoError.noSPC.rawValue, userInfo: [ @@ -48,13 +50,22 @@ enum RCTVideoErrorHandler { NSLocalizedRecoverySuggestionErrorKey: "Check your DRM config." ]) - static let noDataFromLicenseRequest:NSError! = NSError( + static let noLicenseServerURL = NSError( + domain: "RCTVideo", + code: RCTVideoError.noLicenseServerURL.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining DRM License.", + NSLocalizedFailureReasonErrorKey: "No license server URL has been found.", + NSLocalizedRecoverySuggestionErrorKey: "Did you specified the prop licenseServer?" + ]) + + static let noDataFromLicenseRequest = NSError( domain: "RCTVideo", code: RCTVideoError.noDataFromLicenseRequest.rawValue, userInfo: [ NSLocalizedDescriptionKey: "Error obtaining DRM license.", NSLocalizedFailureReasonErrorKey: "No data received from the license server.", - NSLocalizedRecoverySuggestionErrorKey: "Is the licenseServer ok?." + NSLocalizedRecoverySuggestionErrorKey: "Is the licenseServer ok?" ]) static func licenseRequestNotOk(_ statusCode: Int) -> NSError { @@ -80,4 +91,13 @@ enum RCTVideoErrorHandler { NSLocalizedRecoverySuggestionErrorKey: error ]) } + + static let invalidContentId = NSError( + domain: "RCTVideo", + code: RCTVideoError.invalidContentId.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining DRM license.", + NSLocalizedFailureReasonErrorKey: "No valide content Id received", + NSLocalizedRecoverySuggestionErrorKey: "Is the contentId and url ok?" + ]) } diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift index 51a6aae7..69c127af 100644 --- a/ios/Video/Features/RCTVideoUtils.swift +++ b/ios/Video/Features/RCTVideoUtils.swift @@ -1,4 +1,5 @@ import AVFoundation +import Promises /*! * Collection of pure functions @@ -140,4 +141,110 @@ enum RCTVideoUtils { static func getCurrentTime(playerItem:AVPlayerItem?) -> Float { return Float(CMTimeGetSeconds(playerItem?.currentTime() ?? .zero)) } + + static func base64DataFromBase64String(base64String:String?) -> Data? { + if let base64String = base64String { + return Data(base64Encoded:base64String) + } + return nil + } + + static func replaceURLScheme(url: URL, scheme: String?) -> URL? { + var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) + urlComponents?.scheme = scheme + + return urlComponents?.url + } + + static func extractDataFromCustomSchemeUrl(from url: URL, scheme: String) -> Data? { + guard url.scheme == scheme, + let adoptURL = RCTVideoUtils.replaceURLScheme(url:url, scheme: nil) else { return nil } + + return Data(base64Encoded: adoptURL.absoluteString) + } + + static func generateMixComposition(_ asset:AVAsset) -> AVMutableComposition { + let mixComposition:AVMutableComposition = AVMutableComposition() + + let videoAsset:AVAssetTrack! = asset.tracks(withMediaType: AVMediaType.video).first + let videoCompTrack:AVMutableCompositionTrack! = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID:kCMPersistentTrackID_Invalid) + do { + try videoCompTrack.insertTimeRange( + CMTimeRangeMake(start: .zero, duration: videoAsset.timeRange.duration), + of: videoAsset, + at: .zero) + } catch { + } + + let audioAsset:AVAssetTrack! = asset.tracks(withMediaType: AVMediaType.audio).first + let audioCompTrack:AVMutableCompositionTrack! = mixComposition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID:kCMPersistentTrackID_Invalid) + do { + try audioCompTrack.insertTimeRange( + CMTimeRangeMake(start: .zero, duration: videoAsset.timeRange.duration), + of: audioAsset, + at: .zero) + } catch { + } + + return mixComposition + } + + static func getValidTextTracks(asset:AVAsset, assetOptions:NSDictionary?, mixComposition:AVMutableComposition, textTracks:[TextTrack]?) -> [TextTrack] { + let videoAsset:AVAssetTrack! = asset.tracks(withMediaType: AVMediaType.video).first + var validTextTracks:[TextTrack] = [] + if let textTracks = textTracks, textTracks.count > 0 { + for i in 0.. Promise { + return Promise(on: .global()) { fulfill, reject in + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(seconds)) / Double(NSEC_PER_SEC), execute: { + fulfill(()) + }) + } + } + + static func prepareAsset(source:VideoSource) -> (asset:AVURLAsset?, assetOptions:NSMutableDictionary?)? { + guard source.uri != nil && source.uri != "" else { return nil } + var asset:AVURLAsset! + let bundlePath = Bundle.main.path(forResource: source.uri, ofType: source.type) ?? "" + let url = source.isNetwork || source.isAsset + ? URL(string: source.uri ?? "") + : URL(fileURLWithPath: bundlePath) + let assetOptions:NSMutableDictionary! = NSMutableDictionary() + + if source.isNetwork { + if let headers = source.requestHeaders, headers.count > 0 { + assetOptions.setObject(headers, forKey:"AVURLAssetHTTPHeaderFieldsKey" as NSCopying) + } + let cookies:[AnyObject]! = HTTPCookieStorage.shared.cookies + assetOptions.setObject(cookies, forKey:AVURLAssetHTTPCookiesKey as NSCopying) + asset = AVURLAsset(url: url!, options:assetOptions as! [String : Any]) + } else { + asset = AVURLAsset(url: url!) + } + return (asset, assetOptions) + } } diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index fb5b66ba..d124998d 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -2,6 +2,7 @@ import AVFoundation import AVKit import Foundation import React +import Promises class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverHandler { @@ -17,6 +18,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH /* DRM */ private var _drm:DRMParams? + private var _localSourceEncryptionKeyScheme:String? + /* Required to publish events */ private var _eventDispatcher:RCTEventDispatcher? private var _videoLoadStarted:Bool = false @@ -61,7 +64,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH private var _playerObserver: RCTPlayerObserver = RCTPlayerObserver() #if canImport(RCTVideoCache) - private var _videoCache:RCTVideoCachingHandler = RCTVideoCachingHandler(self.playerItemPrepareText) + private let _videoCache:RCTVideoCachingHandler = RCTVideoCachingHandler() #endif #if TARGET_OS_IOS @@ -125,6 +128,9 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH object: nil ) _playerObserver._handlers = self +#if canImport(RCTVideoCache) + _videoCache.playerItemPrepareText = playerItemPrepareText +#endif } required init?(coder aDecoder: NSCoder) { @@ -216,10 +222,39 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH removePlayerLayer() _playerObserver.player = nil _playerObserver.playerItem = nil - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(0)) / Double(NSEC_PER_SEC), execute: { [weak self] in - guard let self = self else {return} - // perform on next run loop, otherwise other passed react-props may not be set - self.playerItemForSource(withCallback:{ (playerItem:AVPlayerItem!) in + + // perform on next run loop, otherwise other passed react-props may not be set + RCTVideoUtils.delay() + .then{ [weak self] in + guard let self = self else {throw NSError(domain: "", code: 0, userInfo: nil)} + guard let source = self._source, + let assetResult = RCTVideoUtils.prepareAsset(source: source), + let asset = assetResult.asset, + let assetOptions = assetResult.assetOptions else { + DebugLog("Could not find video URL in source '\(self._source)'") + throw NSError(domain: "", code: 0, userInfo: nil) + } + +#if canImport(RCTVideoCache) + if self._videoCache.shouldCache(source:source, textTracks:self._textTracks) { + return self._videoCache.playerItemForSourceUsingCache(uri: source.uri, assetOptions:assetOptions) + } +#endif + + if self._drm != nil || self._localSourceEncryptionKeyScheme != nil { + self._resouceLoaderDelegate = RCTResourceLoaderDelegate( + asset: asset, + drm: self._drm, + localSourceEncryptionKeyScheme: self._localSourceEncryptionKeyScheme, + onVideoError: self.onVideoError, + onGetLicense: self.onGetLicense, + reactTag: self.reactTag + ) + } + return Promise{self.playerItemPrepareText(asset: asset, assetOptions:assetOptions)} + }.then{[weak self] (playerItem:AVPlayerItem!) in + guard let self = self else {throw NSError(domain: "", code: 0, userInfo: nil)} + self._player?.pause() self._playerItem = playerItem self._playerObserver.playerItem = self._playerItem @@ -233,7 +268,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH self._playerObserver.player = self._player self._player?.actionAtItemEnd = .none - + if #available(iOS 10.0, *) { self.setAutomaticallyWaitsToMinimizeStalling(self._automaticallyWaitsToMinimizeStalling) } @@ -248,122 +283,38 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH "drm": self._drm?.json ?? NSNull(), "target": self.reactTag ]) - - }) - }) + }.catch{_ in } _videoLoadStarted = true } @objc - func setDrm(_ drm:NSDictionary!) { + func setDrm(_ drm:NSDictionary) { _drm = DRMParams(drm) } - func playerItemPrepareText(asset:AVAsset!, assetOptions:NSDictionary?, withCallback handler:(AVPlayerItem?)->Void) { + @objc + func setLocalSourceEncryptionKeyScheme(_ keyScheme:String) { + _localSourceEncryptionKeyScheme = keyScheme + } + + func playerItemPrepareText(asset:AVAsset!, assetOptions:NSDictionary?) -> AVPlayerItem { if (_textTracks == nil) || _textTracks?.count==0 { - handler(AVPlayerItem(asset: asset)) - return + return AVPlayerItem(asset: asset) } // AVPlayer can't airplay AVMutableCompositions _allowsExternalPlayback = false - - // sideload text tracks - let mixComposition:AVMutableComposition! = AVMutableComposition() - - let videoAsset:AVAssetTrack! = asset.tracks(withMediaType: AVMediaType.video).first - let videoCompTrack:AVMutableCompositionTrack! = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID:kCMPersistentTrackID_Invalid) - do { - try videoCompTrack.insertTimeRange( - CMTimeRangeMake(start: .zero, duration: videoAsset.timeRange.duration), - of: videoAsset, - at: .zero) - } catch { - } - - let audioAsset:AVAssetTrack! = asset.tracks(withMediaType: AVMediaType.audio).first - let audioCompTrack:AVMutableCompositionTrack! = mixComposition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID:kCMPersistentTrackID_Invalid) - do { - try audioCompTrack.insertTimeRange( - CMTimeRangeMake(start: .zero, duration: videoAsset.timeRange.duration), - of: audioAsset, - at: .zero) - } catch { - } - - var validTextTracks:[TextTrack] = [] - if let textTracks = _textTracks, let textTrackCount = _textTracks?.count { - for i in 0..Void) { - var asset:AVURLAsset! - guard let source = _source, source.uri != nil && source.uri != "" else { - DebugLog("Could not find video URL in source '\(_source)'") - return - } - - let bundlePath = Bundle.main.path(forResource: source.uri, ofType: source.type) ?? "" - let url = source.isNetwork || source.isAsset - ? URL(string: source.uri ?? "") - : URL(fileURLWithPath: bundlePath) - - let assetOptions:NSMutableDictionary! = NSMutableDictionary() - - if url != nil && source.isNetwork { - if let headers = source.requestHeaders, headers.count > 0 { - assetOptions.setObject(headers, forKey:"AVURLAssetHTTPHeaderFieldsKey" as NSCopying) - } - let cookies:[AnyObject]! = HTTPCookieStorage.shared.cookies - assetOptions.setObject(cookies, forKey:AVURLAssetHTTPCookiesKey as NSCopying) -#if canImport(RCTVideoCache) - if _videoCache.playerItemForSourceUsingCache(shouldCache:shouldCache, textTracks:_textTracks, uri:uri, assetOptions:assetOptions, handler:handler) { - return - } -#endif - - asset = AVURLAsset(url: url!, options:assetOptions as! [String : Any]) - } else { - asset = AVURLAsset(url: url!) - } - - if _drm != nil { - _resouceLoaderDelegate = RCTResourceLoaderDelegate( - asset: asset, - drm: _drm, - onVideoError: onVideoError, - onGetLicense: onGetLicense, - reactTag: reactTag - ) - } - - self.playerItemPrepareText(asset: asset, assetOptions:assetOptions, withCallback:handler) + return AVPlayerItem(asset: mixComposition) } // MARK: - Prop setters @@ -415,13 +366,13 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } @objc - func setIgnoreSilentSwitch(_ ignoreSilentSwitch:String!) { + func setIgnoreSilentSwitch(_ ignoreSilentSwitch:String?) { _ignoreSilentSwitch = ignoreSilentSwitch self.applyModifiers() } @objc - func setMixWithOthers(_ mixWithOthers:String!) { + func setMixWithOthers(_ mixWithOthers:String?) { _mixWithOthers = mixWithOthers self.applyModifiers() } @@ -479,7 +430,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH @objc func setCurrentTime(_ currentTime:Float) { - let info:NSDictionary! = [ + let info:NSDictionary = [ "time": NSNumber(value: currentTime), "tolerance": NSNumber(value: 100) ] @@ -490,37 +441,31 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH func setSeek(_ info:NSDictionary!) { let seekTime:NSNumber! = info["time"] as! NSNumber let seekTolerance:NSNumber! = info["tolerance"] as! NSNumber - - let timeScale:Int = 1000 - - let item:AVPlayerItem! = _player?.currentItem - guard item != nil && item.status == AVPlayerItem.Status.readyToPlay else { + let item:AVPlayerItem? = _player?.currentItem + guard item != nil, let player = _player, let item = item, item.status == AVPlayerItem.Status.readyToPlay else { _pendingSeek = true _pendingSeekTime = seekTime.floatValue return } + let wasPaused = _paused - // TODO check loadedTimeRanges - let cmSeekTime:CMTime = CMTimeMakeWithSeconds(Float64(seekTime.floatValue), preferredTimescale: Int32(timeScale)) - let current:CMTime = item.currentTime() - // TODO figure out a good tolerance level - let tolerance:CMTime = CMTimeMake(value: Int64(seekTolerance.floatValue), timescale: Int32(timeScale)) - let wasPaused:Bool = _paused - - guard CMTimeCompare(current, cmSeekTime) != 0 else { return } - if !wasPaused { _player?.pause() } - - _player?.seek(to: cmSeekTime, toleranceBefore:tolerance, toleranceAfter:tolerance, completionHandler:{ [weak self] (finished:Bool) in - guard let self = self else { return } - - self._playerObserver.addTimeObserverIfNotSet() - if !wasPaused { - self.setPaused(false) - } - self.onVideoSeek?(["currentTime": NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))), - "seekTime": seekTime, - "target": self.reactTag]) - }) + RCTPlayerOperations.seek( + player:player, + playerItem:item, + paused:wasPaused, + seekTime:seekTime.floatValue, + seekTolerance:seekTolerance.floatValue) + .then{ [weak self] (finished:Bool) in + guard let self = self else { return } + + self._playerObserver.addTimeObserverIfNotSet() + if !wasPaused { + self.setPaused(false) + } + self.onVideoSeek?(["currentTime": NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))), + "seekTime": seekTime, + "target": self.reactTag]) + }.catch{_ in } _pendingSeek = false } @@ -605,46 +550,46 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _repeat = `repeat` } - + @objc - func setSelectedAudioTrack(_ selectedAudioTrack:NSDictionary!) { + func setSelectedAudioTrack(_ selectedAudioTrack:NSDictionary?) { setSelectedAudioTrack(SelectedTrackCriteria(selectedAudioTrack)) } - func setSelectedAudioTrack(_ selectedAudioTrack:SelectedTrackCriteria!) { + func setSelectedAudioTrack(_ selectedAudioTrack:SelectedTrackCriteria?) { _selectedAudioTrackCriteria = selectedAudioTrack RCTPlayerOperations.setMediaSelectionTrackForCharacteristic(player:_player, characteristic: AVMediaCharacteristic.audible, - criteria:_selectedAudioTrackCriteria) + criteria:_selectedAudioTrackCriteria) } @objc - func setSelectedTextTrack(_ selectedTextTrack:NSDictionary!) { + func setSelectedTextTrack(_ selectedTextTrack:NSDictionary?) { setSelectedTextTrack(SelectedTrackCriteria(selectedTextTrack)) } - func setSelectedTextTrack(_ selectedTextTrack:SelectedTrackCriteria!) { + func setSelectedTextTrack(_ selectedTextTrack:SelectedTrackCriteria?) { _selectedTextTrackCriteria = selectedTextTrack if (_textTracks != nil) { // sideloaded text tracks RCTPlayerOperations.setSideloadedText(player:_player, textTracks:_textTracks, criteria:_selectedTextTrackCriteria) } else { // text tracks included in the HLS playlist RCTPlayerOperations.setMediaSelectionTrackForCharacteristic(player:_player, characteristic: AVMediaCharacteristic.legible, - criteria:_selectedTextTrackCriteria) + criteria:_selectedTextTrackCriteria) } } @objc - func setTextTracks(_ textTracks:[NSDictionary]!) { - setTextTracks(textTracks.map { TextTrack($0) }) + func setTextTracks(_ textTracks:[NSDictionary]?) { + setTextTracks(textTracks?.map { TextTrack($0) }) } - func setTextTracks(_ textTracks:[TextTrack]!) { + func setTextTracks(_ textTracks:[TextTrack]?) { _textTracks = textTracks // in case textTracks was set after selectedTextTrack if (_selectedTextTrackCriteria != nil) {setSelectedTextTrack(_selectedTextTrackCriteria)} } - + @objc func setFullscreen(_ fullscreen:Bool) { if fullscreen && !_fullscreenPlayerPresented && _player != nil { @@ -697,7 +642,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } @objc - func setFullscreenOrientation(_ orientation:String!) { + func setFullscreenOrientation(_ orientation:String?) { _fullscreenOrientation = orientation if _fullscreenPlayerPresented { _playerViewController?.preferredOrientation = orientation @@ -705,18 +650,17 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } func usePlayerViewController() { - guard _player != nil else { return } + guard let _player = _player, let _playerItem = _playerItem else { return } if _playerViewController == nil { - _playerViewController = createPlayerViewController(player: _player, withPlayerItem:_playerItem) - + _playerViewController = createPlayerViewController(player:_player, withPlayerItem:_playerItem) } // to prevent video from being animated when resizeMode is 'cover' // resize mode must be set before subview is added setResizeMode(_resizeMode) guard let _playerViewController = _playerViewController else { return } - + if _controls { let viewController:UIViewController! = self.reactViewController() viewController.addChild(_playerViewController) @@ -726,8 +670,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _playerObserver.playerViewController = _playerViewController } - func createPlayerViewController(player:AVPlayer!, withPlayerItem playerItem:AVPlayerItem!) -> RCTVideoPlayerViewController! { - let viewController:RCTVideoPlayerViewController! = RCTVideoPlayerViewController() + func createPlayerViewController(player:AVPlayer, withPlayerItem playerItem:AVPlayerItem) -> RCTVideoPlayerViewController { + let viewController = RCTVideoPlayerViewController() viewController.showsPlaybackControls = true viewController.rctDelegate = self viewController.preferredOrientation = _fullscreenOrientation @@ -953,20 +897,20 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH return } - var metadata: [[String:String?]?] = [] - for item in _items { - let value = item.value as? String - let identifier = item.identifier?.rawValue + var metadata: [[String:String?]?] = [] + for item in _items { + let value = item.value as? String + let identifier = item.identifier?.rawValue - if let value = value { - metadata.append(["value":value, "identifier":identifier]) - } - } + if let value = value { + metadata.append(["value":value, "identifier":identifier]) + } + } - onTimedMetadata?([ - "target": reactTag, - "metadata": metadata - ]) + onTimedMetadata?([ + "target": reactTag, + "metadata": metadata + ]) } // Handle player item status change. @@ -1134,14 +1078,14 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } //unused -// @objc func handleAVPlayerAccess(notification:NSNotification!) { -// let accessLog:AVPlayerItemAccessLog! = (notification.object as! AVPlayerItem).accessLog() -// let lastEvent:AVPlayerItemAccessLogEvent! = accessLog.events.last -// -// /* TODO: get this working -// if (self.onBandwidthUpdate) { -// self.onBandwidthUpdate(@{@"bitrate": [NSNumber numberWithFloat:lastEvent.observedBitrate]}); -// } -// */ -// } + // @objc func handleAVPlayerAccess(notification:NSNotification!) { + // let accessLog:AVPlayerItemAccessLog! = (notification.object as! AVPlayerItem).accessLog() + // let lastEvent:AVPlayerItemAccessLogEvent! = accessLog.events.last + // + // /* TODO: get this working + // if (self.onBandwidthUpdate) { + // self.onBandwidthUpdate(@{@"bitrate": [NSNumber numberWithFloat:lastEvent.observedBitrate]}); + // } + // */ + // } } diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index ece0a1e3..9979b440 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -34,6 +34,7 @@ RCT_EXPORT_VIEW_PROPERTY(filter, NSString); RCT_EXPORT_VIEW_PROPERTY(filterEnabled, BOOL); RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float); RCT_EXPORT_VIEW_PROPERTY(restoreUserInterfaceForPIPStopCompletionHandler, BOOL); +RCT_EXPORT_VIEW_PROPERTY(localSourceEncryptionKeyScheme, NSString); /* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */ RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTDirectEventBlock); diff --git a/ios/VideoCaching/RCTVideoCachingHandler.swift b/ios/VideoCaching/RCTVideoCachingHandler.swift index 8e37f11a..5f6ffb77 100644 --- a/ios/VideoCaching/RCTVideoCachingHandler.swift +++ b/ios/VideoCaching/RCTVideoCachingHandler.swift @@ -1,52 +1,50 @@ import Foundation import AVFoundation import DVAssetLoaderDelegate +import Promises class RCTVideoCachingHandler: NSObject, DVAssetLoaderDelegatesDelegate { private var _videoCache:RCTVideoCache! = RCTVideoCache.sharedInstance() - private var _playerItemPrepareText: (AVAsset?, NSDictionary?, (AVPlayerItem?)->Void) -> Void + var playerItemPrepareText: ((AVAsset?, NSDictionary?) -> AVPlayerItem)? - init(_ playerItemPrepareText: @escaping (AVAsset?, NSDictionary?, (AVPlayerItem?)->Void) -> Void) { - _playerItemPrepareText = playerItemPrepareText + override init() { + super.init() } - func playerItemForSourceUsingCache(shouldCache:Bool, textTracks:[AnyObject]?, uri:String, assetOptions:NSMutableDictionary, handler:@escaping (AVPlayerItem?)->Void) -> Bool { - if shouldCache && ((textTracks == nil) || (textTracks!.count == 0)) { + func shouldCache(source: VideoSource, textTracks:[TextTrack]?) -> Bool { + if source.isNetwork && source.shouldCache && ((textTracks == nil) || (textTracks!.count == 0)) { /* The DVURLAsset created by cache doesn't have a tracksWithMediaType property, so trying * to bring in the text track code will crash. I suspect this is because the asset hasn't fully loaded. * Until this is fixed, we need to bypass caching when text tracks are specified. */ - DebugLog("Caching is not supported for uri '\(uri)' because text tracks are not compatible with the cache. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md") - playerItemForSourceUsingCache(uri: uri, assetOptions:assetOptions, withCallback:handler) + DebugLog("Caching is not supported for uri '\(source.uri)' because text tracks are not compatible with the cache. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md") return true } return false } - func playerItemForSourceUsingCache(uri:String!, assetOptions options:NSDictionary!, withCallback handler: @escaping (AVPlayerItem?)->Void) { + func playerItemForSourceUsingCache(uri:String!, assetOptions options:NSDictionary!) -> Promise { let url = URL(string: uri) - _videoCache.getItemForUri(uri, withCallback:{ [weak self] (videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?) in - guard let self = self else { return } + return getItemForUri(uri) + .then{ [weak self] (videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?) -> AVPlayerItem in + guard let self = self, let playerItemPrepareText = self.playerItemPrepareText else {throw NSError(domain: "", code: 0, userInfo: nil)} switch (videoCacheStatus) { case .missingFileExtension: DebugLog("Could not generate cache key for uri '\(uri)'. It is currently not supported to cache urls that do not include a file extension. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md") let asset:AVURLAsset! = AVURLAsset(url: url!, options:options as! [String : Any]) - self._playerItemPrepareText(asset, options, handler) - return + return playerItemPrepareText(asset, options) case .unsupportedFileExtension: DebugLog("Could not generate cache key for uri '\(uri)'. The file extension of that uri is currently not supported. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md") let asset:AVURLAsset! = AVURLAsset(url: url!, options:options as! [String : Any]) - self._playerItemPrepareText(asset, options, handler) - return + return playerItemPrepareText(asset, options) default: if let cachedAsset = cachedAsset { DebugLog("Playing back uri '\(uri)' from cache") // See note in playerItemForSource about not being able to support text tracks & caching - handler(AVPlayerItem(asset: cachedAsset)) - return + return AVPlayerItem(asset: cachedAsset) } } @@ -65,8 +63,16 @@ class RCTVideoCachingHandler: NSObject, DVAssetLoaderDelegatesDelegate { asset?.resourceLoader.setDelegate(resourceLoaderDelegate, queue: DispatchQueue.main) */ - handler(AVPlayerItem(asset: asset)) - }) + return AVPlayerItem(asset: asset) + } + } + + func getItemForUri(_ uri:String) -> Promise<(videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?)> { + return Promise<(videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?)> { fulfill, reject in + self._videoCache.getItemForUri(uri, withCallback:{ (videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?) in + fulfill((videoCacheStatus, cachedAsset)) + }) + } } // MARK: - DVAssetLoaderDelegate diff --git a/react-native-video.podspec b/react-native-video.podspec index 5c3f5e5d..e33dcf9d 100644 --- a/react-native-video.podspec +++ b/react-native-video.podspec @@ -12,11 +12,12 @@ Pod::Spec.new do |s| s.homepage = 'https://github.com/react-native-community/react-native-video' s.source = { :git => "https://github.com/react-native-community/react-native-video.git", :tag => "#{s.version}" } - s.ios.deployment_target = "8.0" + s.ios.deployment_target = "9.0" s.tvos.deployment_target = "9.0" s.subspec "Video" do |ss| ss.source_files = "ios/Video/**/*.{h,m,swift}" + ss.dependency "PromisesSwift" end s.subspec "VideoCaching" do |ss| From 0ab7ff476b947a3552a8901123d5137c17bf31ba Mon Sep 17 00:00:00 2001 From: Gabriel Rivero Date: Wed, 3 Nov 2021 19:14:19 -0400 Subject: [PATCH 14/76] VEX-6030-reduce-buffer-size-based-on-heap --- .../com/brentvatne/exoplayer/ReactExoplayerView.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 24dda940..6ce67812 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -2,6 +2,7 @@ package com.brentvatne.exoplayer; import android.annotation.SuppressLint; import android.app.Activity; +import android.app.ActivityManager; import android.content.Context; import android.media.AudioManager; import android.net.Uri; @@ -198,7 +199,7 @@ class ReactExoplayerView extends FrameLayout implements public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) { Timeline.Window window = new Timeline.Window(); - if(!player.getCurrentTimeline().isEmpty()) { + if(!player.getCurrentTimeline().isEmpty()) { player.getCurrentTimeline().getWindow(player.getCurrentWindowIndex(), window); } return window.windowStartTimeMs + currentPosition; @@ -401,6 +402,7 @@ class ReactExoplayerView extends FrameLayout implements } private class RNVLoadControl extends DefaultLoadControl { + private int availableHeapInBytes = 0; public RNVLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs, int bufferForPlaybackMs, int bufferForPlaybackAfterRebufferMs, int targetBufferBytes, boolean prioritizeTimeOverSizeThresholds, int backBufferDurationMs, boolean retainBackBufferFromKeyframe) { super(allocator, minBufferMs, @@ -411,10 +413,18 @@ class ReactExoplayerView extends FrameLayout implements prioritizeTimeOverSizeThresholds, backBufferDurationMs, retainBackBufferFromKeyframe); + if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.N) { + ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(themedReactContext.ACTIVITY_SERVICE); + availableHeapInBytes = activityManager.getMemoryClass() / 2 * 1024 * 1024; + } } @Override public boolean shouldContinueLoading(long playbackPositionUs, long bufferedDurationUs, float playbackSpeed) { + int loadedBytes = getAllocator().getTotalBytesAllocated(); + if (availableHeapInBytes > 0 && loadedBytes >= availableHeapInBytes) { + return false; + } if (ReactExoplayerView.this.disableBuffering) { return false; } From 40e450b0b1e2026e66f2accf5d3fc034de4aaead Mon Sep 17 00:00:00 2001 From: Gabriel Rivero Date: Wed, 3 Nov 2021 19:35:27 -0400 Subject: [PATCH 15/76] VEX-6030-reduce-buffer-size-based-on-heap --- .../main/java/com/brentvatne/exoplayer/ReactExoplayerView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 6ce67812..981bca22 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -413,7 +413,7 @@ class ReactExoplayerView extends FrameLayout implements prioritizeTimeOverSizeThresholds, backBufferDurationMs, retainBackBufferFromKeyframe); - if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.N) { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.N) { ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(themedReactContext.ACTIVITY_SERVICE); availableHeapInBytes = activityManager.getMemoryClass() / 2 * 1024 * 1024; } From 7dbc5eb078f530168394c68731c1185fb334982a Mon Sep 17 00:00:00 2001 From: Gabriel Rivero Date: Thu, 4 Nov 2021 13:54:43 -0400 Subject: [PATCH 16/76] add config parameter maxHeapAllocationPercent --- README.md | 1 + Video.js | 1 + .../exoplayer/ReactExoplayerView.java | 19 +++++++++++-------- .../exoplayer/ReactExoplayerViewManager.java | 6 +++++- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ca6745d3..564423f8 100644 --- a/README.md +++ b/README.md @@ -385,6 +385,7 @@ minBufferMs | number | The default minimum duration of media that the player wil maxBufferMs | number | The default maximum duration of media that the player will attempt to buffer, in milliseconds. bufferForPlaybackMs | number | The default duration of media that must be buffered for playback to start or resume following a user action such as a seek, in milliseconds. bufferForPlaybackAfterRebufferMs | number | The default duration of media that must be buffered for playback to resume after a rebuffer, in milliseconds. A rebuffer is defined to be caused by buffer depletion rather than a user action. +maxHeapAllocationPercent | number | The percentage of available heap that the video can use to buffer, between 0 and 1 This prop should only be set when you are setting the source, changing it after the media is loaded will cause it to be reloaded. diff --git a/Video.js b/Video.js index 62a179a4..fcad8437 100644 --- a/Video.js +++ b/Video.js @@ -468,6 +468,7 @@ Video.propTypes = { maxBufferMs: PropTypes.number, bufferForPlaybackMs: PropTypes.number, bufferForPlaybackAfterRebufferMs: PropTypes.number, + maxHeapAllocationPercent: PropTypes.number, }), stereoPan: PropTypes.number, rate: PropTypes.number, diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 981bca22..37c9657e 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -98,6 +98,8 @@ class ReactExoplayerView extends FrameLayout implements MetadataOutput, DrmSessionEventListener { + public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1; + private static final String TAG = "ReactExoplayerView"; private static final CookieManager DEFAULT_COOKIE_MANAGER; @@ -142,6 +144,7 @@ class ReactExoplayerView extends FrameLayout implements private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS; private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS; private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS; + private double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT; private Handler mainHandler; private Timer bufferCheckTimer; @@ -413,19 +416,18 @@ class ReactExoplayerView extends FrameLayout implements prioritizeTimeOverSizeThresholds, backBufferDurationMs, retainBackBufferFromKeyframe); - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.N) { - ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(themedReactContext.ACTIVITY_SERVICE); - availableHeapInBytes = activityManager.getMemoryClass() / 2 * 1024 * 1024; - } + ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(themedReactContext.ACTIVITY_SERVICE); + availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeapAllocationPercent * 1024 * 1024); } @Override public boolean shouldContinueLoading(long playbackPositionUs, long bufferedDurationUs, float playbackSpeed) { - int loadedBytes = getAllocator().getTotalBytesAllocated(); - if (availableHeapInBytes > 0 && loadedBytes >= availableHeapInBytes) { + if (ReactExoplayerView.this.disableBuffering) { return false; } - if (ReactExoplayerView.this.disableBuffering) { + int loadedBytes = getAllocator().getTotalBytesAllocated(); + boolean isHeapReached = availableHeapInBytes > 0 && loadedBytes >= availableHeapInBytes; + if (isHeapReached) { return false; } return super.shouldContinueLoading(playbackPositionUs, bufferedDurationUs, playbackSpeed); @@ -1528,11 +1530,12 @@ class ReactExoplayerView extends FrameLayout implements exoPlayerView.setHideShutterView(hideShutterView); } - public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs) { + public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs, double newMaxHeapAllocationPercent) { minBufferMs = newMinBufferMs; maxBufferMs = newMaxBufferMs; bufferForPlaybackMs = newBufferForPlaybackMs; bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs; + maxHeapAllocationPercent = newMaxHeapAllocationPercent; releasePlayer(); initializePlayer(); } diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index fc9a1354..3bffbc37 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -55,6 +55,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager Date: Tue, 9 Nov 2021 14:22:32 +0200 Subject: [PATCH 17/76] VEX-6011: Align AVOD resolutions with available resolutions on Content (#14) Add support for content tracks and improve track selection to work even during ads playback. --- Video.js | 1 + .../exoplayer/ReactExoplayerView.java | 142 ++++++++++++++++++ .../exoplayer/ReactExoplayerViewManager.java | 6 + 3 files changed, 149 insertions(+) diff --git a/Video.js b/Video.js index 62a179a4..fdfa141e 100644 --- a/Video.js +++ b/Video.js @@ -477,6 +477,7 @@ Video.propTypes = { playWhenInactive: PropTypes.bool, ignoreSilentSwitch: PropTypes.oneOf(['ignore', 'obey']), reportBandwidth: PropTypes.bool, + contentStartTime: PropTypes.number, disableFocus: PropTypes.bool, disableBuffering: PropTypes.bool, controls: PropTypes.bool, diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 24dda940..7873d95d 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -76,6 +76,13 @@ import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; +import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.source.dash.DashUtil; +import com.google.android.exoplayer2.source.dash.manifest.DashManifest; +import com.google.android.exoplayer2.source.dash.manifest.Period; +import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet; +import com.google.android.exoplayer2.source.dash.manifest.Representation; +import com.google.android.exoplayer2.source.dash.manifest.Descriptor; import java.net.CookieHandler; import java.net.CookieManager; @@ -86,6 +93,13 @@ import java.util.UUID; import java.util.Map; import java.util.Timer; import java.util.TimerTask; +import java.util.List; +import java.lang.Thread; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.lang.Integer; @SuppressLint("ViewConstructor") class ReactExoplayerView extends FrameLayout implements @@ -136,6 +150,8 @@ class ReactExoplayerView extends FrameLayout implements private int maxBitRate = 0; private long seekTime = C.TIME_UNSET; private boolean hasDrmFailed = false; + private boolean isUsingContentResolution = false; + private boolean selectTrackWhenReady = false; private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS; private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS; @@ -159,6 +175,7 @@ class ReactExoplayerView extends FrameLayout implements private ReadableArray textTracks; private boolean disableFocus; private boolean disableBuffering; + private long contentStartTime; private boolean disableDisconnectError; private boolean preventsDisplaySleepDuringVideoPlayback = true; private float mProgressUpdateInterval = 250.0f; @@ -850,6 +867,10 @@ class ReactExoplayerView extends FrameLayout implements onBuffering(false); startProgressHandler(); videoLoaded(); + if (selectTrackWhenReady && isUsingContentResolution) { + selectTrackWhenReady = false; + setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue); + } // Setting the visibility for the playerControlView if (playerControlView != null) { playerControlView.show(); @@ -921,6 +942,13 @@ class ReactExoplayerView extends FrameLayout implements return audioTracks; } private WritableArray getVideoTrackInfo() { + + WritableArray contentVideoTracks = this.getVideoTrackInfoFromManifest(); + if (contentVideoTracks != null) { + isUsingContentResolution = true; + return contentVideoTracks; + } + WritableArray videoTracks = Arguments.createArray(); MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); @@ -947,9 +975,73 @@ class ReactExoplayerView extends FrameLayout implements } } } + return videoTracks; } + private WritableArray getVideoTrackInfoFromManifest() { + ExecutorService es = Executors.newSingleThreadExecutor(); + final DataSource dataSource = this.mediaDataSourceFactory.createDataSource(); + final Uri sourceUri = this.srcUri; + final Timeline timelineRef = this.player.getCurrentTimeline(); + final long startTime = this.contentStartTime * 1000 - 100; // s -> ms with 100ms offset + + Future result = es.submit(new Callable() { + DataSource ds = dataSource; + Uri uri = sourceUri; + Timeline timeline = timelineRef; + long startTimeUs = startTime * 1000; // ms -> us + + public WritableArray call() throws Exception { + WritableArray videoTracks = Arguments.createArray(); + try { + DashManifest manifest = DashUtil.loadManifest(this.ds, this.uri); + int periodCount = manifest.getPeriodCount(); + for (int i = 0; i < periodCount; i++) { + Period period = manifest.getPeriod(i); + for (int adaptationIndex = 0; adaptationIndex < period.adaptationSets.size(); adaptationIndex++) { + AdaptationSet adaptation = period.adaptationSets.get(adaptationIndex); + if (adaptation.type != C.TRACK_TYPE_VIDEO) { + continue; + } + boolean hasFoundContentPeriod = false; + for (int representationIndex = 0; representationIndex < adaptation.representations.size(); representationIndex++) { + Representation representation = adaptation.representations.get(representationIndex); + Format format = representation.format; + if (representation.presentationTimeOffsetUs <= startTimeUs) { + break; + } + hasFoundContentPeriod = true; + WritableMap videoTrack = Arguments.createMap(); + videoTrack.putInt("width", format.width == Format.NO_VALUE ? 0 : format.width); + videoTrack.putInt("height",format.height == Format.NO_VALUE ? 0 : format.height); + videoTrack.putInt("bitrate", format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); + videoTrack.putString("codecs", format.codecs != null ? format.codecs : ""); + videoTrack.putString("trackId", + format.id == null ? String.valueOf(representationIndex) : format.id); + if (isFormatSupported(format)) { + videoTracks.pushMap(videoTrack); + } + } + if (hasFoundContentPeriod) { + return videoTracks; + } + } + } + } catch (Exception e) {} + return null; + } + }); + + try { + WritableArray results = result.get(); + es.shutdown(); + return results; + } catch (Exception e) {} + + return null; + } + private WritableArray getTextTrackInfo() { WritableArray textTracks = Arguments.createArray(); @@ -993,6 +1085,11 @@ class ReactExoplayerView extends FrameLayout implements // which they seeked. updateResumePosition(); } + if (isUsingContentResolution) { + // Discontinuity events might have a different track list so we update the selected track + setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue); + selectTrackWhenReady = true; + } // When repeat is turned on, reaching the end of the video will not cause a state change // so we need to explicitly detect it. if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION @@ -1010,6 +1107,10 @@ class ReactExoplayerView extends FrameLayout implements public void onSeekProcessed() { eventEmitter.seek(player.getCurrentPosition(), seekTime); seekTime = C.TIME_UNSET; + if (isUsingContentResolution) { + // We need to update the selected track to make sure that it still matches user selection if track list has changed in this period + setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue); + } } @Override @@ -1278,14 +1379,51 @@ class ReactExoplayerView extends FrameLayout implements int height = value.asInt(); for (int i = 0; i < groups.length; ++i) { // Search for the exact height TrackGroup group = groups.get(i); + Format closestFormat = null; + int closestTrackIndex = -1; + boolean usingExactMatch = false; for (int j = 0; j < group.length; j++) { Format format = group.getFormat(j); if (format.height == height) { groupIndex = i; tracks[0] = j; + closestFormat = null; + closestTrackIndex = -1; + usingExactMatch = true; break; + } else if (isUsingContentResolution) { + // When using content resolution rather than ads, we need to try and find the closest match if there is no exact match + if (closestFormat != null) { + if ((format.bitrate > closestFormat.bitrate || format.height > closestFormat.height) && format.height < height) { + // Higher quality match + closestFormat = format; + closestTrackIndex = j; + } + } else if(format.height < height) { + closestFormat = format; + closestTrackIndex = j; + } } } + // This is a fallback if the new period contains only higher resolutions than the user has selected + if (closestFormat == null && isUsingContentResolution && !usingExactMatch) { + // No close match found - so we pick the lowest quality + int minHeight = Integer.MAX_VALUE; + for (int j = 0; j < group.length; j++) { + Format format = group.getFormat(j); + if (format.height < minHeight) { + minHeight = format.height; + groupIndex = i; + tracks[0] = j; + } + } + } + // Selecting the closest match found + if (closestFormat != null && closestTrackIndex != -1) { + // We found the closest match instead of an exact one + groupIndex = i; + tracks[0] = closestTrackIndex; + } } } else if (rendererIndex == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18) { // Text default // Use system settings if possible @@ -1468,6 +1606,10 @@ class ReactExoplayerView extends FrameLayout implements this.backBufferDurationMs = backBufferDurationMs; } + public void setContentStartTime(int contentStartTime) { + this.contentStartTime = (long)contentStartTime; + } + public void setDisableBuffering(boolean disableBuffering) { this.disableBuffering = disableBuffering; } diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index fc9a1354..0e7a8c66 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -63,6 +63,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager Date: Tue, 16 Nov 2021 20:35:30 -0400 Subject: [PATCH 18/76] force garbage collector when free memory reaches 0 --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index ea27b947..8843702e 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -423,6 +423,7 @@ class ReactExoplayerView extends FrameLayout implements private class RNVLoadControl extends DefaultLoadControl { private int availableHeapInBytes = 0; + private Runtime runtime; public RNVLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs, int bufferForPlaybackMs, int bufferForPlaybackAfterRebufferMs, int targetBufferBytes, boolean prioritizeTimeOverSizeThresholds, int backBufferDurationMs, boolean retainBackBufferFromKeyframe) { super(allocator, minBufferMs, @@ -433,6 +434,7 @@ class ReactExoplayerView extends FrameLayout implements prioritizeTimeOverSizeThresholds, backBufferDurationMs, retainBackBufferFromKeyframe); + runtime = Runtime.getRuntime(); ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(themedReactContext.ACTIVITY_SERVICE); availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeapAllocationPercent * 1024 * 1024); } @@ -447,6 +449,11 @@ class ReactExoplayerView extends FrameLayout implements if (isHeapReached) { return false; } + if (runtime.freeMemory() == 0) { + Log.w("ExoPlayer Warning", "free memory reached 0, forcing garbage collection"); + runtime.gc(); + return false; + } return super.shouldContinueLoading(playbackPositionUs, bufferedDurationUs, playbackSpeed); } } From ab202c96b6f68ca89304dcb3fccd49d526c5df95 Mon Sep 17 00:00:00 2001 From: Gabriel Rivero Date: Tue, 16 Nov 2021 21:40:38 -0400 Subject: [PATCH 19/76] typo --- .../main/java/com/brentvatne/exoplayer/ReactExoplayerView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 8843702e..eb4dfa8d 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -450,7 +450,7 @@ class ReactExoplayerView extends FrameLayout implements return false; } if (runtime.freeMemory() == 0) { - Log.w("ExoPlayer Warning", "free memory reached 0, forcing garbage collection"); + Log.w("ExoPlayer Warning", "Free memory reached 0, forcing garbage collection"); runtime.gc(); return false; } From 2ab029995f91dd3a073f896d2b4065d618d74282 Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Fri, 21 Jan 2022 14:10:22 +0200 Subject: [PATCH 20/76] VEX-6365: Improve memory management (#17) Improve memory management to reduce pressure on low end devices. JIRA: VEX-6365 --- README.md | 2 ++ .../exoplayer/ReactExoplayerView.java | 27 +++++++++++++++++-- .../exoplayer/ReactExoplayerViewManager.java | 11 +++++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 564423f8..c0a34da6 100644 --- a/README.md +++ b/README.md @@ -386,6 +386,8 @@ maxBufferMs | number | The default maximum duration of media that the player wil bufferForPlaybackMs | number | The default duration of media that must be buffered for playback to start or resume following a user action such as a seek, in milliseconds. bufferForPlaybackAfterRebufferMs | number | The default duration of media that must be buffered for playback to resume after a rebuffer, in milliseconds. A rebuffer is defined to be caused by buffer depletion rather than a user action. maxHeapAllocationPercent | number | The percentage of available heap that the video can use to buffer, between 0 and 1 +minBackBufferMemoryReservePercent | number | The percentage of available app memory at which during startup the back buffer will be disabled, between 0 and 1 +minBufferMemoryReservePercent | number | The percentage of available app memory to keep in reserve that prevents buffer from using it, between 0 and 1 This prop should only be set when you are setting the source, changing it after the media is loaded will cause it to be reloaded. diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index eb4dfa8d..5dcb5e0d 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -113,6 +113,8 @@ class ReactExoplayerView extends FrameLayout implements DrmSessionEventListener { public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1; + public static final double DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE = 0; + public static final double DEFAULT_MIN_BUFFER_MEMORY_RESERVE = 0; private static final String TAG = "ReactExoplayerView"; @@ -161,7 +163,8 @@ class ReactExoplayerView extends FrameLayout implements private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS; private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS; private double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT; - + private double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE; + private double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE; private Handler mainHandler; private Timer bufferCheckTimer; @@ -449,6 +452,14 @@ class ReactExoplayerView extends FrameLayout implements if (isHeapReached) { return false; } + long usedMemory = runtime.totalMemory() - runtime.freeMemory(); + long freeMemory = runtime.maxMemory() - usedMemory; + long reserveMemory = (long)minBufferMemoryReservePercent * runtime.maxMemory(); + long bufferedMs = bufferedDurationUs / (long)1000; + if (reserveMemory > freeMemory && bufferedMs > 2000) { + // We don't have enough memory in reserve so we stop buffering to allow other components to use it instead + return false; + } if (runtime.freeMemory() == 0) { Log.w("ExoPlayer Warning", "Free memory reached 0, forcing garbage collection"); runtime.gc(); @@ -1622,6 +1633,16 @@ class ReactExoplayerView extends FrameLayout implements } public void setBackBufferDurationMs(int backBufferDurationMs) { + Runtime runtime = Runtime.getRuntime(); + long usedMemory = runtime.totalMemory() - runtime.freeMemory(); + long freeMemory = runtime.maxMemory() - usedMemory; + long reserveMemory = (long)minBackBufferMemoryReservePercent * runtime.maxMemory(); + if (reserveMemory > freeMemory) { + // We don't have enough memory in reserve so we will + Log.w("ExoPlayer Warning", "Not enough reserve memory, setting back buffer to 0ms to reduce memory pressure!"); + this.backBufferDurationMs = 0; + return; + } this.backBufferDurationMs = backBufferDurationMs; } @@ -1679,12 +1700,14 @@ class ReactExoplayerView extends FrameLayout implements exoPlayerView.setHideShutterView(hideShutterView); } - public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs, double newMaxHeapAllocationPercent) { + public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs, double newMaxHeapAllocationPercent, double newMinBackBufferMemoryReservePercent, double newMinBufferMemoryReservePercent) { minBufferMs = newMinBufferMs; maxBufferMs = newMaxBufferMs; bufferForPlaybackMs = newBufferForPlaybackMs; bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs; maxHeapAllocationPercent = newMaxHeapAllocationPercent; + minBackBufferMemoryReservePercent = newMinBackBufferMemoryReservePercent; + minBufferMemoryReservePercent = newMinBufferMemoryReservePercent; releasePlayer(); initializePlayer(); } diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index a923e050..5d8a84d4 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -56,6 +56,8 @@ public class ReactExoplayerViewManager extends ViewGroupManager Date: Mon, 14 Feb 2022 21:17:22 -0400 Subject: [PATCH 21/76] add useSecureView prop to prevent screen recording --- README.md | 13 +++++++++++++ Video.js | 1 + .../brentvatne/exoplayer/ExoPlayerView.java | 18 +++++++++++++++++- .../exoplayer/ReactExoplayerView.java | 4 ++++ .../exoplayer/ReactExoplayerViewManager.java | 6 ++++++ 5 files changed, 41 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c0a34da6..2881926f 100644 --- a/README.md +++ b/README.md @@ -316,6 +316,7 @@ var styles = StyleSheet.create({ * [textTracks](#texttracks) * [trackId](#trackId) * [useTextureView](#usetextureview) +* [useSecureView](#useSecureView) * [volume](#volume) * [localSourceEncryptionKeyScheme](#localSourceEncryptionKeyScheme) @@ -911,6 +912,18 @@ useTextureView can only be set at same time you're setting the source. Platforms: Android ExoPlayer +#### useSecureView +Force the output to a SurfaceView and enables the secure surface. + +This will override useTextureView flag. + +SurfaceView is is the only one that can be labeled as secure. + +* **true** - Use security +* **false (default)** - Do not use security + +Platforms: Android ExoPlayer + #### volume Adjust the volume. * **1.0 (default)** - Play at full volume diff --git a/Video.js b/Video.js index 0a97be1d..db88752f 100644 --- a/Video.js +++ b/Video.js @@ -488,6 +488,7 @@ Video.propTypes = { fullscreenOrientation: PropTypes.oneOf(['all', 'landscape', 'portrait']), progressUpdateInterval: PropTypes.number, useTextureView: PropTypes.bool, + useSecureView: PropTypes.bool, hideShutterView: PropTypes.bool, onLoadStart: PropTypes.func, onLoad: PropTypes.func, diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java index 61cb0dd6..a91618d5 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java @@ -41,6 +41,7 @@ public final class ExoPlayerView extends FrameLayout { private ViewGroup.LayoutParams layoutParams; private boolean useTextureView = true; + private boolean useSecureView = false; private boolean hideShutterView = false; public ExoPlayerView(Context context) { @@ -103,7 +104,15 @@ public final class ExoPlayerView extends FrameLayout { } private void updateSurfaceView() { - View view = useTextureView ? new TextureView(context) : new SurfaceView(context); + View view; + if (!useTextureView || useSecureView) { + view = new SurfaceView(context); + if (useSecureView) { + ((SurfaceView)view).setSecure(true); + } + } else { + view = new TextureView(context); + } view.setLayoutParams(layoutParams); surfaceView = view; @@ -178,6 +187,13 @@ public final class ExoPlayerView extends FrameLayout { } } + public void useSecureView(boolean useSecureView) { + if (useSecureView != this.useSecureView) { + this.useSecureView = useSecureView; + updateSurfaceView(); + } + } + public void setHideShutterView(boolean hideShutterView) { this.hideShutterView = hideShutterView; updateShutterViewVisibility(); diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 5dcb5e0d..e4000116 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1696,6 +1696,10 @@ class ReactExoplayerView extends FrameLayout implements exoPlayerView.setUseTextureView(finallyUseTextureView); } + public void useSecureView(boolean useSecureView) { + exoPlayerView.useSecureView(useSecureView); + } + public void setHideShutterView(boolean hideShutterView) { exoPlayerView.setHideShutterView(hideShutterView); } diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 5d8a84d4..3744fcae 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -72,6 +72,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager Date: Tue, 29 Mar 2022 15:21:39 +0300 Subject: [PATCH 22/76] Capture more errors from DRM session manager --- .../exoplayer/ReactExoplayerView.java | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index e4000116..3dbc0e20 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -564,6 +564,10 @@ class ReactExoplayerView extends FrameLayout implements return; } } + if (drmSessionManager == null) { + // DRM Session Manager failed to instantiate, no other work can be done - error has been handled by the instantiating method + return; + } // End DRM ArrayList mediaSourceList = buildTextSources(); @@ -608,26 +612,36 @@ class ReactExoplayerView extends FrameLayout implements } - private DrmSessionManager buildDrmSessionManager(UUID uuid, - String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException { + private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray, int retryCount) throws UnsupportedDrmException { if (Util.SDK_INT < 18) { return null; } - HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl, - buildHttpDataSourceFactory(false)); - if (keyRequestPropertiesArray != null) { - for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) { - drmCallback.setKeyRequestProperty(keyRequestPropertiesArray[i], - keyRequestPropertiesArray[i + 1]); + try { + HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl, + buildHttpDataSourceFactory(false)); + if (keyRequestPropertiesArray != null) { + for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) { + drmCallback.setKeyRequestProperty(keyRequestPropertiesArray[i], keyRequestPropertiesArray[i + 1]); + } } + FrameworkMediaDrm mediaDrm = FrameworkMediaDrm.newInstance(uuid); + if (hasDrmFailed) { + // When DRM fails using L1 we want to switch to L3 + mediaDrm.setPropertyString("securityLevel", "L3"); + } + return new DefaultDrmSessionManager(uuid, mediaDrm, drmCallback, null, false, 3); + } catch(UnsupportedDrmException e) { + // Unsupported DRM exceptions are handled by the calling method + throw e; + } catch (Exception e) { + if (retryCount < 3) { + // Attempt retry 3 times in case where the OS Media DRM Framework fails for whatever reason + return buildDrmSessionManager(uuid, licenseUrl, keyRequestPropertiesArray, ++retryCount); + } + // Handle the unknow exception and emit to JS + eventEmitter.error(ex.toString(), ex, "3006"); + return null; } - FrameworkMediaDrm mediaDrm = FrameworkMediaDrm.newInstance(uuid); - if (hasDrmFailed) { - // When DRM fails using L1 we want to switch to L3 - mediaDrm.setPropertyString("securityLevel", "L3"); - } - return new DefaultDrmSessionManager(uuid, - mediaDrm, drmCallback, null, false, 3); } private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) { From 39dee21efab52c5d868abe09c3feb63cd036f3e7 Mon Sep 17 00:00:00 2001 From: Nick Fujita Date: Wed, 30 Mar 2022 18:17:56 +0900 Subject: [PATCH 23/76] Updates disableDisconnectError retry condition to be more specific (#22) Updates disableDisconnectError retry condition to be more specific. Jira: VEX-6518 Reviews Major reviewer (domain expert): @armadilio3 Minor reviewer: @gabriel-rivero --- .../exoplayer/ReactExoplayerLoadErrorHandlingPolicy.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerLoadErrorHandlingPolicy.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerLoadErrorHandlingPolicy.java index c701310f..d0d68de1 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerLoadErrorHandlingPolicy.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerLoadErrorHandlingPolicy.java @@ -16,7 +16,10 @@ public final class ReactExoplayerLoadErrorHandlingPolicy extends DefaultLoadErro @Override public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) { - if (loadErrorInfo.exception instanceof HttpDataSourceException) { + if ( + loadErrorInfo.exception instanceof HttpDataSourceException && + (loadErrorInfo.exception.getMessage() == "Unable to connect" || loadErrorInfo.exception.getMessage() == "Software caused connection abort") + ) { // Capture the error we get when there is no network connectivity and keep retrying it return 1000; // Retry every second } else if(loadErrorInfo.errorCount < this.minLoadRetryCount) { From 88f32ae3c8e8d21a24d67d39042016fdcf77b775 Mon Sep 17 00:00:00 2001 From: Armands Malejevs Date: Wed, 30 Mar 2022 14:29:08 +0300 Subject: [PATCH 24/76] Move DRM init to a separate thread --- .../exoplayer/ReactExoplayerView.java | 127 +++++++++++------- 1 file changed, 79 insertions(+), 48 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 3dbc0e20..13eaca83 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -7,6 +7,7 @@ import android.content.Context; import android.media.AudioManager; import android.net.Uri; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.text.TextUtils; import android.util.Log; @@ -547,59 +548,33 @@ class ReactExoplayerView extends FrameLayout implements PlaybackParameters params = new PlaybackParameters(rate, 1f); player.setPlaybackParameters(params); } + if (playerNeedsSource && srcUri != null) { exoPlayerView.invalidateAspectRatio(); + // DRM session manager creation must be done on a different thread to prevent crashes so we start a new thread + ExecutorService es = Executors.newSingleThreadExecutor(); + es.execute(new Runnable() { + @Override + public void run() { + + // DRM initialization must run on a different thread + initializePlayerDrm(); + + // Initialize handler to run on the main thread + new Handler(Looper.getMainLooper()).post(new Runnable () { + @Override + public void run () { + // Source initialization must run on the main thread + initializePlayerSource(); + } + }); - // DRM - DrmSessionManager drmSessionManager = null; - if (self.drmUUID != null) { - try { - drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl, - self.drmLicenseHeader); - } catch (UnsupportedDrmException e) { - int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported - : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME - ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); - eventEmitter.error(getResources().getString(errorStringId), e, "3003"); - return; } - } - if (drmSessionManager == null) { - // DRM Session Manager failed to instantiate, no other work can be done - error has been handled by the instantiating method - return; - } - // End DRM - - ArrayList mediaSourceList = buildTextSources(); - MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager); - MediaSource mediaSource; - if (mediaSourceList.size() == 0) { - mediaSource = videoSource; - } else { - mediaSourceList.add(0, videoSource); - MediaSource[] textSourceArray = mediaSourceList.toArray( - new MediaSource[mediaSourceList.size()] - ); - mediaSource = new MergingMediaSource(textSourceArray); - } - - boolean haveResumePosition = resumeWindow != C.INDEX_UNSET; - if (haveResumePosition) { - player.seekTo(resumeWindow, resumePosition); - } - player.prepare(mediaSource, !haveResumePosition, false); - playerNeedsSource = false; - - reLayout(exoPlayerView); - eventEmitter.loadStart(); - loadVideoStarted = true; + }); + } else { + finishPlayerInitialization(); } - - // Initializing the playerControlView - initializePlayerControl(); - setControls(controls); - applyModifiers(); - startBufferCheckTimer(); + } catch (Exception ex) { self.playerNeedsSource = true; @@ -612,6 +587,62 @@ class ReactExoplayerView extends FrameLayout implements } + private void initializePlayerDrm() { + DrmSessionManager drmSessionManager = null; + if (self.drmUUID != null) { + try { + drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl, + self.drmLicenseHeader); + } catch (UnsupportedDrmException e) { + int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported + : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME + ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); + eventEmitter.error(getResources().getString(errorStringId), e, "3003"); + return; + } + } + if (drmSessionManager == null) { + // DRM Session Manager failed to instantiate, no other work can be done - error has been handled by the instantiating method + return; + } + } + + private void initializePlayerSource() { + ArrayList mediaSourceList = buildTextSources(); + MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager); + MediaSource mediaSource; + if (mediaSourceList.size() == 0) { + mediaSource = videoSource; + } else { + mediaSourceList.add(0, videoSource); + MediaSource[] textSourceArray = mediaSourceList.toArray( + new MediaSource[mediaSourceList.size()] + ); + mediaSource = new MergingMediaSource(textSourceArray); + } + + boolean haveResumePosition = resumeWindow != C.INDEX_UNSET; + if (haveResumePosition) { + player.seekTo(resumeWindow, resumePosition); + } + player.prepare(mediaSource, !haveResumePosition, false); + playerNeedsSource = false; + + reLayout(exoPlayerView); + eventEmitter.loadStart(); + loadVideoStarted = true; + + finishPlayerInitialization(); + } + + private void finishPlayerInitialization() { + // Initializing the playerControlView + initializePlayerControl(); + setControls(controls); + applyModifiers(); + startBufferCheckTimer(); + } + private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray, int retryCount) throws UnsupportedDrmException { if (Util.SDK_INT < 18) { return null; From 2f77765fe2ce4fbd33540596358cba625cd94b1a Mon Sep 17 00:00:00 2001 From: Armands Malejevs Date: Wed, 30 Mar 2022 14:35:48 +0300 Subject: [PATCH 25/76] Clean up --- .../exoplayer/ReactExoplayerView.java | 85 ++++++++++--------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 13eaca83..98413ef5 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -512,41 +512,8 @@ class ReactExoplayerView extends FrameLayout implements public void run() { try { if (player == null) { - ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); - trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); - trackSelector.setParameters(trackSelector.buildUponParameters() - .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); - - DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); - RNVLoadControl loadControl = new RNVLoadControl( - allocator, - minBufferMs, - maxBufferMs, - bufferForPlaybackMs, - bufferForPlaybackAfterRebufferMs, - -1, - true, - backBufferDurationMs, - DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME - ); - DefaultRenderersFactory renderersFactory = - new DefaultRenderersFactory(getContext()) - .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); - player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) - .setTrackSelector​(trackSelector) - .setBandwidthMeter(bandwidthMeter) - .setLoadControl(loadControl) - .build(); - player.addListener(self); - player.addMetadataOutput(self); - exoPlayerView.setPlayer(player); - audioBecomingNoisyReceiver.setListener(self); - bandwidthMeter.addEventListener(new Handler(), self); - setPlayWhenReady(!isPaused); - playerNeedsSource = true; - - PlaybackParameters params = new PlaybackParameters(rate, 1f); - player.setPlaybackParameters(params); + // Initialize core configuration and listeners + initializePlayerCore(self); } if (playerNeedsSource && srcUri != null) { @@ -558,21 +525,21 @@ class ReactExoplayerView extends FrameLayout implements public void run() { // DRM initialization must run on a different thread - initializePlayerDrm(); + initializePlayerDrm(self); // Initialize handler to run on the main thread new Handler(Looper.getMainLooper()).post(new Runnable () { @Override public void run () { // Source initialization must run on the main thread - initializePlayerSource(); + initializePlayerSource(self); } }); } }); } else { - finishPlayerInitialization(); + initializePlayerSource(self); } @@ -587,7 +554,45 @@ class ReactExoplayerView extends FrameLayout implements } - private void initializePlayerDrm() { + private void initializePlayerCore(ReactExoplayerView self) { + ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); + trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); + trackSelector.setParameters(trackSelector.buildUponParameters() + .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); + + DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); + RNVLoadControl loadControl = new RNVLoadControl( + allocator, + minBufferMs, + maxBufferMs, + bufferForPlaybackMs, + bufferForPlaybackAfterRebufferMs, + -1, + true, + backBufferDurationMs, + DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME + ); + DefaultRenderersFactory renderersFactory = + new DefaultRenderersFactory(getContext()) + .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); + player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) + .setTrackSelector​(trackSelector) + .setBandwidthMeter(bandwidthMeter) + .setLoadControl(loadControl) + .build(); + player.addListener(self); + player.addMetadataOutput(self); + exoPlayerView.setPlayer(player); + audioBecomingNoisyReceiver.setListener(self); + bandwidthMeter.addEventListener(new Handler(), self); + setPlayWhenReady(!isPaused); + playerNeedsSource = true; + + PlaybackParameters params = new PlaybackParameters(rate, 1f); + player.setPlaybackParameters(params); + } + + private void initializePlayerDrm(ReactExoplayerView self) { DrmSessionManager drmSessionManager = null; if (self.drmUUID != null) { try { @@ -607,7 +612,7 @@ class ReactExoplayerView extends FrameLayout implements } } - private void initializePlayerSource() { + private void initializePlayerSource(ReactExoplayerView self) { ArrayList mediaSourceList = buildTextSources(); MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager); MediaSource mediaSource; From 142521bc0ec1e37b92cb2e2a8a8402c41f6aed8e Mon Sep 17 00:00:00 2001 From: Armands Malejevs Date: Wed, 30 Mar 2022 14:43:04 +0300 Subject: [PATCH 26/76] Fix build issues --- .../exoplayer/ReactExoplayerView.java | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 98413ef5..2b0ee236 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -523,23 +523,24 @@ class ReactExoplayerView extends FrameLayout implements es.execute(new Runnable() { @Override public void run() { - // DRM initialization must run on a different thread - initializePlayerDrm(self); - + DRMSessionManager drmSessionManager = initializePlayerDrm(self); + if (drmSessionManager == null) { + // Failed to intialize DRM session manager - cannot continue + return; + } // Initialize handler to run on the main thread new Handler(Looper.getMainLooper()).post(new Runnable () { @Override public void run () { // Source initialization must run on the main thread - initializePlayerSource(self); + initializePlayerSource(self, drmSessionManager); } }); - } }); } else { - initializePlayerSource(self); + initializePlayerSource(self, null); } @@ -592,27 +593,24 @@ class ReactExoplayerView extends FrameLayout implements player.setPlaybackParameters(params); } - private void initializePlayerDrm(ReactExoplayerView self) { + private DrmSessionManager initializePlayerDrm(ReactExoplayerView self) { DrmSessionManager drmSessionManager = null; if (self.drmUUID != null) { try { - drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl, + drmSessionManager = self.buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl, self.drmLicenseHeader); } catch (UnsupportedDrmException e) { int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); eventEmitter.error(getResources().getString(errorStringId), e, "3003"); - return; + return null; } } - if (drmSessionManager == null) { - // DRM Session Manager failed to instantiate, no other work can be done - error has been handled by the instantiating method - return; - } + return drmSessionManager; } - private void initializePlayerSource(ReactExoplayerView self) { + private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager) { ArrayList mediaSourceList = buildTextSources(); MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager); MediaSource mediaSource; @@ -666,10 +664,10 @@ class ReactExoplayerView extends FrameLayout implements mediaDrm.setPropertyString("securityLevel", "L3"); } return new DefaultDrmSessionManager(uuid, mediaDrm, drmCallback, null, false, 3); - } catch(UnsupportedDrmException e) { + } catch(UnsupportedDrmException ex) { // Unsupported DRM exceptions are handled by the calling method - throw e; - } catch (Exception e) { + throw ex; + } catch (Exception ex) { if (retryCount < 3) { // Attempt retry 3 times in case where the OS Media DRM Framework fails for whatever reason return buildDrmSessionManager(uuid, licenseUrl, keyRequestPropertiesArray, ++retryCount); From cca274454269828d29352fa9526fa7983eeaf2a3 Mon Sep 17 00:00:00 2001 From: Armands Malejevs Date: Wed, 30 Mar 2022 14:49:40 +0300 Subject: [PATCH 27/76] Fix typo --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 2b0ee236..ea003623 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -524,7 +524,7 @@ class ReactExoplayerView extends FrameLayout implements @Override public void run() { // DRM initialization must run on a different thread - DRMSessionManager drmSessionManager = initializePlayerDrm(self); + DrmSessionManager drmSessionManager = initializePlayerDrm(self); if (drmSessionManager == null) { // Failed to intialize DRM session manager - cannot continue return; @@ -646,6 +646,10 @@ class ReactExoplayerView extends FrameLayout implements startBufferCheckTimer(); } + private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException { + return buildDrmSessionManager(uuid, licenseUrl, keyRequestPropertiesArray, 0); + } + private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray, int retryCount) throws UnsupportedDrmException { if (Util.SDK_INT < 18) { return null; From 7067afa609e6d7c4e801d0be446d06f082cf1f1c Mon Sep 17 00:00:00 2001 From: Armands Malejevs Date: Wed, 30 Mar 2022 15:04:49 +0300 Subject: [PATCH 28/76] Fix runtime crash --- .../main/java/com/brentvatne/exoplayer/ReactExoplayerView.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index ea003623..18174c41 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -500,6 +500,9 @@ class ReactExoplayerView extends FrameLayout implements } private void stopBufferCheckTimer() { + if (this.bufferCheckTimer == null) { + return; + } this.bufferCheckTimer.cancel(); this.bufferCheckTimer = null; } From bd1f7f50cb48ebba526bdf6c2fad1e7230650d32 Mon Sep 17 00:00:00 2001 From: Armands Malejevs Date: Wed, 30 Mar 2022 15:19:50 +0300 Subject: [PATCH 29/76] Fix runtime issues --- .../exoplayer/ReactExoplayerView.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 18174c41..4d8fd980 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -560,8 +560,8 @@ class ReactExoplayerView extends FrameLayout implements private void initializePlayerCore(ReactExoplayerView self) { ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); - trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); - trackSelector.setParameters(trackSelector.buildUponParameters() + self.trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); + self.trackSelector.setParameters(self.trackSelector.buildUponParameters() .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); @@ -579,21 +579,21 @@ class ReactExoplayerView extends FrameLayout implements DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(getContext()) .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); - player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) + self.player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) .setTrackSelector​(trackSelector) .setBandwidthMeter(bandwidthMeter) .setLoadControl(loadControl) .build(); - player.addListener(self); - player.addMetadataOutput(self); - exoPlayerView.setPlayer(player); - audioBecomingNoisyReceiver.setListener(self); - bandwidthMeter.addEventListener(new Handler(), self); + self.player.addListener(self); + self.player.addMetadataOutput(self); + self.exoPlayerView.setPlayer(player); + self.audioBecomingNoisyReceiver.setListener(self); + self.bandwidthMeter.addEventListener(new Handler(), self); setPlayWhenReady(!isPaused); - playerNeedsSource = true; + self.playerNeedsSource = true; PlaybackParameters params = new PlaybackParameters(rate, 1f); - player.setPlaybackParameters(params); + self.player.setPlaybackParameters(params); } private DrmSessionManager initializePlayerDrm(ReactExoplayerView self) { @@ -615,7 +615,7 @@ class ReactExoplayerView extends FrameLayout implements private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager) { ArrayList mediaSourceList = buildTextSources(); - MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager); + MediaSource videoSource = self.buildMediaSource(self.srcUri, self.extension, drmSessionManager); MediaSource mediaSource; if (mediaSourceList.size() == 0) { mediaSource = videoSource; From edd3cc169819c7ce8c457fececd569fd52977d08 Mon Sep 17 00:00:00 2001 From: Armands Malejevs Date: Wed, 30 Mar 2022 15:27:09 +0300 Subject: [PATCH 30/76] Fix runtime issues --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 4d8fd980..759989ef 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -537,13 +537,13 @@ class ReactExoplayerView extends FrameLayout implements @Override public void run () { // Source initialization must run on the main thread - initializePlayerSource(self, drmSessionManager); + initializePlayerSource(self, drmSessionManager, srcUri); } }); } }); } else { - initializePlayerSource(self, null); + initializePlayerSource(self, null, srcUri); } @@ -613,9 +613,9 @@ class ReactExoplayerView extends FrameLayout implements return drmSessionManager; } - private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager) { + private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager, Uri srcUri) { ArrayList mediaSourceList = buildTextSources(); - MediaSource videoSource = self.buildMediaSource(self.srcUri, self.extension, drmSessionManager); + MediaSource videoSource = self.buildMediaSource(srcUri, self.extension, drmSessionManager); MediaSource mediaSource; if (mediaSourceList.size() == 0) { mediaSource = videoSource; From 0e3fe11b7885ee2ab63dfa86c3b2caac7ff40c73 Mon Sep 17 00:00:00 2001 From: Armands Malejevs Date: Wed, 30 Mar 2022 15:33:17 +0300 Subject: [PATCH 31/76] Fix runtime issues --- .../exoplayer/ReactExoplayerView.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 759989ef..2022c108 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -537,13 +537,13 @@ class ReactExoplayerView extends FrameLayout implements @Override public void run () { // Source initialization must run on the main thread - initializePlayerSource(self, drmSessionManager, srcUri); + initializePlayerSource(self, drmSessionManager); } }); } }); - } else { - initializePlayerSource(self, null, srcUri); + } else if (srcUri != null) { + initializePlayerSource(self, null); } @@ -560,8 +560,8 @@ class ReactExoplayerView extends FrameLayout implements private void initializePlayerCore(ReactExoplayerView self) { ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); - self.trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); - self.trackSelector.setParameters(self.trackSelector.buildUponParameters() + trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); + trackSelector.setParameters(trackSelector.buildUponParameters() .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); @@ -579,21 +579,21 @@ class ReactExoplayerView extends FrameLayout implements DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(getContext()) .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); - self.player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) + player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) .setTrackSelector​(trackSelector) .setBandwidthMeter(bandwidthMeter) .setLoadControl(loadControl) .build(); - self.player.addListener(self); - self.player.addMetadataOutput(self); - self.exoPlayerView.setPlayer(player); - self.audioBecomingNoisyReceiver.setListener(self); - self.bandwidthMeter.addEventListener(new Handler(), self); + player.addListener(self); + player.addMetadataOutput(self); + exoPlayerView.setPlayer(player); + audioBecomingNoisyReceiver.setListener(self); + bandwidthMeter.addEventListener(new Handler(), self); setPlayWhenReady(!isPaused); - self.playerNeedsSource = true; + playerNeedsSource = true; PlaybackParameters params = new PlaybackParameters(rate, 1f); - self.player.setPlaybackParameters(params); + player.setPlaybackParameters(params); } private DrmSessionManager initializePlayerDrm(ReactExoplayerView self) { @@ -613,9 +613,9 @@ class ReactExoplayerView extends FrameLayout implements return drmSessionManager; } - private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager, Uri srcUri) { + private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager) { ArrayList mediaSourceList = buildTextSources(); - MediaSource videoSource = self.buildMediaSource(srcUri, self.extension, drmSessionManager); + MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager); MediaSource mediaSource; if (mediaSourceList.size() == 0) { mediaSource = videoSource; From 4bf4ee7392454edcc5d302c91ed7e42bb031cbf0 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Wed, 30 Mar 2022 22:36:21 +0200 Subject: [PATCH 32/76] Use prop types from `deprecated-react-native-prop-types` Starting with React Native 0.68, using Prop Types from `react-native` emits a warning. --- Video.js | 3 ++- package.json | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Video.js b/Video.js index b492d48b..959d0fe7 100644 --- a/Video.js +++ b/Video.js @@ -1,6 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { StyleSheet, requireNativeComponent, NativeModules, View, ViewPropTypes, Image, Platform, findNodeHandle } from 'react-native'; +import { StyleSheet, requireNativeComponent, NativeModules, View, Image, Platform, findNodeHandle } from 'react-native'; +import { ViewPropTypes } from "deprecated-react-native-prop-types"; import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; import TextTrackType from './TextTrackType'; import FilterType from './FilterType'; diff --git a/package.json b/package.json index cb8d146e..9dabd081 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "react-native-windows": "^0.61.0-0" }, "dependencies": { + "deprecated-react-native-prop-types": "^2.2.0", "keymirror": "^0.1.1", "prop-types": "^15.7.2", "shaka-player": "^2.5.9" From e39d87edae3dc66e2da852d6a742e29a280ee685 Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Thu, 31 Mar 2022 15:37:53 +0300 Subject: [PATCH 33/76] VEX-6778: Android: React Native video getVideoTrackInfoFromManifest crash (#20) JIRA: VEX-6778 Move video track info request logic entirely on a different thread, completely preventing ANRs in this section of code. --- .../exoplayer/ReactExoplayerView.java | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index e4000116..f764e2c0 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -100,6 +100,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Callable; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.lang.Integer; @SuppressLint("ViewConstructor") @@ -943,8 +944,24 @@ class ReactExoplayerView extends FrameLayout implements int width = videoFormat != null ? videoFormat.width : 0; int height = videoFormat != null ? videoFormat.height : 0; String trackId = videoFormat != null ? videoFormat.id : "-1"; - eventEmitter.load(player.getDuration(), player.getCurrentPosition(), width, height, - getAudioTrackInfo(), getTextTrackInfo(), getVideoTrackInfo(), trackId); + + // Properties that must be accessed on the main thread + long duration = player.getDuration(); + long currentPosition = player.getCurrentPosition(); + WritableArray audioTrackInfo = getAudioTrackInfo(); + WritableArray textTrackInfo = getTextTrackInfo(); + Timeline timelineRef = player.getCurrentTimeline(); + int trackRendererIndex = getTrackRendererIndex(C.TRACK_TYPE_VIDEO); + + ExecutorService es = Executors.newSingleThreadExecutor(); + es.execute(new Runnable() { + @Override + public void run() { + // To prevent ANRs caused by getVideoTrackInfo we run this on a different thread and notify the player only when we're done + eventEmitter.load(duration, currentPosition, width, height, + audioTrackInfo, textTrackInfo, getVideoTrackInfo(timelineRef, trackRendererIndex), trackId); + } + }); } } @@ -971,9 +988,9 @@ class ReactExoplayerView extends FrameLayout implements } return audioTracks; } - private WritableArray getVideoTrackInfo() { + private WritableArray getVideoTrackInfo(Timeline timelineRef, int trackRendererIndex) { - WritableArray contentVideoTracks = this.getVideoTrackInfoFromManifest(); + WritableArray contentVideoTracks = this.getVideoTrackInfoFromManifest(timelineRef); if (contentVideoTracks != null) { isUsingContentResolution = true; return contentVideoTracks; @@ -982,12 +999,12 @@ class ReactExoplayerView extends FrameLayout implements WritableArray videoTracks = Arguments.createArray(); MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); - int index = getTrackRendererIndex(C.TRACK_TYPE_VIDEO); - if (info == null || index == C.INDEX_UNSET) { + + if (info == null || trackRendererIndex == C.INDEX_UNSET) { return videoTracks; } - TrackGroupArray groups = info.getTrackGroups(index); + TrackGroupArray groups = info.getTrackGroups(trackRendererIndex); for (int i = 0; i < groups.length; ++i) { TrackGroup group = groups.get(i); @@ -998,8 +1015,7 @@ class ReactExoplayerView extends FrameLayout implements videoTrack.putInt("height",format.height == Format.NO_VALUE ? 0 : format.height); videoTrack.putInt("bitrate", format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); videoTrack.putString("codecs", format.codecs != null ? format.codecs : ""); - videoTrack.putString("trackId", - format.id == null ? String.valueOf(trackIndex) : format.id); + videoTrack.putString("trackId", format.id == null ? String.valueOf(trackIndex) : format.id); if (isFormatSupported(format)) { videoTracks.pushMap(videoTrack); } @@ -1009,11 +1025,14 @@ class ReactExoplayerView extends FrameLayout implements return videoTracks; } - private WritableArray getVideoTrackInfoFromManifest() { + private WritableArray getVideoTrackInfoFromManifest(Timeline timeline) { + return this.getVideoTrackInfoFromManifest(timeline, 0); + } + + private WritableArray getVideoTrackInfoFromManifest(Timeline timelineRef, int retryCount) { ExecutorService es = Executors.newSingleThreadExecutor(); final DataSource dataSource = this.mediaDataSourceFactory.createDataSource(); final Uri sourceUri = this.srcUri; - final Timeline timelineRef = this.player.getCurrentTimeline(); final long startTime = this.contentStartTime * 1000 - 100; // s -> ms with 100ms offset Future result = es.submit(new Callable() { @@ -1064,7 +1083,10 @@ class ReactExoplayerView extends FrameLayout implements }); try { - WritableArray results = result.get(); + WritableArray results = result.get(3000, TimeUnit.MILLISECONDS); + if (results == null && retryCount < 1) { + return this.getVideoTrackInfoFromManifest(timelineRef, ++retryCount); + } es.shutdown(); return results; } catch (Exception e) {} From f78c623df47e3d1a37a74088b2ef5016e1db6b52 Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Thu, 7 Apr 2022 19:36:50 +0300 Subject: [PATCH 34/76] VEX-6779: React Native Video buildDrmSessionManager crash followup (#24) Fix infinite loading on some devices caused by earlier fix to drm session manager crash --- .../exoplayer/ReactExoplayerView.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 42974360..d8ab3ac2 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -510,6 +510,7 @@ class ReactExoplayerView extends FrameLayout implements private void initializePlayer() { ReactExoplayerView self = this; + Activity activity = themedReactContext.getCurrentActivity(); // This ensures all props have been settled, to avoid async racing conditions. new Handler().postDelayed(new Runnable() { @Override @@ -519,7 +520,6 @@ class ReactExoplayerView extends FrameLayout implements // Initialize core configuration and listeners initializePlayerCore(self); } - if (playerNeedsSource && srcUri != null) { exoPlayerView.invalidateAspectRatio(); // DRM session manager creation must be done on a different thread to prevent crashes so we start a new thread @@ -529,14 +529,16 @@ class ReactExoplayerView extends FrameLayout implements public void run() { // DRM initialization must run on a different thread DrmSessionManager drmSessionManager = initializePlayerDrm(self); - if (drmSessionManager == null) { + if (drmSessionManager == null && self.drmUUID != null) { // Failed to intialize DRM session manager - cannot continue + Log.e("ExoPlayer Exception", "Failed to initialize DRM Session Manager Framework!"); + eventEmitter.error("Failed to initialize DRM Session Manager Framework!", new Exception("DRM Session Manager Framework failure!"), "3003"); return; } + // Initialize handler to run on the main thread - new Handler(Looper.getMainLooper()).post(new Runnable () { - @Override - public void run () { + activity.runOnUiThread(new Runnable() { + public void run() { // Source initialization must run on the main thread initializePlayerSource(self, drmSessionManager); } @@ -595,6 +597,7 @@ class ReactExoplayerView extends FrameLayout implements PlaybackParameters params = new PlaybackParameters(rate, 1f); player.setPlaybackParameters(params); + } private DrmSessionManager initializePlayerDrm(ReactExoplayerView self) { @@ -1283,6 +1286,14 @@ class ReactExoplayerView extends FrameLayout implements } else if(cause instanceof MediaDrmCallbackException) { errorCode = "3005"; errorString = getResources().getString(R.string.unrecognized_media_format); + if (!hasDrmFailed) { + // When DRM fails to reach the app level certificate server it will fail with a source error so we assume that it is DRM related and try one more time + hasDrmFailed = true; + playerNeedsSource = true; + updateResumePosition(); + initializePlayer(); + return; + } } else { errorCode = "2021"; errorString = getResources().getString(R.string.unrecognized_media_format); From 03f77495fc7ea3cb1984fae9a749adf063bfe462 Mon Sep 17 00:00:00 2001 From: Gabriel Rivero Date: Tue, 19 Apr 2022 12:12:47 -0400 Subject: [PATCH 35/76] VEX-6350: add onPlaybackStateChanged prop (#25) * Adds the new prop onPlaybackStateChanged --- README.md | 19 +++++++++++++++++++ Video.js | 8 ++++++++ .../exoplayer/ReactExoplayerView.java | 5 +++++ .../exoplayer/VideoEventEmitter.java | 12 +++++++++++- 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2881926f..80577e2a 100644 --- a/README.md +++ b/README.md @@ -332,6 +332,7 @@ var styles = StyleSheet.create({ * [onFullscreenPlayerDidDismiss](#onfullscreenplayerdiddismiss) * [onLoad](#onload) * [onLoadStart](#onloadstart) +* [onPlaybackStateChanged]($onPlaybackStateChanged) * [onReadyForDisplay](#onreadyfordisplay) * [onPictureInPictureStatusChanged](#onpictureinpicturestatuschanged) * [onPlaybackRateChange](#onplaybackratechange) @@ -1110,6 +1111,24 @@ Example: Platforms: all +#### onPlaybackStateChanged +Callback function that is called when the playback state changes. + +Payload: + +Property | Description +--- | --- +isPlaying | boolean | Boolean indicating if the media is playing or not + +Example: +``` +{ + isPlaying: true, +} +``` + +Platforms: Android ExoPlayer + #### onReadyForDisplay Callback function that is called when the first video frame is ready for display. This is when the poster is removed. diff --git a/Video.js b/Video.js index db88752f..13d141f0 100644 --- a/Video.js +++ b/Video.js @@ -99,6 +99,12 @@ export default class Video extends Component { } }; + _onPlaybackStateChanged = (event) => { + if (this.props.onPlaybackStateChanged) { + this.props.onPlaybackStateChanged(event.nativeEvent); + } + }; + _onLoad = (event) => { // Need to hide poster here for windows as onReadyForDisplay is not implemented if (Platform.OS === 'windows') { @@ -311,6 +317,7 @@ export default class Video extends Component { requestHeaders: source.headers ? this.stringsOnlyObject(source.headers) : {}, }, onVideoLoadStart: this._onLoadStart, + onVideoPlaybackStateChanged: this._onPlaybackStateChanged, onVideoLoad: this._onLoad, onVideoError: this._onError, onVideoProgress: this._onProgress, @@ -491,6 +498,7 @@ Video.propTypes = { useSecureView: PropTypes.bool, hideShutterView: PropTypes.bool, onLoadStart: PropTypes.func, + onPlaybackStateChanged: PropTypes.func, onLoad: PropTypes.func, onBuffer: PropTypes.func, onError: PropTypes.func, diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index d8ab3ac2..5a818e32 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1243,6 +1243,11 @@ class ReactExoplayerView extends FrameLayout implements eventEmitter.playbackRateChange(params.speed); } + @Override + public void onIsPlayingChanged(boolean isPlaying) { + eventEmitter.playbackStateChanged(isPlaying); + } + @Override public void onPlayerError(ExoPlaybackException e) { String errorString = "ExoPlaybackException type : " + e.type; diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java index 266d2538..5aeb09ce 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java @@ -44,6 +44,7 @@ class VideoEventEmitter { private static final String EVENT_RESUME = "onPlaybackResume"; private static final String EVENT_READY = "onReadyForDisplay"; private static final String EVENT_BUFFER = "onVideoBuffer"; + private static final String EVENT_PLAYBACK_STATE_CHANGED = "onVideoPlaybackStateChanged"; private static final String EVENT_BUFFER_PROGRESS = "onVideoBufferProgress"; private static final String EVENT_IDLE = "onVideoIdle"; private static final String EVENT_TIMED_METADATA = "onTimedMetadata"; @@ -66,6 +67,7 @@ class VideoEventEmitter { EVENT_RESUME, EVENT_READY, EVENT_BUFFER, + EVENT_PLAYBACK_STATE_CHANGED, EVENT_BUFFER_PROGRESS, EVENT_IDLE, EVENT_TIMED_METADATA, @@ -91,6 +93,7 @@ class VideoEventEmitter { EVENT_RESUME, EVENT_READY, EVENT_BUFFER, + EVENT_PLAYBACK_STATE_CHANGED, EVENT_BUFFER_PROGRESS, EVENT_IDLE, EVENT_TIMED_METADATA, @@ -137,8 +140,9 @@ class VideoEventEmitter { private static final String EVENT_PROP_TIMED_METADATA = "metadata"; - private static final String EVENT_PROP_BITRATE = "bitrate"; + private static final String EVENT_PROP_BITRATE = "bitrate"; + private static final String EVENT_PROP_IS_PLAYING = "isPlaying"; void setViewId(int viewId) { this.viewId = viewId; @@ -215,6 +219,12 @@ class VideoEventEmitter { receiveEvent(EVENT_BUFFER, map); } + void playbackStateChanged(boolean isPlaying) { + WritableMap map = Arguments.createMap(); + map.putBoolean(EVENT_PROP_IS_PLAYING, isPlaying); + receiveEvent(EVENT_PLAYBACK_STATE_CHANGED, map); + } + void bufferProgress(double start, double end) { WritableMap map = Arguments.createMap(); map.putDouble(EVENT_PROP_BUFFER_START, start); From 6815044013efeeed5fc73e818d658e2ed4a5bd9d Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Wed, 20 Apr 2022 03:05:32 +0300 Subject: [PATCH 36/76] VEX-6099: Android: Progress knob jumps backward after tapping Skip Forwards multiple times (#26) Move to the new way of detecting seek end and remove onSeekProcessed which was deprecated. --- .../exoplayer/ReactExoplayerView.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 5a818e32..b86deacb 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1206,6 +1206,19 @@ class ReactExoplayerView extends FrameLayout implements && player.getRepeatMode() == Player.REPEAT_MODE_ONE) { eventEmitter.end(); } + + } + + @Override + public void onPlaybackStateChanged(int playbackState) { + if (playbackState == Player.STATE_READY && seekTime != C.TIME_UNSET) { + eventEmitter.seek(player.getCurrentPosition(), seekTime); + seekTime = C.TIME_UNSET; + if (isUsingContentResolution) { + // We need to update the selected track to make sure that it still matches user selection if track list has changed in this period + setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue); + } + } } @Override @@ -1213,16 +1226,6 @@ class ReactExoplayerView extends FrameLayout implements // Do nothing. } - @Override - public void onSeekProcessed() { - eventEmitter.seek(player.getCurrentPosition(), seekTime); - seekTime = C.TIME_UNSET; - if (isUsingContentResolution) { - // We need to update the selected track to make sure that it still matches user selection if track list has changed in this period - setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue); - } - } - @Override public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { // Do nothing. From f6e9cc850215aa058c81b7452f7592e4cd57fb72 Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Tue, 26 Apr 2022 02:44:37 +0300 Subject: [PATCH 37/76] VEX-6891: Android: Infinite loading when closing & reopening the player on low end devices (#27) On low end devices when replaying same content too quickly DRM could fail with an uncaptured error (DrmSessionException). To prevent this we now check for that error and retry DRM one more time. --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index b86deacb..15526b27 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -34,6 +34,7 @@ import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.drm.MediaDrmCallbackException; +import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.PlaybackParameters; @@ -1291,7 +1292,7 @@ class ReactExoplayerView extends FrameLayout implements if (cause instanceof DefaultDrmSessionManager.MissingSchemeDataException) { errorCode = "3004"; errorString = getResources().getString(R.string.unrecognized_media_format); - } else if(cause instanceof MediaDrmCallbackException) { + } else if(cause instanceof MediaDrmCallbackException || cause instanceof DrmSessionException) { errorCode = "3005"; errorString = getResources().getString(R.string.unrecognized_media_format); if (!hasDrmFailed) { @@ -1300,6 +1301,7 @@ class ReactExoplayerView extends FrameLayout implements playerNeedsSource = true; updateResumePosition(); initializePlayer(); + setPlayWhenReady(true); return; } } else { @@ -1317,6 +1319,7 @@ class ReactExoplayerView extends FrameLayout implements playerNeedsSource = true; updateResumePosition(); initializePlayer(); + setPlayWhenReady(true); return; } } @@ -1373,7 +1376,8 @@ class ReactExoplayerView extends FrameLayout implements public void setSrc(final Uri uri, final String extension, Map headers) { if (uri != null) { boolean isSourceEqual = uri.equals(srcUri); - + + hasDrmFailed = false; this.srcUri = uri; this.extension = extension; this.requestHeaders = headers; From b274541431bae8a453a6e24111058edb235d6474 Mon Sep 17 00:00:00 2001 From: Gabriel Rivero Date: Thu, 28 Apr 2022 20:17:59 -0400 Subject: [PATCH 38/76] VEX-7093: Protect initialization of DRM (#29) This PR will fix the crash from the experiment on react-native-video Jira: VEX-7093 velocity PR: crunchyroll/velocity#2443 The crash was caused by using the player object before it was set by another thread, the change simply waits for the player to be available before executing any player interaction after DRM initialization. Reviews Major reviewer (domain expert): @jctorresM Minor reviewer: @jacob-livingston --- .../com/brentvatne/exoplayer/ReactExoplayerView.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 15526b27..7d7a7c0e 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -632,6 +632,16 @@ class ReactExoplayerView extends FrameLayout implements mediaSource = new MergingMediaSource(textSourceArray); } + // wait for player to be set + while (player == null) { + try { + wait(); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + Log.e("ExoPlayer Exception", ex.toString()); + } + } + boolean haveResumePosition = resumeWindow != C.INDEX_UNSET; if (haveResumePosition) { player.seekTo(resumeWindow, resumePosition); From 9035e7dfc94f6bcc8ec8e89a08367d87552148b7 Mon Sep 17 00:00:00 2001 From: Gabriel Rivero Date: Tue, 3 May 2022 16:43:53 -0400 Subject: [PATCH 39/76] VEX-6625: Handle state to keep playing after gaining connection (#28) This PR handles autoplay after disconnection Jira: VEX-6625 There was an error in react-native-video not being properly handled, in addition, the state of the player when reconnecting using a free user was not the expected one, requesting the player to keep playing when ready, made the behavior consistent in all scenarios Velocity PR: crunchyroll/velocity#2430 Reviews Major reviewer (domain expert): @jctorresM Minor reviewer: @jacob-livingston --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 7d7a7c0e..fcddb28c 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -817,7 +817,6 @@ class ReactExoplayerView extends FrameLayout implements case Player.STATE_IDLE: case Player.STATE_ENDED: initializePlayer(); - break; case Player.STATE_BUFFERING: case Player.STATE_READY: if (!player.getPlayWhenReady()) { @@ -827,7 +826,6 @@ class ReactExoplayerView extends FrameLayout implements default: break; } - } else { initializePlayer(); } @@ -1314,6 +1312,12 @@ class ReactExoplayerView extends FrameLayout implements setPlayWhenReady(true); return; } + } else if (cause instanceof HttpDataSource.HttpDataSourceException) { + // this exception happens when connectivity is lost + updateResumePosition(); + initializePlayer(); + setPlayWhenReady(true); + return; } else { errorCode = "2021"; errorString = getResources().getString(R.string.unrecognized_media_format); From 3bd4dcd99875602cfc80e0d4d3b2e3ed6c64a45b Mon Sep 17 00:00:00 2001 From: "Tone T. Thangsongcharoen" Date: Wed, 4 May 2022 20:21:17 -0500 Subject: [PATCH 40/76] Update Dependency to JitPack! Updated Android-ScalableVideoView to use JitPack instead of JCenter as JCenter has been deprecated. https://www.jitpack.io/#thang2162/Android-ScalableVideoView --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 2fb8dfd2..4120458b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -22,5 +22,5 @@ android { dependencies { //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}" - implementation 'com.yqritc:android-scalablevideoview:1.0.4' + implementation 'com.github.thang2162:Android-ScalableVideoView:v1.1.1' } From c584022d34a3b4413482c47890e1cd20d28a801f Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Fri, 6 May 2022 00:10:55 +0300 Subject: [PATCH 41/76] VEX-7094: Android: Crash in ReactExoplayerView.java Line 694 (#30) When moving init logic to a different thread we missed one function that was not captured if an error occurred. This PR wraps the source init function that happens on a different thread in a try/catch to capture any errors that occur. --- .../brentvatne/exoplayer/ReactExoplayerView.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index fcddb28c..931bc9fe 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -540,8 +540,15 @@ class ReactExoplayerView extends FrameLayout implements // Initialize handler to run on the main thread activity.runOnUiThread(new Runnable() { public void run() { - // Source initialization must run on the main thread - initializePlayerSource(self, drmSessionManager); + try { + // Source initialization must run on the main thread + initializePlayerSource(self, drmSessionManager); + } catch (Exception ex) { + self.playerNeedsSource = true; + Log.e("ExoPlayer Exception", "Failed to initialize Player!"); + Log.e("ExoPlayer Exception", ex.toString()); + self.eventEmitter.error(ex.toString(), ex, "1001"); + } } }); } @@ -620,7 +627,7 @@ class ReactExoplayerView extends FrameLayout implements private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager) { ArrayList mediaSourceList = buildTextSources(); - MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager); + MediaSource videoSource = buildMediaSource(self.srcUri, self.extension, drmSessionManager); MediaSource mediaSource; if (mediaSourceList.size() == 0) { mediaSource = videoSource; From c193fe873efbb1d05339336e51f3682eeb910231 Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Tue, 10 May 2022 00:53:09 +0300 Subject: [PATCH 42/76] VEX-7095: Crash in ReactExoplayerView.java Line 1059 (#31) Prevent crashing when the track selector is null. --- .../com/brentvatne/exoplayer/ReactExoplayerView.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 931bc9fe..5b770d9c 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -571,8 +571,8 @@ class ReactExoplayerView extends FrameLayout implements private void initializePlayerCore(ReactExoplayerView self) { ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); - trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); - trackSelector.setParameters(trackSelector.buildUponParameters() + self.trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); + self.trackSelector.setParameters(trackSelector.buildUponParameters() .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); @@ -591,7 +591,7 @@ class ReactExoplayerView extends FrameLayout implements new DefaultRenderersFactory(getContext()) .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) - .setTrackSelector​(trackSelector) + .setTrackSelector​(self.trackSelector) .setBandwidthMeter(bandwidthMeter) .setLoadControl(loadControl) .build(); @@ -1042,6 +1042,11 @@ class ReactExoplayerView extends FrameLayout implements private WritableArray getAudioTrackInfo() { WritableArray audioTracks = Arguments.createArray(); + if (trackSelector == null) { + // Likely player is unmounting so no audio tracks are available anymore + return audioTracks; + } + MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); int index = getTrackRendererIndex(C.TRACK_TYPE_AUDIO); if (info == null || index == C.INDEX_UNSET) { From 5c7d0c130559e2c747800cebcceef99b510c69c9 Mon Sep 17 00:00:00 2001 From: Zhminko Roman <92923411+ZhminkoRoman@users.noreply.github.com> Date: Wed, 11 May 2022 10:32:46 +0300 Subject: [PATCH 43/76] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 24e2edb0..aa1548ad 100644 --- a/README.md +++ b/README.md @@ -1500,6 +1500,15 @@ allprojects { } } ``` + +## Black Screen on Release build (Android) +If your video work on Debug mode, but on Release you see only black screen, please, check the link to your video. If you use 'http' protocol there, you will need to add next string to your AndroidManifest.xml file. +``` + +``` If you encounter an error `Could not find com.android.support:support-annotations:27.0.0.` reinstall your Android Support Repository. From 9e46997e96960276f800cfa3b7362952a14a37fb Mon Sep 17 00:00:00 2001 From: Zhminko Roman <92923411+ZhminkoRoman@users.noreply.github.com> Date: Wed, 11 May 2022 10:40:17 +0300 Subject: [PATCH 44/76] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index aa1548ad..894f0eb9 100644 --- a/README.md +++ b/README.md @@ -1500,6 +1500,7 @@ allprojects { } } ``` +If you encounter an error `Could not find com.android.support:support-annotations:27.0.0.` reinstall your Android Support Repository. ## Black Screen on Release build (Android) If your video work on Debug mode, but on Release you see only black screen, please, check the link to your video. If you use 'http' protocol there, you will need to add next string to your AndroidManifest.xml file. @@ -1510,8 +1511,6 @@ If your video work on Debug mode, but on Release you see only black screen, plea > ``` -If you encounter an error `Could not find com.android.support:support-annotations:27.0.0.` reinstall your Android Support Repository. - ## TODOS - [ ] Add support for playing multiple videos in a sequence (will interfere with current `repeat` implementation) From 87472ebd4a29ac087cccf23f356205225a472309 Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Mon, 16 May 2022 10:54:04 +0300 Subject: [PATCH 45/76] VEX-7213: Update readme to include missing props (#32) --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 80577e2a..eb0b7fa1 100644 --- a/README.md +++ b/README.md @@ -278,6 +278,7 @@ var styles = StyleSheet.create({ * [automaticallyWaitsToMinimizeStalling](#automaticallyWaitsToMinimizeStalling) * [backBufferDurationMs](#backBufferDurationMs) * [bufferConfig](#bufferconfig) +* [contentStartTime](#contentStartTime) * [controls](#controls) * [currentPlaybackTime](#currentPlaybackTime) * [disableFocus](#disableFocus) @@ -412,11 +413,14 @@ Platforms: Android ExoPlayer, iOS #### controls Determines whether to show player controls. -* ** false (default)** - Don't show player controls +* **false (default)** - Don't show player controls * **true** - Show player controls Note on iOS, controls are always shown when in fullscreen mode. +### contentStartTime +The start time in ms for SSAI content. This determines at what time to load the video info like resolutions. Use this only when you have SSAI stream where ads resolution is not the same as content resolution. + For Android MediaPlayer, you will need to build your own controls or use a package like [react-native-video-controls](https://github.com/itsnubix/react-native-video-controls) or [react-native-video-player](https://github.com/cornedor/react-native-video-player). Note on Android ExoPlayer, native controls are available by default. If needed, you can also add your controls or use a package like [react-native-video-controls]. From 68b9db4d1120f2588120ad09883cef676d95190d Mon Sep 17 00:00:00 2001 From: Nick Fujita Date: Thu, 19 May 2022 22:29:25 +0900 Subject: [PATCH 46/76] iOS Swift Conversion (#2527) Converts iOS implementation from Objective-c to Swift # During the conversion process some updates to the code structure were also made - Modularize codebase from single file to smaller focused files - Untangled large nested IF statements - Added more null checks, since Swift is more strict with null pointers - Added property to allow for decoding of local video sources with self contained key for offline playback - Updates example apps to test react-native 0.63.4 and uses auto native dependency imports for android and ios --- .gitignore | 8 + CHANGELOG.md | 5 +- README.md | 56 + Video.js | 1 + examples/basic/.buckconfig | 6 - examples/basic/.flowconfig | 58 - examples/basic/.gitattributes | 1 - examples/basic/.gitignore | 55 - examples/basic/.watchmanconfig | 1 - examples/basic/__tests__/App-test.js | 14 + examples/basic/android/app/BUCK | 69 +- examples/basic/android/app/build.gradle | 101 +- examples/basic/android/app/build_defs.bzl | 19 + examples/basic/android/app/debug.keystore | Bin 0 -> 2257 bytes examples/basic/android/app/proguard-rules.pro | 2 +- .../android/app/src/debug/AndroidManifest.xml | 8 + .../com/videoplayer/ReactNativeFlipper.java | 72 + .../android/app/src/main/AndroidManifest.xml | 11 +- .../java/com/videoplayer/MainActivity.java | 16 +- .../java/com/videoplayer/MainApplication.java | 25 +- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3418 -> 3056 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5024 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2206 -> 2096 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2858 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4842 -> 4569 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7098 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 7718 -> 6464 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10676 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9250 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15523 bytes .../app/src/main/res/values/styles.xml | 1 + examples/basic/android/build.gradle | 22 +- examples/basic/android/gradle.properties | 9 +- .../android/gradle/wrapper/gradle-wrapper.jar | Bin 52266 -> 58695 bytes .../gradle/wrapper/gradle-wrapper.properties | 3 +- examples/basic/android/gradlew | 147 +- examples/basic/android/gradlew.bat | 193 +- examples/basic/android/keystores/BUCK | 8 - .../keystores/debug.keystore.properties | 4 - examples/basic/android/settings.gradle | 7 - examples/basic/app.json | 4 + examples/basic/babel.config.js | 2 +- examples/basic/ios/AppDelegate.h | 16 - examples/basic/ios/AppDelegate.m | 52 - .../basic/ios/Base.lproj/LaunchScreen.xib | 42 - .../AppIcon.appiconset/Contents.json | 38 - examples/basic/ios/Info.plist | 47 - examples/basic/ios/Podfile | 26 + examples/basic/ios/Podfile.lock | 392 + .../basic/ios/VideoPlayer-Bridging-Header.h | 12 + .../Info.plist} | 0 .../ios/VideoPlayer-tvOSTests/Info.plist | 24 + .../ios/VideoPlayer.xcodeproj/project.pbxproj | 1499 ++-- .../xcschemes/VideoPlayer-tvOS.xcscheme | 47 +- .../xcschemes/VideoPlayer.xcscheme | 41 +- .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + examples/basic/ios/VideoPlayer/AppDelegate.h | 3 +- examples/basic/ios/VideoPlayer/AppDelegate.m | 42 +- .../VideoPlayer/Base.lproj/LaunchScreen.xib | 42 - examples/basic/ios/VideoPlayer/File.swift | 1 + .../VideoPlayer/Images.xcassets/Contents.json | 6 + examples/basic/ios/VideoPlayer/Info.plist | 33 +- .../ios/VideoPlayer/LaunchScreen.storyboard | 58 + .../basic/ios/VideoPlayerTests/Info.plist | 24 + .../ios/VideoPlayerTests/VideoPlayerTests.m | 65 + examples/basic/ios/main.jsbundle | 5 - examples/basic/ios/main.m | 19 - examples/basic/jsconfig.json | 18 - examples/basic/metro.config.js | 32 +- examples/basic/package.json | 34 +- examples/basic/react-native.config.js | 5 - examples/basic/src/VideoPlayer.android.tsx | 13 +- examples/basic/src/VideoPlayer.ios.tsx | 8 +- examples/basic/src/VideoPlayer.windows.tsx | 2 +- examples/basic/src/index.js | 6 +- examples/basic/tsconfig.json | 38 +- examples/basic/windows/VideoPlayer.sln | 128 +- examples/basic/windows/VideoPlayer/.gitignore | 1 + examples/basic/windows/VideoPlayer/App.cpp | 129 +- examples/basic/windows/VideoPlayer/App.h | 33 +- examples/basic/windows/VideoPlayer/App.idl | 6 +- examples/basic/windows/VideoPlayer/App.xaml | 4 +- .../VideoPlayer/AutolinkedNativeModules.g.cpp | 18 + .../VideoPlayer/AutolinkedNativeModules.g.h | 10 + .../AutolinkedNativeModules.g.props | 6 + .../AutolinkedNativeModules.g.targets | 10 + .../windows/VideoPlayer/Bundle/.gitignore | 2 - .../basic/windows/VideoPlayer/MainPage.cpp | 20 + examples/basic/windows/VideoPlayer/MainPage.h | 19 + .../basic/windows/VideoPlayer/MainPage.idl | 8 + .../basic/windows/VideoPlayer/MainPage.xaml | 16 + .../windows/VideoPlayer/Package.appxmanifest | 21 +- .../windows/VideoPlayer/PropertySheet.props | 30 +- .../VideoPlayer/ReactPackageProvider.cpp | 35 +- .../VideoPlayer/ReactPackageProvider.h | 33 +- .../VideoPlayer/VideoPlayer_TemporaryKey.pfx | Bin 2544 -> 2606 bytes .../windows/VideoPlayer/nativeModules.g.h | 2 - .../basic/windows/VideoPlayer/packages.config | 8 +- examples/basic/windows/VideoPlayer/pch.cpp | 2 +- examples/basic/windows/VideoPlayer/pch.h | 54 +- ...ideoPlayer.vcxproj => videoplayer.vcxproj} | 368 +- ...oj.filters => videoplayer.vcxproj.filters} | 120 +- examples/basic/yarn.lock | 6581 +++++++++-------- ios/RCTVideo.xcodeproj/project.pbxproj | 91 +- ios/Video/DataStructures/DRMParams.swift | 30 + .../SelectedTrackCriteria.swift | 18 + ios/Video/DataStructures/TextTrack.swift | 25 + ios/Video/DataStructures/VideoSource.swift | 31 + ios/Video/Features/RCTPictureInPicture.swift | 75 + ios/Video/Features/RCTPlayerObserver.swift | 207 + ios/Video/Features/RCTPlayerOperations.swift | 223 + .../Features/RCTResourceLoaderDelegate.swift | 167 + ios/Video/Features/RCTVideoDRM.swift | 168 + .../Features/RCTVideoErrorHandling.swift | 103 + ios/Video/Features/RCTVideoSave.swift | 75 + ios/Video/Features/RCTVideoUtils.swift | 250 + ios/Video/RCTSwiftLog/RCTSwiftLog.h | 11 + ios/Video/RCTSwiftLog/RCTSwiftLog.m | 32 + ios/Video/RCTSwiftLog/RCTSwiftLog.swift | 53 + ios/Video/RCTVideo-Bridging-Header.h | 7 + ios/Video/RCTVideo.h | 67 - ios/Video/RCTVideo.m | 2032 ----- ios/Video/RCTVideo.swift | 1061 +++ ios/Video/RCTVideoManager.h | 6 - ios/Video/RCTVideoManager.m | 85 +- ios/Video/RCTVideoManager.swift | 63 + ios/Video/RCTVideoPlayerViewController.h | 20 - ios/Video/RCTVideoPlayerViewController.m | 43 - ios/Video/RCTVideoPlayerViewController.swift | 44 + .../RCTVideoPlayerViewControllerDelegate.h | 7 - ...RCTVideoPlayerViewControllerDelegate.swift | 7 + ios/Video/UIView+FindUIViewController.h | 15 - ios/Video/UIView+FindUIViewController.m | 21 - ios/Video/UIView+FindUIViewController.swift | 18 + ios/VideoCaching/RCTVideoCachingHandler.swift | 87 + package.json | 5 +- react-native-video.podspec | 11 +- 138 files changed, 8869 insertions(+), 7589 deletions(-) delete mode 100644 examples/basic/.buckconfig delete mode 100644 examples/basic/.flowconfig delete mode 100644 examples/basic/.gitattributes delete mode 100644 examples/basic/.gitignore delete mode 100644 examples/basic/.watchmanconfig create mode 100644 examples/basic/__tests__/App-test.js create mode 100644 examples/basic/android/app/build_defs.bzl create mode 100644 examples/basic/android/app/debug.keystore create mode 100644 examples/basic/android/app/src/debug/AndroidManifest.xml create mode 100644 examples/basic/android/app/src/debug/java/com/videoplayer/ReactNativeFlipper.java create mode 100644 examples/basic/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 examples/basic/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 examples/basic/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 examples/basic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 examples/basic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 examples/basic/android/keystores/BUCK delete mode 100644 examples/basic/android/keystores/debug.keystore.properties create mode 100644 examples/basic/app.json delete mode 100644 examples/basic/ios/AppDelegate.h delete mode 100644 examples/basic/ios/AppDelegate.m delete mode 100644 examples/basic/ios/Base.lproj/LaunchScreen.xib delete mode 100644 examples/basic/ios/Images.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 examples/basic/ios/Info.plist create mode 100644 examples/basic/ios/Podfile create mode 100644 examples/basic/ios/Podfile.lock create mode 100644 examples/basic/ios/VideoPlayer-Bridging-Header.h rename examples/basic/ios/{VideoPlayer-tvOS.plist => VideoPlayer-tvOS/Info.plist} (100%) create mode 100644 examples/basic/ios/VideoPlayer-tvOSTests/Info.plist create mode 100644 examples/basic/ios/VideoPlayer.xcworkspace/contents.xcworkspacedata create mode 100644 examples/basic/ios/VideoPlayer.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 examples/basic/ios/VideoPlayer/Base.lproj/LaunchScreen.xib create mode 100644 examples/basic/ios/VideoPlayer/File.swift create mode 100644 examples/basic/ios/VideoPlayer/Images.xcassets/Contents.json create mode 100644 examples/basic/ios/VideoPlayer/LaunchScreen.storyboard create mode 100644 examples/basic/ios/VideoPlayerTests/Info.plist create mode 100644 examples/basic/ios/VideoPlayerTests/VideoPlayerTests.m delete mode 100644 examples/basic/ios/main.jsbundle delete mode 100644 examples/basic/ios/main.m delete mode 100644 examples/basic/jsconfig.json delete mode 100644 examples/basic/react-native.config.js create mode 100644 examples/basic/windows/VideoPlayer/.gitignore create mode 100644 examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.cpp create mode 100644 examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.h create mode 100644 examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.props create mode 100644 examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.targets delete mode 100644 examples/basic/windows/VideoPlayer/Bundle/.gitignore create mode 100644 examples/basic/windows/VideoPlayer/MainPage.cpp create mode 100644 examples/basic/windows/VideoPlayer/MainPage.h create mode 100644 examples/basic/windows/VideoPlayer/MainPage.idl create mode 100644 examples/basic/windows/VideoPlayer/MainPage.xaml delete mode 100644 examples/basic/windows/VideoPlayer/nativeModules.g.h rename examples/basic/windows/VideoPlayer/{VideoPlayer.vcxproj => videoplayer.vcxproj} (58%) rename examples/basic/windows/VideoPlayer/{VideoPlayer.vcxproj.filters => videoplayer.vcxproj.filters} (90%) create mode 100644 ios/Video/DataStructures/DRMParams.swift create mode 100644 ios/Video/DataStructures/SelectedTrackCriteria.swift create mode 100644 ios/Video/DataStructures/TextTrack.swift create mode 100644 ios/Video/DataStructures/VideoSource.swift create mode 100644 ios/Video/Features/RCTPictureInPicture.swift create mode 100644 ios/Video/Features/RCTPlayerObserver.swift create mode 100644 ios/Video/Features/RCTPlayerOperations.swift create mode 100644 ios/Video/Features/RCTResourceLoaderDelegate.swift create mode 100644 ios/Video/Features/RCTVideoDRM.swift create mode 100644 ios/Video/Features/RCTVideoErrorHandling.swift create mode 100644 ios/Video/Features/RCTVideoSave.swift create mode 100644 ios/Video/Features/RCTVideoUtils.swift create mode 100644 ios/Video/RCTSwiftLog/RCTSwiftLog.h create mode 100644 ios/Video/RCTSwiftLog/RCTSwiftLog.m create mode 100644 ios/Video/RCTSwiftLog/RCTSwiftLog.swift create mode 100644 ios/Video/RCTVideo-Bridging-Header.h delete mode 100644 ios/Video/RCTVideo.h delete mode 100644 ios/Video/RCTVideo.m create mode 100644 ios/Video/RCTVideo.swift delete mode 100644 ios/Video/RCTVideoManager.h create mode 100644 ios/Video/RCTVideoManager.swift delete mode 100644 ios/Video/RCTVideoPlayerViewController.h delete mode 100644 ios/Video/RCTVideoPlayerViewController.m create mode 100644 ios/Video/RCTVideoPlayerViewController.swift delete mode 100644 ios/Video/RCTVideoPlayerViewControllerDelegate.h create mode 100644 ios/Video/RCTVideoPlayerViewControllerDelegate.swift delete mode 100644 ios/Video/UIView+FindUIViewController.h delete mode 100644 ios/Video/UIView+FindUIViewController.m create mode 100644 ios/Video/UIView+FindUIViewController.swift create mode 100644 ios/VideoCaching/RCTVideoCachingHandler.swift diff --git a/.gitignore b/.gitignore index d477ec05..ce0db63c 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ DerivedData *.ipa *.xcuserstate project.xcworkspace +Pods # Android/IJ # @@ -29,6 +30,9 @@ project.xcworkspace .gradle local.properties *.hprof +.project +.settings +.classpath # node.js # @@ -46,3 +50,7 @@ buck-out/ \.buckd/ android/app/libs android/keystores/debug.keystore + +# windows +Deploy.binlog +msbuild.binlog \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a872711..c6e3c265 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ - (Breaking) Bump shaka-player to 3.3.2 [#2587](https://github.com/react-native-video/react-native-video/pull/2587) - Improve basic player example on android [#2662](https://github.com/react-native-video/react-native-video/pull/2662) - Ensure we always use `hideShutterView` before showing the `shutterView` on Android [#2609](https://github.com/react-native-video/react-native-video/pull/2609) +- Convert iOS implementation to Swift [#2527](https://github.com/react-native-video/react-native-video/pull/2527) +- Add iOS support for decoding offline sources [#2527](https://github.com/react-native-video/react-native-video/pull/2527) +- Update basic example applications (React Native 0.63.4) [#2527](https://github.com/react-native-video/react-native-video/pull/2527) ### Version 5.2.0 @@ -223,4 +226,4 @@ - Add cookie support for ExoPlayer [#922](https://github.com/react-native-community/react-native-video/pull/922) - Remove ExoPlayer onMetadata that wasn't being used [#1040](https://github.com/react-native-community/react-native-video/pull/1040) - Fix bug where setting the progress interval on iOS didn't work [#800](https://github.com/react-native-community/react-native-video/pull/800) -- Support setting the poster resize mode [#595](https://github.com/react-native-community/react-native-video/pull/595) +- Support setting the poster resize mode [#595](https://github.com/react-native-community/react-native-video/pull/595) \ No newline at end of file diff --git a/README.md b/README.md index 24e2edb0..345fedb1 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ Version 3.0 features a number of changes to existing behavior. See [Updating](#u * [Android](#android-installation) * [Windows](#windows-installation) * [react-native-dom](#react-native-dom-installation) +* [Examples](#examples) + * [iOS](#ios-example) + * [Android](#android-example) + * [Windows](#windows-example) * [Usage](#usage) * [iOS App Transport Security](#ios-app-transport-security) * [Audio Mixing](#audio-mixing) @@ -69,6 +73,12 @@ Run `npx pod-install`. Linking is not required in React Native 0.60 and above. Run `react-native link react-native-video` to link the react-native-video library. +#### Enable Static Linking for dependencies in your ios project Podfile + +Add `use_frameworks! :linkage => :static` just under `platform :ios` in your ios project Podfile. + +[See the example ios project for reference](examples/basic/ios/Podfile#L5) + #### Using CocoaPods (required to enable caching) Setup your Podfile like it is described in the [react-native documentation](https://facebook.github.io/react-native/docs/integration-with-existing-apps#configuring-cocoapods-dependencies). @@ -166,6 +176,11 @@ android.enableJetifier=true #### **MainApplication.java** +If using com.facebook.react.PackageList to auto import native dependencies, there are no updates required here. Please see the android example project for more details. +/examples/basic/android/app/src/main/java/com/videoplayer/MainApplication.java + +##### For manual linking + On top, where imports are: ```java @@ -257,6 +272,25 @@ const ReactNativeDomOptions = { ``` +## Examples + +Run `yarn xbasic install` before running any of the examples. + +### iOS Example +``` +yarn xbasic ios +``` + +### Android Example +``` +yarn xbasic android +``` + +### Windows Example +``` +yarn xbasic windows +``` + ## Usage ```javascript @@ -926,6 +960,18 @@ Adjust the volume. Platforms: all +#### localSourceEncryptionKeyScheme +Set the url scheme for stream encryption key for local assets + +Type: String + +Example: +``` +localSourceEncryptionKeyScheme="my-offline-key" +``` + +Platforms: iOS + ### Event props @@ -1404,6 +1450,16 @@ To enable audio to play in background on iOS the audio session needs to be set t ## Updating +### Version 6.0.0 + +#### iOS + +In your project Podfile add support for static dependency linking. This is required to support the new Promises subdependency in the iOS swift conversion. + +Add `use_frameworks! :linkage => :static` just under `platform :ios` in your ios project Podfile. + +[See the example ios project for reference](examples/basic/ios/Podfile#L5) + ### Version 5.0.0 Probably you want to update your gradle version: diff --git a/Video.js b/Video.js index d6ea1e08..0c20ddc9 100644 --- a/Video.js +++ b/Video.js @@ -413,6 +413,7 @@ Video.propTypes = { certificateUrl: PropTypes.string, getLicense: PropTypes.func, }), + localSourceEncryptionKeyScheme: PropTypes.string, minLoadRetryCount: PropTypes.number, maxBitRate: PropTypes.number, resizeMode: PropTypes.string, diff --git a/examples/basic/.buckconfig b/examples/basic/.buckconfig deleted file mode 100644 index 934256cb..00000000 --- a/examples/basic/.buckconfig +++ /dev/null @@ -1,6 +0,0 @@ - -[android] - target = Google Inc.:Google APIs:23 - -[maven_repositories] - central = https://repo1.maven.org/maven2 diff --git a/examples/basic/.flowconfig b/examples/basic/.flowconfig deleted file mode 100644 index bb65654d..00000000 --- a/examples/basic/.flowconfig +++ /dev/null @@ -1,58 +0,0 @@ -[ignore] -; We fork some components by platform -.*/*[.]android.js - -# We fork some components by platform. -.*/*[.]android.js - -# Ignore templates with `@flow` in header -.*/local-cli/generator.* - -# Ignore malformed json -.*/node_modules/y18n/test/.*\.json - -# Ignore the website subdir -/website/.* - -# Ignore BUCK generated dirs - -/\.buckd/ - -; Ignore unexpected extra "@providesModule" -.*/node_modules/.*/node_modules/fbjs/.* - -; Ignore duplicate module providers -; For RN Apps installed via npm, "Libraries" folder is inside -; "node_modules/react-native" but in the source repo it is in the root -.*/Libraries/react-native/React.js -.*/Libraries/react-native/ReactNative.js - -[include] - -[libs] -node_modules/react-native/Libraries/react-native/react-native-interface.js -node_modules/react-native/flow -flow/ - -[options] -module.system=haste - -experimental.strict_type_args=true - -munge_underscores=true - -module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' - -suppress_type=$FlowIssue -suppress_type=$FlowFixMe -suppress_type=$FixMe - -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-6]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-6]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ -suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy - -unsafe.enable_getters_and_setters=true - -[version] - -^0.36.0 diff --git a/examples/basic/.gitattributes b/examples/basic/.gitattributes deleted file mode 100644 index d42ff183..00000000 --- a/examples/basic/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.pbxproj -text diff --git a/examples/basic/.gitignore b/examples/basic/.gitignore deleted file mode 100644 index e9ec10b5..00000000 --- a/examples/basic/.gitignore +++ /dev/null @@ -1,55 +0,0 @@ -# OSX -# -.DS_Store - -# Xcode -# -build/ -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.xcuserstate -project.xcworkspace - -# Android/IntelliJ -# -build/ -.idea -.gradle -local.properties -*.iml - -# node.js -# -node_modules/ -npm-debug.log - -# BUCK -buck-out/ -\.buckd/ -android/app/libs -*.keystore - -# fastlane -# -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the -# screenshots whenever they are needed. -# For more information about the recommended setup visit: -# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md - -fastlane/report.xml -fastlane/Preview.html -fastlane/screenshots - -*.binlog diff --git a/examples/basic/.watchmanconfig b/examples/basic/.watchmanconfig deleted file mode 100644 index 9e26dfee..00000000 --- a/examples/basic/.watchmanconfig +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/examples/basic/__tests__/App-test.js b/examples/basic/__tests__/App-test.js new file mode 100644 index 00000000..d9297fbe --- /dev/null +++ b/examples/basic/__tests__/App-test.js @@ -0,0 +1,14 @@ +/** + * @format + */ + +import 'react-native'; +import React from 'react'; +import VideoPlayer from '../src/VideoPlayer'; + +// Note: test renderer must be required after react-native. +import renderer from 'react-test-renderer'; + +it('renders correctly', () => { + renderer.create(); +}); diff --git a/examples/basic/android/app/BUCK b/examples/basic/android/app/BUCK index 41c624bb..96fa2d68 100644 --- a/examples/basic/android/app/BUCK +++ b/examples/basic/android/app/BUCK @@ -1,5 +1,3 @@ -import re - # To learn about Buck see [Docs](https://buckbuild.com/). # To run your application with Buck: # - install Buck @@ -10,57 +8,48 @@ import re # - `buck install -r android/app` - compile, install and run application # -lib_deps = [] -for jarfile in glob(['libs/*.jar']): - name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile) - lib_deps.append(':' + name) - prebuilt_jar( - name = name, - binary_jar = jarfile, - ) +load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") -for aarfile in glob(['libs/*.aar']): - name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile) - lib_deps.append(':' + name) - android_prebuilt_aar( - name = name, - aar = aarfile, - ) +lib_deps = [] + +create_aar_targets(glob(["libs/*.aar"])) + +create_jar_targets(glob(["libs/*.jar"])) android_library( - name = 'all-libs', - exported_deps = lib_deps + name = "all-libs", + exported_deps = lib_deps, ) android_library( - name = 'app-code', - srcs = glob([ - 'src/main/java/**/*.java', - ]), - deps = [ - ':all-libs', - ':build_config', - ':res', - ], + name = "app-code", + srcs = glob([ + "src/main/java/**/*.java", + ]), + deps = [ + ":all-libs", + ":build_config", + ":res", + ], ) android_build_config( - name = 'build_config', - package = 'com.videoplayer', + name = "build_config", + package = "com.videoplayer", ) android_resource( - name = 'res', - res = 'src/main/res', - package = 'com.videoplayer', + name = "res", + package = "com.videoplayer", + res = "src/main/res", ) android_binary( - name = 'app', - package_type = 'debug', - manifest = 'src/main/AndroidManifest.xml', - keystore = '//android/keystores:debug', - deps = [ - ':app-code', - ], + name = "app", + keystore = "//android/keystores:debug", + manifest = "src/main/AndroidManifest.xml", + package_type = "debug", + deps = [ + ":app-code", + ], ) diff --git a/examples/basic/android/app/build.gradle b/examples/basic/android/app/build.gradle index aa7ccf61..31a0e7d1 100644 --- a/examples/basic/android/app/build.gradle +++ b/examples/basic/android/app/build.gradle @@ -15,9 +15,14 @@ import com.android.build.OutputFile * // the name of the generated asset file containing your JS bundle * bundleAssetName: "index.android.bundle", * - * // the entry file for bundle generation + * // the entry file for bundle generation. If none specified and + * // "index.android.js" exists, it will be used. Otherwise "index.js" is + * // default. Can be overridden with ENTRY_FILE environment variable. * entryFile: "index.android.js", * + * // https://reactnative.dev/docs/performance#enable-the-ram-format + * bundleCommand: "ram-bundle", + * * // whether to bundle JS and assets in debug mode * bundleInDebug: false, * @@ -33,6 +38,13 @@ import com.android.build.OutputFile * // bundleInPaidRelease: true, * // bundleInBeta: true, * + * // whether to disable dev mode in custom build variants (by default only disabled in release) + * // for example: to disable dev mode in the staging build type (if configured) + * devDisabledInStaging: true, + * // The configuration property can be in the following formats + * // 'devDisabledIn${productFlavor}${buildType}' + * // 'devDisabledIn${buildType}' + * * // the root of your project, i.e. where "package.json" lives * root: "../../", * @@ -58,7 +70,7 @@ import com.android.build.OutputFile * inputExcludes: ["android/**", "ios/**"], * * // override which node gets called and with what additional arguments - * nodeExecutableAndArgs: ["node"] + * nodeExecutableAndArgs: ["node"], * * // supply additional arguments to the packager * extraPackagerArgs: [] @@ -66,9 +78,9 @@ import com.android.build.OutputFile */ project.ext.react = [ - entryFile: "index.js", - enableHermes: false, + enableHermes: false, // clean and rebuild if changing ] + apply from: "../../node_modules/react-native/react.gradle" /** @@ -86,60 +98,117 @@ def enableSeparateBuildPerCPUArchitecture = false */ def enableProguardInReleaseBuilds = false +/** + * The preferred build flavor of JavaScriptCore. + * + * For example, to use the international variant, you can use: + * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` + * + * The international variant includes ICU i18n library and necessary data + * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that + * give correct results when using with locales other than en-US. Note that + * this variant is about 6MiB larger per architecture than default. + */ +def jscFlavor = 'org.webkit:android-jsc:+' + +/** + * Whether to enable the Hermes VM. + * + * This should be set on project.ext.react and mirrored here. If it is not set + * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode + * and the benefits of using Hermes will therefore be sharply reduced. + */ +def enableHermes = project.ext.react.get("enableHermes", false); + android { compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion rootProject.ext.buildToolsVersion + compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + defaultConfig { applicationId "com.videoplayer" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - multiDexEnabled true versionCode 1 versionName "1.0" - ndk { - abiFilters "armeabi-v7a", "x86" - } + multiDexEnabled true + } splits { abi { reset() enable enableSeparateBuildPerCPUArchitecture universalApk false // If true, also generate a universal APK - include "armeabi-v7a", "x86" + include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" + } + } + signingConfigs { + debug { + storeFile file('debug.keystore') + storePassword 'android' + keyAlias 'androiddebugkey' + keyPassword 'android' } } buildTypes { + debug { + signingConfig signingConfigs.debug + } release { + // Caution! In production, you need to generate your own keystore file. + // see https://reactnative.dev/docs/signed-apk-android. signingConfig signingConfigs.debug minifyEnabled enableProguardInReleaseBuilds - matchingFallbacks = ['release', 'debug'] proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" } } + // applicationVariants are e.g. debug, release applicationVariants.all { variant -> variant.outputs.each { output -> // For each separate APK per architecture, set a unique version code as described here: - // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits - def versionCodes = ["armeabi-v7a":1, "x86":2] + // https://developer.android.com/studio/build/configure-apk-splits.html + def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] def abi = output.getFilter(OutputFile.ABI) if (abi != null) { // null for the universal-debug, universal-release variants output.versionCodeOverride = versionCodes.get(abi) * 1048576 + defaultConfig.versionCode } + } } } dependencies { - implementation project(':react-native-video') - implementation 'com.android.support:multidex:1.0.3' + implementation fileTree(dir: "libs", include: ["*.jar"]) + //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" // From node_modules - implementation 'org.webkit:android-jsc:+' + + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" + + debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { + exclude group:'com.facebook.fbjni' + } + + debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { + exclude group:'com.facebook.flipper' + exclude group:'com.squareup.okhttp3', module:'okhttp' + } + + debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { + exclude group:'com.facebook.flipper' + } + + if (enableHermes) { + def hermesPath = "../../node_modules/hermes-engine/android/"; + debugImplementation files(hermesPath + "hermes-debug.aar") + releaseImplementation files(hermesPath + "hermes-release.aar") + } else { + implementation jscFlavor + } } // Run this once to be able to run the application with BUCK diff --git a/examples/basic/android/app/build_defs.bzl b/examples/basic/android/app/build_defs.bzl new file mode 100644 index 00000000..fff270f8 --- /dev/null +++ b/examples/basic/android/app/build_defs.bzl @@ -0,0 +1,19 @@ +"""Helper definitions to glob .aar and .jar targets""" + +def create_aar_targets(aarfiles): + for aarfile in aarfiles: + name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] + lib_deps.append(":" + name) + android_prebuilt_aar( + name = name, + aar = aarfile, + ) + +def create_jar_targets(jarfiles): + for jarfile in jarfiles: + name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] + lib_deps.append(":" + name) + prebuilt_jar( + name = name, + binary_jar = jarfile, + ) diff --git a/examples/basic/android/app/debug.keystore b/examples/basic/android/app/debug.keystore new file mode 100644 index 0000000000000000000000000000000000000000..364e105ed39fbfd62001429a68140672b06ec0de GIT binary patch literal 2257 zcmchYXEfYt8;7T1^dLH$VOTZ%2NOdOH5j5LYLtZ0q7x-V8_6gU5)#7dkq{HTmsfNq zB3ZqcAxeY^G10@?efK?Q&)M(qInVv!xjx+IKEL}p*K@LYvIzo#AZG>st5|P)KF1_Z;y){W{<7K{nl!CPuE z_^(!C(Ol0n8 zK13*rzAtW>(wULKPRYLd7G18F8#1P`V*9`(Poj26eOXYyBVZPno~Cvvhx7vPjAuZo zF?VD!zB~QG(!zbw#qsxT8%BSpqMZ4f70ZPn-3y$L8{EVbbN9$H`B&Z1quk9tgp5FM zuxp3pJ0b8u|3+#5bkJ4SRnCF2l7#DyLYXYY8*?OuAwK4E6J{0N=O3QNVzQ$L#FKkR zi-c@&!nDvezOV$i$Lr}iF$XEcwnybQ6WZrMKuw8gCL^U#D;q3t&HpTbqyD%vG=TeDlzCT~MXUPC|Leb-Uk+ z=vnMd(|>ld?Fh>V8poP;q;;nc@en$|rnP0ytzD&fFkCeUE^kG9Kx4wUh!!rpjwKDP zyw_e|a^x_w3E zP}}@$g>*LLJ4i0`Gx)qltL}@;mDv}D*xR^oeWcWdPkW@Uu)B^X&4W1$p6}ze!zudJ zyiLg@uggoMIArBr*27EZV7djDg@W1MaL+rcZ-lrANJQ%%>u8)ZMWU@R2qtnmG(acP z0d_^!t>}5W zpT`*2NR+0+SpTHb+6Js4b;%LJB;B_-ChhnU5py}iJtku*hm5F0!iql8Hrpcy1aYbT z1*dKC5ua6pMX@@iONI?Hpr%h;&YaXp9n!ND7-=a%BD7v&g zOO41M6EbE24mJ#S$Ui0-brR5ML%@|ndz^)YLMMV1atna{Fw<;TF@>d&F|!Z>8eg>>hkFrV)W+uv=`^F9^e zzzM2*oOjT9%gLoub%(R57p-`TXFe#oh1_{&N-YN z<}artH|m=d8TQuKSWE)Z%puU|g|^^NFwC#N=@dPhasyYjoy(fdEVfKR@cXKHZV-`06HsP`|Ftx;8(YD$fFXumLWbGnu$GMqRncXYY9mwz9$ap zQtfZB^_BeNYITh^hA7+(XNFox5WMeG_LtJ%*Q}$8VKDI_p8^pqX)}NMb`0e|wgF7D zuQACY_Ua<1ri{;Jwt@_1sW9zzdgnyh_O#8y+C;LcZq6=4e^cs6KvmK@$vVpKFGbQ= z$)Eux5C|Fx;Gtmv9^#Y-g@7Rt7*eLp5n!gJmn7&B_L$G?NCN`AP>cXQEz}%F%K;vUs{+l4Q{}eWW;ATe2 zqvXzxoIDy(u;F2q1JH7Sf;{jy_j})F+cKlIOmNfjBGHoG^CN zM|Ho&&X|L-36f}Q-obEACz`sI%2f&k>z5c$2TyTSj~vmO)BW~+N^kt`Jt@R|s!){H ze1_eCrlNaPkJQhL$WG&iRvF*YG=gXd1IyYQ9ew|iYn7r~g!wOnw;@n42>enAxBv*A zEmV*N#sxdicyNM=A4|yaOC5MByts}s_Hpfj|y<6G=o=!3S@eIFKDdpR7|FY>L&Wat&oW&cm&X~ z5Bt>Fcq(fgnvlvLSYg&o6>&fY`ODg4`V^lWWD=%oJ#Kbad2u~! zLECFS*??>|vDsNR&pH=Ze0Eo`sC_G`OjoEKVHY|wmwlX&(XBE<@sx3Hd^gtd-fNwUHsylg06p`U2y_={u}Bc + + + + + + diff --git a/examples/basic/android/app/src/debug/java/com/videoplayer/ReactNativeFlipper.java b/examples/basic/android/app/src/debug/java/com/videoplayer/ReactNativeFlipper.java new file mode 100644 index 00000000..05c77d34 --- /dev/null +++ b/examples/basic/android/app/src/debug/java/com/videoplayer/ReactNativeFlipper.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + *

This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + */ +package com.videoplayer; + +import android.content.Context; +import com.facebook.flipper.android.AndroidFlipperClient; +import com.facebook.flipper.android.utils.FlipperUtils; +import com.facebook.flipper.core.FlipperClient; +import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; +import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; +import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; +import com.facebook.flipper.plugins.inspector.DescriptorMapping; +import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; +import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; +import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; +import com.facebook.flipper.plugins.react.ReactFlipperPlugin; +import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; +import com.facebook.react.ReactInstanceManager; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.modules.network.NetworkingModule; +import okhttp3.OkHttpClient; + +public class ReactNativeFlipper { + public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { + if (FlipperUtils.shouldEnableFlipper(context)) { + final FlipperClient client = AndroidFlipperClient.getInstance(context); + + client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); + client.addPlugin(new ReactFlipperPlugin()); + client.addPlugin(new DatabasesFlipperPlugin(context)); + client.addPlugin(new SharedPreferencesFlipperPlugin(context)); + client.addPlugin(CrashReporterPlugin.getInstance()); + + NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); + NetworkingModule.setCustomClientBuilder( + new NetworkingModule.CustomClientBuilder() { + @Override + public void apply(OkHttpClient.Builder builder) { + builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); + } + }); + client.addPlugin(networkFlipperPlugin); + client.start(); + + // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized + // Hence we run if after all native modules have been initialized + ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); + if (reactContext == null) { + reactInstanceManager.addReactInstanceEventListener( + new ReactInstanceManager.ReactInstanceEventListener() { + @Override + public void onReactContextInitialized(ReactContext reactContext) { + reactInstanceManager.removeReactInstanceEventListener(this); + reactContext.runOnNativeModulesQueueThread( + new Runnable() { + @Override + public void run() { + client.addPlugin(new FrescoFlipperPlugin()); + } + }); + } + }); + } else { + client.addPlugin(new FrescoFlipperPlugin()); + } + } + } +} diff --git a/examples/basic/android/app/src/main/AndroidManifest.xml b/examples/basic/android/app/src/main/AndroidManifest.xml index 4f434091..4ea9eb2a 100644 --- a/examples/basic/android/app/src/main/AndroidManifest.xml +++ b/examples/basic/android/app/src/main/AndroidManifest.xml @@ -1,22 +1,23 @@ + package="com.videoplayer"> - + android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" + android:launchMode="singleTask" + android:windowSoftInputMode="adjustResize"> diff --git a/examples/basic/android/app/src/main/java/com/videoplayer/MainActivity.java b/examples/basic/android/app/src/main/java/com/videoplayer/MainActivity.java index 7045aa12..2ed27c34 100644 --- a/examples/basic/android/app/src/main/java/com/videoplayer/MainActivity.java +++ b/examples/basic/android/app/src/main/java/com/videoplayer/MainActivity.java @@ -4,12 +4,12 @@ import com.facebook.react.ReactActivity; public class MainActivity extends ReactActivity { - /** - * Returns the name of the main component registered from JavaScript. - * This is used to schedule rendering of the component. - */ - @Override - protected String getMainComponentName() { - return "VideoPlayer"; - } + /** + * Returns the name of the main component registered from JavaScript. This is used to schedule + * rendering of the component. + */ + @Override + protected String getMainComponentName() { + return "VideoPlayer"; + } } diff --git a/examples/basic/android/app/src/main/java/com/videoplayer/MainApplication.java b/examples/basic/android/app/src/main/java/com/videoplayer/MainApplication.java index c017e91d..c3db7f87 100644 --- a/examples/basic/android/app/src/main/java/com/videoplayer/MainApplication.java +++ b/examples/basic/android/app/src/main/java/com/videoplayer/MainApplication.java @@ -1,21 +1,17 @@ package com.videoplayer; import android.app.Application; - -import androidx.multidex.MultiDexApplication; +import android.content.Context; +import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; -import com.brentvatne.react.ReactVideoPackage; +import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; -import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; - -import com.reactnativecommunity.picker.RNCPickerPackage; - -import java.util.Arrays; +import java.lang.reflect.InvocationTargetException; import java.util.List; -public class MainApplication extends MultiDexApplication implements ReactApplication { +public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override @@ -25,11 +21,12 @@ public class MainApplication extends MultiDexApplication implements ReactApplica @Override protected List getPackages() { - return Arrays.asList( - new MainReactPackage(), - new ReactVideoPackage(), - new RNCPickerPackage() - ); + @SuppressWarnings("UnnecessaryLocalVariable") + List packages = new PackageList(this).getPackages(); + // Packages that cannot be autolinked yet can be added manually here, for + // example: + // packages.add(new MyReactNativePackage()); + return packages; } @Override diff --git a/examples/basic/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/basic/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index cde69bcccec65160d92116f20ffce4fce0b5245c..a2f5908281d070150700378b64a84c7db1f97aa1 100644 GIT binary patch delta 3051 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 tKEN0F)D!;>WXK>I89 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 O0000004R>004l5008;`004mK z004C`008P>0026e000+ooVrmw00006VoOIv00000008+zyMF)x010qNS#tmY3ljhU z3ljkVnw%H_00*E+L_t(&-tAdkY!ufO{?475^{#iq-c;^Dmy#{tlF zn2UDq+?oI&B7XskqVt14KD48|Z|2gspW4!NjY-f0yVrcCZHvtAf9?KnMZp(|!29oH z21uZ${mz;f0I1iHfV!f0?jb3Sns?v0Fc8I9Q3%Mx2i|z^lXDZt;&r^?splVDAE6(G ze9B_k_1t~y<7uqB@b`&Ve|zw~(*U>@6A1>4^+}vU}5!#{kHC?rGQ7Y+rpxY>wQfCgoNV z##{y$v463{hy((3%FgE0mkO8pmxuQL_0*X&X9@tszTe&dY^o8DoH+W)6TkU!{|f-d zucik8K#j-at$U7ac&)yrw(G(v+1D^%X-_oKexc~|z&2o8`~(1inTzdgD5K*U-Ze2| z47GOCTVo$ho!tAtu}uIn018np8&wEEqPutLU4PpTbRPo<4CzExH39p7vh;rX*vY=L z?+*0?n2HKYsfNJv8hbJGL1hvZ>p(|GM{LcbZNFD*c)4xWqGdTKXsRIOeX_>WQs{Z` z@x`woLeZ>5K-qqvXHR@C^}2wEf_H+uvOKW+ChB;@_g>z#6BVMQTqEszc57GTXT=d8 zL1ChYk%#g27yiTmWxn;r8 zQ^yC#!p1Gx+V$jfPz7dN?& zCr=};q>z_WI5qkzE{vZH?WxAmmVejkpF4l@<4IpzfWBz!O0KH};#~Fw&Sg&&trVyo zP$TR&IeiSrhks!!mK_HGZtqI>y$u}pS|TWfltiO?UnyXmA1@e=2;RHZa4Cj@cGjC@ zv)B@3@E`(OGDTks8_@YLbm9Q;-jPDVhRB?`T5lu*D^_&WYH_M%aAd$v;D2G@1Iu2S z(D7D#rADNeE^SFvB0#O3^{I+TL%@ewx!TYPR0mMEqM;U^<<#ercuI}svV~zok`QFL zlqDjDm@E@z_FOmem(+`3@1;Dc^JK9vWG9aTVR))dSR>%&?)-GA1XUaMCFz8VkYb)BPL zi72XxP8O`x_T&K-mrdI%c0L3FF9Y5ptLw9s3t2#QFHb@-Yl4)e&>=fXsTBJprL&@y zjrSn%Y!B`cUkK>|`;AH>RBN+kZ86NHwX}lR;OSF=2lT?M^v&&k@wYz>U7FQ(-{Cn( zoQEA!7>Z(V7=PIHZG-n8OxZV982FhPIq{cc=P!FP_--Y}$7e|z+CLU{roXIe!oPnl zBV=vZ?1hKPT=&@#I#I3e)XGijldu15;2R&jf9Wg$-94vY6ae*_hMN~IYi?2$<%(A- z!zf(w1*u^eY=4iO{clFs@ezP509P1=rK|u@gKPUI)qkkMF#p_?_ku4+AZq*dBymIj zFXewDA7BDFxU!7^001R)MObuXVRU6WV{&C-bY%cCFflYOFgYzSHB>P$IyE;sH8?FW zH##sdyvITA0000bbVXQnWMOn=I&E)cX=ZrgFgH3d VFwL6zQVak9002ovPDHLkV1l)_3F80& diff --git a/examples/basic/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/examples/basic/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..115a4c768a20c9e13185c17043f4c4d12dd4632a GIT binary patch literal 2858 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⁣ zwiG+x#%j}{0gU~J8;<|!B1@-XaB@{KORFwrYg_8rOv({b0EO#DbeQRm;B6_9=mXGf z-x|VL{zd`)#@yN}HkCSJbjbNlE|zL3Wm9Q8HY`sV)}3%pgN>cL^67{Z;PPL(*wT8N zUjXU{@|*hvm}({wsAC=x0^ok0%UAz0;sogW{B!nDqk|JJ5x~4NfTDgP49^zeu`csl?5mY@JdQdISc zFs!E{^grmkLnUk9 zny~m)1vws@5BFI<-0Tuo2JWX(0v`W|t(wg;s--L47WTvTMz-8l#TL^=OJNRS2?_Qj z3AKT+gvbyBi#H*-tJ%tWD|>EV3wy|8qxfzS!5RW;Jpl5*zo&^UBU=fG#2}UvRyNkK zA06Dy9;K1ca@r2T>yThYgI!ont$(G{6q#2QT+00r_x0(b)gsE`lBB?2gr55gq^D3Fi&p%E(p9>U%bv zkg1Jco(RbyTX7FDHOnl7-O@ zI$AaIl?9NJKPm(WiBP`1-#CB1QzU>&hKm)fpa5DKE{2$X0hGz-0uZ?cyTk(YC!Y&| zL=1VrNERSA5NA2jq7FACfX4JfPyj5XXl1yv0>~s;eF7L2$>&oMqeTFT2m$y7FlkON z_yurD1yIOvA;5C6016pyxBznGUt0kJ&k5r#;&>Jow`r)sp9R~PmK~lz$3xH%LT*1U zJdOyABZ3!FvNoR*vN$5ykHS8f`jA4zV+|L}i1C4`B2c{R0;UdYxaU|H)2avz@ z=mEYc|2S<+(B2Tj+FkX+2D+yFI!k9lWMA61DJ{)e;lum$(;O87?vGJJe!KtK04+N_ zI*P~t@dUb>9Xh{dbyl{-ZQ(UMgz7$|QfL5XSPkskt^NgctYC#;4WcZB1@%@wy@2t3 z2z0DI7&%b$*Aw~abe?GxE`ez@+6hOh-6*8fHRV{1os$EL@}uUZeG4h1&Be`98q*7j z=3-v+lhIjfWVo12!<>%V^a6lTgW3+_#W6n|p*~==zOH7z$0{LSZk(Tpd7EaD04hnA zL;#fxS0aD{`5^&D`}>0Uq?byDD-l2=!wm_bLcUl4gc(% za1p|itVANvFF>hghAS07Im1;IK;|b*W)}VDyI;BIp2=K*yu2a)j?B|f<44NI$NbmJ z#dE0>jI$fMr&@>4kN8MLFb4&2O9fEKaQg%(QO$4_1rVQywG^CmBLh#}_7gKW3vd?| z2?1^&KWq8}8I^_S0|)MowU_pw$q@nl@Nkn$z>BQq_KA^9yaR`(R3u{{Ig;cwt z@AJ^{ODQCm^neroM9nKNUAXi9RCK`OsP_LuR0PUR(YZCCX5dNF6VzcoK&=b^r`W?ltt|*F zpkoae%ZT{C1h~EcFui~b7fF`vb<<~j_VquuUA$}QqIKYELPp#;{u?q8Dz}WAG-(3; zjrm$i%7UbyZMM(Y{>!uJ#vNB?R~B{6Htp=>e*<{fQQ5W7V(1coCWlOON!MzZxhum| ztZBQpGR z;~#ur^&PockKdV{Q6R>o`Pl{0x!DEbpZ7y9Y;*ZvE!*gU`V1W3znva{f=?WO5I&>B z&hw6}tjECtaghm5z|C#%M;Yf_*pI^};h}Vl=^r9EN=tVDj86D;C$jIJ?K7VP+00000NkvXXu0mjf D5i!M* literal 4842 zcmZ{oXE5C1x5t0WvTCfdv7&7fy$d2l*k#q|U5FAbL??P!61}%ovaIM)mL!5G(V|6J zAtDH(OY|Du^}l!K&fFLG%sJ2JIp@rG=9y>Ci)Wq~U2RobsvA@Q0MM$dq4lq5{hy#9 zzgp+B{O(-=?1<7r0l>Q?>N6X%s~lmgrmqD6fjj_!c?AF`S0&6U06Z51fWOuNAe#jM z%pSN#J-Mp}`ICpL=qp~?u~Jj$6(~K_%)9}Bn(;pY0&;M00H9x2N23h=CpR7kr8A9X zU%oh4-E@i!Ac}P+&%vOPQ3warO9l!SCN)ixGW54Jsh!`>*aU)#&Mg7;#O_6xd5%I6 zneGSZL3Kn-4B^>#T7pVaIHs3^PY-N^v1!W=%gzfioIWosZ!BN?_M)OOux&6HCyyMf z3ToZ@_h75A33KyC!T)-zYC-bp`@^1n;w3~N+vQ0#4V7!f|JPMlWWJ@+Tg~8>1$GzLlHGuxS)w&NAF*&Y;ef`T^w4HP7GK%6UA8( z{&ALM(%!w2U7WFWwq8v4H3|0cOjdt7$JLh(;U8VcTG;R-vmR7?21nA?@@b+XPgJbD z*Y@v&dTqo5Bcp-dIQQ4@?-m{=7>`LZ{g4jvo$CE&(+7(rp#WShT9&9y>V#ikmXFau03*^{&d(AId0Jg9G;tc7K_{ivzBjqHuJx08cx<8U`z2JjtOK3( zvtuduBHha>D&iu#))5RKXm>(|$m=_;e?7ZveYy=J$3wjL>xPCte-MDcVW<;ng`nf= z9);CVVZjI-&UcSAlhDB{%0v$wPd=w6MBwsVEaV!hw~8G(rs`lw@|#AAHbyA&(I-7Y zFE&1iIGORsaskMqSYfX33U%&17oTszdHPjr&Sx(`IQzoccST*}!cU!ZnJ+~duBM6f z{Lf8PITt%uWZ zTY09Jm5t<2+Un~yC-%DYEP>c-7?=+|reXO4Cd^neCQ{&aP@yODLN8}TQAJ8ogsnkb zM~O>~3&n6d+ee`V_m@$6V`^ltL&?uwt|-afgd7BQ9Kz|g{B@K#qQ#$o4ut`9lQsYfHofccNoqE+`V zQ&UXP{X4=&Z16O_wCk9SFBQPKyu?<&B2zDVhI6%B$12c^SfcRYIIv!s1&r|8;xw5t zF~*-cE@V$vaB;*+91`CiN~1l8w${?~3Uy#c|D{S$I? zb!9y)DbLJ3pZ>!*+j=n@kOLTMr-T2>Hj^I~lml-a26UP1_?#!5S_a&v zeZ86(21wU0)4(h&W0iE*HaDlw+-LngX=}es#X$u*1v9>qR&qUGfADc7yz6$WN`cx9 zzB#!5&F%AK=ed|-eV6kb;R>Atp2Rk=g3lU6(IVEP3!;0YNAmqz=x|-mE&8u5W+zo7 z-QfwS6uzp9K4wC-Te-1~u?zPb{RjjIVoL1bQ=-HK_a_muB>&3I z*{e{sE_sI$CzyK-x>7abBc+uIZf?#e8;K_JtJexgpFEBMq92+Fm0j*DziUMras`o= zTzby8_XjyCYHeE@q&Q_7x?i|V9XY?MnSK;cLV?k>vf?!N87)gFPc9#XB?p)bEWGs$ zH>f$8?U7In{9@vsd%#sY5u!I$)g^%ZyutkNBBJ0eHQeiR5!DlQbYZJ-@09;c?IP7A zx>P=t*xm1rOqr@ec>|ziw@3e$ymK7YSXtafMk30i?>>1lC>LLK1~JV1n6EJUGJT{6 zWP4A(129xkvDP09j<3#1$T6j6$mZaZ@vqUBBM4Pi!H>U8xvy`bkdSNTGVcfkk&y8% z=2nfA@3kEaubZ{1nwTV1gUReza>QX%_d}x&2`jE*6JZN{HZtXSr{{6v6`r47MoA~R zejyMpeYbJ$F4*+?*=Fm7E`S_rUC0v+dHTlj{JnkW-_eRa#9V`9o!8yv_+|lB4*+p1 zUI-t)X$J{RRfSrvh80$OW_Wwp>`4*iBr|oodPt*&A9!SO(x|)UgtVvETLuLZ<-vRp z&zAubgm&J8Pt647V?Qxh;`f6E#Zgx5^2XV($YMV7;Jn2kx6aJn8T>bo?5&;GM4O~| zj>ksV0U}b}wDHW`pgO$L@Hjy2`a)T}s@(0#?y3n zj;yjD76HU&*s!+k5!G4<3{hKah#gBz8HZ6v`bmURyDi(wJ!C7+F%bKnRD4=q{(Fl0 zOp*r}F`6~6HHBtq$afFuXsGAk58!e?O(W$*+3?R|cDO88<$~pg^|GRHN}yml3WkbL zzSH*jmpY=`g#ZX?_XT`>-`INZ#d__BJ)Ho^&ww+h+3>y8Z&T*EI!mtgEqiofJ@5&E z6M6a}b255hCw6SFJ4q(==QN6CUE3GYnfjFNE+x8T(+J!C!?v~Sbh`Sl_0CJ;vvXsP z5oZRiPM-Vz{tK(sJM~GI&VRbBOd0JZmGzqDrr9|?iPT(qD#M*RYb$>gZi*i)xGMD`NbmZt;ky&FR_2+YqpmFb`8b`ry;}D+y&WpUNd%3cfuUsb8 z7)1$Zw?bm@O6J1CY9UMrle_BUM<$pL=YI^DCz~!@p25hE&g62n{j$?UsyYjf#LH~b z_n!l6Z(J9daalVYSlA?%=mfp(!e+Hk%%oh`t%0`F`KR*b-Zb=7SdtDS4`&&S@A)f>bKC7vmRWwT2 zH}k+2Hd7@>jiHwz^GrOeU8Y#h?YK8>a*vJ#s|8-uX_IYp*$9Y=W_Edf%$V4>w;C3h z&>ZDGavV7UA@0QIQV$&?Z_*)vj{Q%z&(IW!b-!MVDGytRb4DJJV)(@WG|MbhwCx!2 z6QJMkl^4ju9ou8Xjb*pv=Hm8DwYsw23wZqQFUI)4wCMjPB6o8yG7@Sn^5%fmaFnfD zSxp8R-L({J{p&cR7)lY+PA9#8Bx87;mB$zXCW8VDh0&g#@Z@lktyArvzgOn&-zerA zVEa9h{EYvWOukwVUGWUB5xr4{nh}a*$v^~OEasKj)~HyP`YqeLUdN~f!r;0dV7uho zX)iSYE&VG67^NbcP5F*SIE@T#=NVjJ1=!Mn!^oeCg1L z?lv_%(ZEe%z*pGM<(UG{eF1T(#PMw}$n0aihzGoJAP^UceQMiBuE8Y`lZ|sF2_h_6 zQw*b*=;2Ey_Flpfgsr4PimZ~8G~R(vU}^Zxmri5)l?N>M_dWyCsjZw<+a zqjmL0l*}PXNGUOh)YxP>;ENiJTd|S^%BARx9D~%7x?F6u4K(Bx0`KK2mianotlX^9 z3z?MW7Coqy^ol0pH)Z3+GwU|Lyuj#7HCrqs#01ZF&KqEg!olHc$O#Wn>Ok_k2`zoD z+LYbxxVMf<(d2OkPIm8Xn>bwFsF6m8@i7PA$sdK~ZA4|ic?k*q2j1YQ>&A zjPO%H@H(h`t+irQqx+e)ll9LGmdvr1zXV;WTi}KCa>K82n90s|K zi`X}C*Vb12p?C-sp5maVDP5{&5$E^k6~BuJ^UxZaM=o+@(LXBWChJUJ|KEckEJTZL zI2K&Nd$U65YoF3_J6+&YU4uKGMq2W6ZQ%BG>4HnIM?V;;Ohes{`Ucs56ue^7@D7;4 z+EsFB)a_(%K6jhxND}n!UBTuF3wfrvll|mp7)3wi&2?LW$+PJ>2)2C-6c@O&lKAn zOm=$x*dn&dI8!QCb(ul|t3oDY^MjHqxl~lp{p@#C%Od-U4y@NQ4=`U!YjK$7b=V}D z%?E40*f8DVrvV2nV>`Z3f5yuz^??$#3qR#q6F($w>kmKK`x21VmX=9kb^+cPdBY2l zGkIZSf%C+`2nj^)j zo}g}v;5{nk<>%xj-2OqDbJ3S`7|tQWqdvJdgiL{1=w0!qS9$A`w9Qm7>N0Y*Ma%P_ zr@fR4>5u{mKwgZ33Xs$RD6(tcVH~Mas-87Fd^6M6iuV^_o$~ql+!eBIw$U)lzl`q9 z=L6zVsZzi0IIW=DT&ES9HajKhb5lz4yQxT-NRBLv_=2sn7WFX&Wp6Y!&}P+%`!A;s zrCwXO3}jrdA7mB`h~N~HT64TM{R$lNj*~ekqSP^n9P~z;P zWPlRPz0h6za8-P>!ARb+A1-r>8VF*xhrGa8W6J$p*wy`ULrD$CmYV7Gt^scLydQWbo7XN-o9X1i7;l+J_8Ncu zc=EX&dg`GRo4==cz2d_Rz28oLS`Suf6OCp~f{0-aQ`t5YZ=!CAMc6-RZw#}A%;s44 znf2`6gcgm=0SezTH9h+JzeR3Lcm;8?*@+?FDfguK^9)z(Z`I!RKrSAI?H~4et6GTkz07Qgq4B6%Q*8Y0yPc4x z8(^YwtZjYIeOvVLey#>@$UzIciJ#x0pJLFg=8UaZv%-&?Yzp7gWNIo_x^(d75=x2c zv|LQ`HrKP(8TqFxTiP5gdT2>aTN0S7XW*pilASS$UkJ2*n+==D)0mgTGxv43t61fr z47GkfMnD-zSH@|mZ26r*d3WEtr+l-xH@L}BM)~ThoMvKqGw=Ifc}BdkL$^wC}=(XSf4YpG;sA9#OSJf)V=rs#Wq$?Wj+nTlu$YXn yn3SQon5>kvtkl(BT2@T#Mvca!|08g9w{vm``2PjZHg=b<1c17-HkzPl9sXa)&-Ts$ diff --git a/examples/basic/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/examples/basic/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..459ca609d3ae0d3943ab44cdc27feef9256dc6d7 GIT binary patch literal 7098 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 literal 0 HcmV?d00001 diff --git a/examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 324e72cdd7480cb983fa1bcc7ce686e51ef87fe7..8ca12fe024be86e868d14e91120a6902f8e88ac6 100644 GIT binary patch literal 6464 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 B0*L?s literal 7718 zcmZ{JWl)?=u?hpbj?h-6mfK3P*Eck~k0Tzeg5-hkABxtZea0_k$f-mlF z0S@Qqtva`>x}TYzc}9LrO?P#qj+P1@HZ?W?0C;Muih9o&|G$cb@ocx1*PEUJ%~tM} z901hB;rx4#{@jOHs_MN00ADr$2n+#$yJuJ64gh!x0KlF(07#?(0ENrf7G3D`0EUHz zisCaq%dJ9dz%zhdRNuG*01nCjDhiPCl@b8xIMfv7^t~4jVRrSTGYyZUWqY@yW=)V_ z&3sUP1SK9v1f{4lDSN(agrKYULc;#EGDVeU*5b@#MOSY5JBn#QG8wqxQh+mdR638{mo5f>O zLUdZIPSjFk0~F26zDrM3y_#P^P91oWtLlPaZrhnM$NR%qsbHHK#?fN?cX?EvAhY1Sr9A(1;Kw4@87~|;2QP~ z(kKOGvCdB}qr4m#)1DwQFlh^NdBZvNLkld&yg%&GU`+boBMsoj5o?8tVuY^b0?4;E zsxoLxz8?S$y~a~x0{?dqk+6~Dd(EG7px_yH(X&NX&qEtHPUhu*JHD258=5$JS12rQ zcN+7p>R>tbFJ3NzEcRIpS98?}YEYxBIA8}1Y8zH9wq0c{hx+EXY&ZQ!-Hvy03X zLTMo4EZwtKfwb294-cY5XhQRxYJSybphcrNJWW2FY+b?|QB^?$5ZN=JlSs9Og(;8+ z*~-#CeeEOxt~F#aWn8wy-N_ilDDe_o+SwJD>4y?j5Lpj z2&!EX)RNxnadPBAa?fOj5D1C{l1E0X?&G3+ckcVfk`?%2FTsoUf4@~eaS#th=zq7v zMEJR@1T?Pi4;$xiPv`3)9rsrbVUH&b0e2{YTEG%;$GGzKUKEim;R6r>F@Q-}9JR-< zOPpQI>W0Vt6&7d?~$d&}chKTr_rELu} zWY;KTvtpJFr?P~ReHL4~2=ABn1`GN4Li%OI_1{mMRQi1Bf?+^Va?xdn4>h)Bq#ZRK zYo%R_h5etrv|!$1QF8fu80fN?1oXe(Jx#e6H^$+>C}N{*i$bNbELsXDA>cxlh|iFq zh~$yJ?1lTdcFd1Yv+Hr^PP!yupP!0H@Y6(wFcaVE+0?qjDJ1;*-Q8qL{NNPc{GAoi z_kBH`kw^(^7ShmzArk^A-!3_$W%!M-pGaZC=K`p-ch&iT%CV0>ofS74aPd7oT&cRr zXI30fVV6#PR*Z?c*orR0!$K6SUl9!H>hG+%`LdifNk`!Sw7Hon{Wn=|qV{a%v9nEq zAdBW*5kq6il=yA}x8cZQt^c+RBS|TRn;!?$ue?@jIV~0w1dt1FJRYI-K5>z-^01)R z)r}A&QXp^?-?}Uj`}ZPqB#}xO-?{0wrmi|eJOEjzdXbey4$rtKNHz)M*o?Ov+;S=K z-l~`)xV`%7Gvzy5wfvwqc0|80K29k0G~1nuBO+y-6)w11Kz2{>yD{HTt-uybe2pe? zUZK*Eij7TT4NwF1Jr@6R7gMuu^@qn#zPIgRtF?-SJL83LBDrh7k#{F^222EXPg}S0d4Lf0!|1 z|2k$^b~)^8$Z-yH{B-vo%7sVU@ZCvXN+Am)-fy$afZ_4HAUpK}j4p`UyXRel-+(VS z#K>-=-oA1pH+Lo$&|!lYB|M7Y&&bF##Oi@y_G3p1X$0I{jS1!NEdTz#x0`H`d*l%X z*8Y3>L*>j@ZQGOdPqwY(GzbA4nxqT(UAP<-tBf{_cb&Hn8hO5gEAotoV;tF6K4~wr2-M0v|2acQ!E@G*g$J z)~&_lvwN%WW>@U_taX5YX@a~pnG7A~jGwQwd4)QKk|^d_x9j+3JYmI5H`a)XMKwDt zk(nmso_I$Kc5m+8iVbIhY<4$34Oz!sg3oZF%UtS(sc6iq3?e8Z;P<{OFU9MACE6y( zeVprnhr!P;oc8pbE%A~S<+NGI2ZT@4A|o9bByQ0er$rYB3(c)7;=)^?$%a${0@70N zuiBVnAMd|qX7BE)8})+FAI&HM|BIb3e=e`b{Do8`J0jc$H>gl$zF26=haG31FDaep zd~i}CHSn$#8|WtE06vcA%1yxiy_TH|RmZ5>pI5*8pJZk0X54JDQQZgIf1Pp3*6hepV_cXe)L2iW$Ov=RZ4T)SP^a_8V} z+Nl?NJL7fAi<)Gt98U+LhE>x4W=bfo4F>5)qBx@^8&5-b>y*Wq19MyS(72ka8XFr2 zf*j(ExtQkjwN|4B?D z7+WzS*h6e_Po+Iqc-2n)gTz|de%FcTd_i9n+Y5*Vb=E{8xj&|h`CcUC*(yeCf~#Mf zzb-_ji&PNcctK6Xhe#gB0skjFFK5C4=k%tQQ}F|ZvEnPcH=#yH4n%z78?McMh!vek zVzwC0*OpmW2*-A6xz0=pE#WdXHMNxSJ*qGY(RoV9)|eu)HSSi_+|)IgT|!7HRx~ zjM$zp%LEBY)1AKKNI?~*>9DE3Y2t5p#jeqeq`1 zsjA-8eQKC*!$%k#=&jm+JG?UD(}M!tI{wD*3FQFt8jgv2xrRUJ}t}rWx2>XWz9ndH*cxl()ZC zoq?di!h6HY$fsglgay7|b6$cUG-f!U4blbj(rpP^1ZhHv@Oi~;BBvrv<+uC;%6QK!nyQ!bb3i3D~cvnpDAo3*3 zXRfZ@$J{FP?jf(NY7~-%Kem>jzZ2+LtbG!9I_fdJdD*;^T9gaiY>d+S$EdQrW9W62 z6w8M&v*8VWD_j)fmt?+bdavPn>oW8djd zRnQ}{XsIlwYWPp;GWLXvbSZ8#w25z1T}!<{_~(dcR_i1U?hyAe+lL*(Y6c;j2q7l! zMeN(nuA8Z9$#w2%ETSLjF{A#kE#WKus+%pal;-wx&tTsmFPOcbJtT?j&i(#-rB}l@ zXz|&%MXjD2YcYCZ3h4)?KnC*X$G%5N)1s!0!Ok!F9KLgV@wxMiFJIVH?E5JcwAnZF zU8ZPDJ_U_l81@&npI5WS7Y@_gf3vTXa;511h_(@{y1q-O{&bzJ z*8g>?c5=lUH6UfPj3=iuuHf4j?KJPq`x@en2Bp>#zIQjX5(C<9-X4X{a^S znWF1zJ=7rEUwQ&cZgyV4L12f&2^eIc^dGIJP@ToOgrU_Qe=T)utR;W$_2Vb7NiZ+d z$I0I>GFIutqOWiLmT~-Q<(?n5QaatHWj**>L8sxh1*pAkwG>siFMGEZYuZ)E!^Hfs zYBj`sbMQ5MR;6=1^0W*qO*Zthx-svsYqrUbJW)!vTGhWKGEu8c+=Yc%xi}Rncu3ph zTT1j_>={i3l#~$!rW!%ZtD9e6l6k-k8l{2w53!mmROAD^2yB^e)3f9_Qyf&C#zk`( z|5RL%r&}#t(;vF4nO&n}`iZpIL=p9tYtYv3%r@GzLWJ6%y_D(icSF^swYM`e8-n43iwo$C~>G<)dd0ze@5}n(!^YD zHf#OVbQ$Li@J}-qcOYn_iWF=_%)EXhrVuaYiai|B<1tXwNsow(m;XfL6^x~|Tr%L3~cs0@c) zDvOFU-AYn1!A;RBM0S}*EhYK49H$mBAxus)CB*KW(87#!#_C0wDr<0*dZ+GN&(3wR z6)cFLiDvOfs*-7Q75ekTAx)k!dtENUKHbP|2y4=tf*d_BeZ(9kR*m;dVzm&0fkKuD zVw5y9N>pz9C_wR+&Ql&&y{4@2M2?fWx~+>f|F%8E@fIfvSM$Dsk26(UL32oNvTR;M zE?F<7<;;jR4)ChzQaN((foV z)XqautTdMYtv<=oo-3W-t|gN7Q43N~%fnClny|NNcW9bIPPP5KK7_N8g!LB8{mK#! zH$74|$b4TAy@hAZ!;irT2?^B0kZ)7Dc?(7xawRUpO~AmA#}eX9A>+BA7{oDi)LA?F ze&CT`Cu_2=;8CWI)e~I_65cUmMPw5fqY1^6v))pc_TBArvAw_5Y8v0+fFFT`T zHP3&PYi2>CDO=a|@`asXnwe>W80%%<>JPo(DS}IQiBEBaNN0EF6HQ1L2i6GOPMOdN zjf3EMN!E(ceXhpd8~<6;6k<57OFRs;mpFM6VviPN>p3?NxrpNs0>K&nH_s ze)2#HhR9JHPAXf#viTkbc{-5C7U`N!`>J-$T!T6%=xo-)1_WO=+BG{J`iIk%tvxF39rJtK49Kj#ne;WG1JF1h7;~wauZ)nMvmBa2PPfrqREMKWX z@v}$0&+|nJrAAfRY-%?hS4+$B%DNMzBb_=Hl*i%euVLI5Ts~UsBVi(QHyKQ2LMXf` z0W+~Kz7$t#MuN|X2BJ(M=xZDRAyTLhPvC8i&9b=rS-T{k34X}|t+FMqf5gwQirD~N1!kK&^#+#8WvcfENOLA`Mcy@u~ zH10E=t+W=Q;gn}&;`R1D$n(8@Nd6f)9=F%l?A>?2w)H}O4avWOP@7IMVRjQ&aQDb) zzj{)MTY~Nk78>B!^EbpT{&h zy{wTABQlVVQG<4;UHY?;#Je#-E;cF3gVTx520^#XjvTlEX>+s{?KP#Rh@hM6R;~DE zaQY16$Axm5ycukte}4FtY-VZHc>=Ps8mJDLx3mwVvcF<^`Y6)v5tF`RMXhW1kE-;! z7~tpIQvz5a6~q-8@hTfF9`J;$QGQN%+VF#`>F4K3>h!tFU^L2jEagQ5Pk1U_I5&B> z+i<8EMFGFO$f7Z?pzI(jT0QkKnV)gw=j74h4*jfkk3UsUT5PemxD`pO^Y#~;P2Cte zzZ^pr>SQHC-576SI{p&FRy36<`&{Iej&&A&%>3-L{h(fUbGnb)*b&eaXj>i>gzllk zLXjw`pp#|yQIQ@;?mS=O-1Tj+ZLzy+aqr7%QwWl?j=*6dw5&4}>!wXqh&j%NuF{1q zzx$OXeWiAue+g#nkqQ#Uej@Zu;D+@z^VU*&HuNqqEm?V~(Z%7D`W5KSy^e|yF6kM7 z8Z9fEpcs^ElF9Vnolfs7^4b0fsNt+i?LwUX8Cv|iJeR|GOiFV!JyHdq+XQ&dER(KSqMxW{=M)lA?Exe&ZEB~6SmHg`zkcD7x#myq0h61+zhLr_NzEIjX zr~NGX_Uh~gdcrvjGI(&5K_zaEf}1t*)v3uT>~Gi$r^}R;H+0FEE5El{y;&DniH2@A z@!71_8mFHt1#V8MVsIYn={v&*0;3SWf4M$yLB^BdewOxz;Q=+gakk`S{_R_t!z2b| z+0d^C?G&7U6$_-W9@eR6SH%+qLx_Tf&Gu5%pn*mOGU0~kv~^K zhPeqYZMWWoA(Y+4GgQo9nNe6S#MZnyce_na@78ZnpwFenVafZC3N2lc5Jk-@V`{|l zhaF`zAL)+($xq8mFm{7fXtHru+DANoGz-A^1*@lTnE;1?03lz8kAnD{zQU=Pb^3f` zT5-g`z5|%qOa!WTBed-8`#AQ~wb9TrUZKU)H*O7!LtNnEd!r8!Oda)u!Gb5P`9(`b z`lMP6CLh4OzvXC#CR|@uo$EcHAyGr=)LB7)>=s3 zvU;aR#cN3<5&CLMFU@keW^R-Tqyf4fdkOnwI(H$x#@I1D6#dkUo@YW#7MU0@=NV-4 zEh2K?O@+2e{qW^7r?B~QTO)j}>hR$q9*n$8M(4+DOZ00WXFonLlk^;os8*zI>YG#? z9oq$CD~byz>;`--_NMy|iJRALZ#+qV8OXn=AmL^GL&|q1Qw-^*#~;WNNNbk(96Tnw zGjjscNyIyM2CYwiJ2l-}u_7mUGcvM+puPF^F89eIBx27&$|p_NG)fOaafGv|_b9G$;1LzZ-1aIE?*R6kHg}dy%~K(Q5S2O6086 z{lN&8;0>!pq^f*Jlh=J%Rmaoed<=uf@$iKl+bieC83IT!09J&IF)9H)C?d!eW1UQ}BQwxaqQY47DpOk@`zZ zo>#SM@oI^|nrWm~Ol7=r`!Bp9lQNbBCeHcfN&X$kjj0R(@?f$OHHt|fWe6jDrYg3(mdEd$8P2Yzjt9*EM zLE|cp-Tzsdyt(dvLhU8}_IX&I?B=|yoZ!&<`9&H5PtApt=VUIB4l0a1NH v0SQqt3DM`an1p};^>=lX|A*k@Y-MNT^ZzF}9G-1G696?OEyXH%^Pv9$0dR%J diff --git a/examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..8e19b410a1b15ff180f3dacac19395fe3046cdec GIT binary patch literal 10676 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 literal 0 HcmV?d00001 diff --git a/examples/basic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/examples/basic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..4c19a13c239cb67b8a2134ddd5f325db1d2d5bee GIT binary patch literal 15523 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 BIP(Ai literal 0 HcmV?d00001 diff --git a/examples/basic/android/app/src/main/res/values/styles.xml b/examples/basic/android/app/src/main/res/values/styles.xml index 319eb0ca..62fe59fa 100644 --- a/examples/basic/android/app/src/main/res/values/styles.xml +++ b/examples/basic/android/app/src/main/res/values/styles.xml @@ -3,6 +3,7 @@ diff --git a/examples/basic/android/build.gradle b/examples/basic/android/build.gradle index c3fc996e..ef27ce6c 100644 --- a/examples/basic/android/build.gradle +++ b/examples/basic/android/build.gradle @@ -4,18 +4,15 @@ buildscript { ext { buildToolsVersion = "29.0.2" minSdkVersion = 16 - compileSdkVersion = 31 + compileSdkVersion = 29 targetSdkVersion = 29 - supportLibVersion = "28.0.0" - -} + } repositories { - jcenter() google() + jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.2' - + classpath("com.android.tools.build:gradle:4.2.2") // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } @@ -24,18 +21,17 @@ buildscript { allprojects { repositories { mavenLocal() - google() - maven { - url "https://jitpack.io" - } - jcenter() maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm - url "$rootDir/../node_modules/react-native/android" + url("$rootDir/../node_modules/react-native/android") } maven { // Android JSC is installed from npm url("$rootDir/../node_modules/jsc-android/dist") } + + google() + jcenter() + maven { url 'https://www.jitpack.io' } } } diff --git a/examples/basic/android/gradle.properties b/examples/basic/android/gradle.properties index ccb748f5..3bdbd3d4 100644 --- a/examples/basic/android/gradle.properties +++ b/examples/basic/android/gradle.properties @@ -17,5 +17,12 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true + +# Version of flipper SDK to use with React Native +FLIPPER_VERSION=0.54.0 diff --git a/examples/basic/android/gradle/wrapper/gradle-wrapper.jar b/examples/basic/android/gradle/wrapper/gradle-wrapper.jar index b5166dad4d90021f6a0b45268c0755719f1d5cd4..f3d88b1c2faf2fc91d853cd5d4242b5547257070 100644 GIT binary patch literal 58695 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 literal 52266 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 z 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 diff --git a/examples/basic/android/gradle/wrapper/gradle-wrapper.properties b/examples/basic/android/gradle/wrapper/gradle-wrapper.properties index 20553249..6499169f 100644 --- a/examples/basic/android/gradle/wrapper/gradle-wrapper.properties +++ b/examples/basic/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Thu Mar 09 08:03:03 PST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/examples/basic/android/gradlew b/examples/basic/android/gradlew index 91a7e269..2fe81a7d 100755 --- a/examples/basic/android/gradlew +++ b/examples/basic/android/gradlew @@ -1,4 +1,20 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## ## @@ -6,47 +22,6 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" @@ -61,9 +36,49 @@ while [ -h "$PRG" ] ; do fi done SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- +cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" -cd "$SAVED" >&- +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -90,7 +105,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -110,10 +125,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -138,27 +154,30 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=`save "$@"` -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/examples/basic/android/gradlew.bat b/examples/basic/android/gradlew.bat index aec99730..62bd9b9c 100644 --- a/examples/basic/android/gradlew.bat +++ b/examples/basic/android/gradlew.bat @@ -1,90 +1,103 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/basic/android/keystores/BUCK b/examples/basic/android/keystores/BUCK deleted file mode 100644 index 15da20e6..00000000 --- a/examples/basic/android/keystores/BUCK +++ /dev/null @@ -1,8 +0,0 @@ -keystore( - name = 'debug', - store = 'debug.keystore', - properties = 'debug.keystore.properties', - visibility = [ - 'PUBLIC', - ], -) diff --git a/examples/basic/android/keystores/debug.keystore.properties b/examples/basic/android/keystores/debug.keystore.properties deleted file mode 100644 index 121bfb49..00000000 --- a/examples/basic/android/keystores/debug.keystore.properties +++ /dev/null @@ -1,4 +0,0 @@ -key.store=debug.keystore -key.alias=androiddebugkey -key.store.password=android -key.alias.password=android diff --git a/examples/basic/android/settings.gradle b/examples/basic/android/settings.gradle index f367409d..29cf817a 100644 --- a/examples/basic/android/settings.gradle +++ b/examples/basic/android/settings.gradle @@ -1,12 +1,5 @@ rootProject.name = 'VideoPlayer' - apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) - include ':react-native-video' project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android-exoplayer') - - -include ':@react-native-picker_picker' -project(':@react-native-picker_picker').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-picker/picker/android') - include ':app' diff --git a/examples/basic/app.json b/examples/basic/app.json new file mode 100644 index 00000000..77e4fa7e --- /dev/null +++ b/examples/basic/app.json @@ -0,0 +1,4 @@ +{ + "name": "VideoPlayer", + "displayName": "VideoPlayer" +} \ No newline at end of file diff --git a/examples/basic/babel.config.js b/examples/basic/babel.config.js index e233168b..6c9b6e99 100644 --- a/examples/basic/babel.config.js +++ b/examples/basic/babel.config.js @@ -13,4 +13,4 @@ module.exports = { }, ], ], -}; +}; \ No newline at end of file diff --git a/examples/basic/ios/AppDelegate.h b/examples/basic/ios/AppDelegate.h deleted file mode 100644 index a9654d5e..00000000 --- a/examples/basic/ios/AppDelegate.h +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -@interface AppDelegate : UIResponder - -@property (nonatomic, strong) UIWindow *window; - -@end diff --git a/examples/basic/ios/AppDelegate.m b/examples/basic/ios/AppDelegate.m deleted file mode 100644 index 0e5653a5..00000000 --- a/examples/basic/ios/AppDelegate.m +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "AppDelegate.h" - -#import "RCTRootView.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - NSURL *jsCodeLocation; - - // Loading JavaScript code - uncomment the one you want. - - // OPTION 1 - // Load from development server. Start the server from the repository root: - // - // $ npm start - // - // To run on device, change `localhost` to the IP address of your computer, and make sure your computer and - // iOS device are on the same Wi-Fi network. - jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.bundle?platform=ios&dev=true"]; - - // OPTION 2 - // Load from pre-bundled file on disk. To re-generate the static bundle, run - // - // $ curl 'http://localhost:8081/index.bundle?dev=false&minify=true' -o iOS/main.jsbundle - // - // and uncomment the next following line - // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; - - RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation - moduleName:@"VideoPlayer" - initialProperties: nil - launchOptions:launchOptions]; - - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - UIViewController *rootViewController = [[UIViewController alloc] init]; - rootViewController.view = rootView; - self.window.rootViewController = rootViewController; - [self.window makeKeyAndVisible]; - return YES; -} - -@end diff --git a/examples/basic/ios/Base.lproj/LaunchScreen.xib b/examples/basic/ios/Base.lproj/LaunchScreen.xib deleted file mode 100644 index 187346b0..00000000 --- a/examples/basic/ios/Base.lproj/LaunchScreen.xib +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/basic/ios/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/basic/ios/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 118c98f7..00000000 --- a/examples/basic/ios/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/examples/basic/ios/Info.plist b/examples/basic/ios/Info.plist deleted file mode 100644 index 45532a3a..00000000 --- a/examples/basic/ios/Info.plist +++ /dev/null @@ -1,47 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0.0 - CFBundleSignature - ???? - CFBundleVersion - 2 - LSApplicationCategoryType - - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/examples/basic/ios/Podfile b/examples/basic/ios/Podfile new file mode 100644 index 00000000..bb45269f --- /dev/null +++ b/examples/basic/ios/Podfile @@ -0,0 +1,26 @@ +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '10.0' +use_frameworks! :linkage => :static + +target 'VideoPlayer' do + config = use_native_modules! + + use_react_native!(:path => config["reactNativePath"]) + + target 'VideoPlayerTests' do + inherit! :complete + # Pods for testing + end + +end + +target 'VideoPlayer-tvOS' do + # Pods for VideoPlayer-tvOS + + target 'VideoPlayer-tvOSTests' do + inherit! :search_paths + # Pods for testing + end +end diff --git a/examples/basic/ios/Podfile.lock b/examples/basic/ios/Podfile.lock new file mode 100644 index 00000000..242b20a4 --- /dev/null +++ b/examples/basic/ios/Podfile.lock @@ -0,0 +1,392 @@ +PODS: + - boost-for-react-native (1.63.0) + - DoubleConversion (1.1.6) + - FBLazyVector (0.63.4) + - FBReactNativeSpec (0.63.4): + - Folly (= 2020.01.13.00) + - RCTRequired (= 0.63.4) + - RCTTypeSafety (= 0.63.4) + - React-Core (= 0.63.4) + - React-jsi (= 0.63.4) + - ReactCommon/turbomodule/core (= 0.63.4) + - Folly (2020.01.13.00): + - boost-for-react-native + - DoubleConversion + - Folly/Default (= 2020.01.13.00) + - glog + - Folly/Default (2020.01.13.00): + - boost-for-react-native + - DoubleConversion + - glog + - glog (0.3.5) + - PromisesObjC (2.0.0) + - PromisesSwift (2.0.0): + - PromisesObjC (= 2.0.0) + - RCTRequired (0.63.4) + - RCTTypeSafety (0.63.4): + - FBLazyVector (= 0.63.4) + - Folly (= 2020.01.13.00) + - RCTRequired (= 0.63.4) + - React-Core (= 0.63.4) + - React (0.63.4): + - React-Core (= 0.63.4) + - React-Core/DevSupport (= 0.63.4) + - React-Core/RCTWebSocket (= 0.63.4) + - React-RCTActionSheet (= 0.63.4) + - React-RCTAnimation (= 0.63.4) + - React-RCTBlob (= 0.63.4) + - React-RCTImage (= 0.63.4) + - React-RCTLinking (= 0.63.4) + - React-RCTNetwork (= 0.63.4) + - React-RCTSettings (= 0.63.4) + - React-RCTText (= 0.63.4) + - React-RCTVibration (= 0.63.4) + - React-callinvoker (0.63.4) + - React-Core (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default (= 0.63.4) + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-Core/CoreModulesHeaders (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-Core/Default (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-Core/DevSupport (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default (= 0.63.4) + - React-Core/RCTWebSocket (= 0.63.4) + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - React-jsinspector (= 0.63.4) + - Yoga + - React-Core/RCTActionSheetHeaders (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-Core/RCTAnimationHeaders (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-Core/RCTBlobHeaders (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-Core/RCTImageHeaders (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-Core/RCTLinkingHeaders (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-Core/RCTNetworkHeaders (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-Core/RCTSettingsHeaders (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-Core/RCTTextHeaders (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-Core/RCTVibrationHeaders (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-Core/RCTWebSocket (0.63.4): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default (= 0.63.4) + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsiexecutor (= 0.63.4) + - Yoga + - React-CoreModules (0.63.4): + - FBReactNativeSpec (= 0.63.4) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.4) + - React-Core/CoreModulesHeaders (= 0.63.4) + - React-jsi (= 0.63.4) + - React-RCTImage (= 0.63.4) + - ReactCommon/turbomodule/core (= 0.63.4) + - React-cxxreact (0.63.4): + - boost-for-react-native (= 1.63.0) + - DoubleConversion + - Folly (= 2020.01.13.00) + - glog + - React-callinvoker (= 0.63.4) + - React-jsinspector (= 0.63.4) + - React-jsi (0.63.4): + - boost-for-react-native (= 1.63.0) + - DoubleConversion + - Folly (= 2020.01.13.00) + - glog + - React-jsi/Default (= 0.63.4) + - React-jsi/Default (0.63.4): + - boost-for-react-native (= 1.63.0) + - DoubleConversion + - Folly (= 2020.01.13.00) + - glog + - React-jsiexecutor (0.63.4): + - DoubleConversion + - Folly (= 2020.01.13.00) + - glog + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - React-jsinspector (0.63.4) + - react-native-video (6.0.0-alpha1): + - React-Core + - react-native-video/Video (= 6.0.0-alpha1) + - react-native-video/Video (6.0.0-alpha1): + - PromisesSwift + - React-Core + - React-RCTActionSheet (0.63.4): + - React-Core/RCTActionSheetHeaders (= 0.63.4) + - React-RCTAnimation (0.63.4): + - FBReactNativeSpec (= 0.63.4) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.4) + - React-Core/RCTAnimationHeaders (= 0.63.4) + - React-jsi (= 0.63.4) + - ReactCommon/turbomodule/core (= 0.63.4) + - React-RCTBlob (0.63.4): + - FBReactNativeSpec (= 0.63.4) + - Folly (= 2020.01.13.00) + - React-Core/RCTBlobHeaders (= 0.63.4) + - React-Core/RCTWebSocket (= 0.63.4) + - React-jsi (= 0.63.4) + - React-RCTNetwork (= 0.63.4) + - ReactCommon/turbomodule/core (= 0.63.4) + - React-RCTImage (0.63.4): + - FBReactNativeSpec (= 0.63.4) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.4) + - React-Core/RCTImageHeaders (= 0.63.4) + - React-jsi (= 0.63.4) + - React-RCTNetwork (= 0.63.4) + - ReactCommon/turbomodule/core (= 0.63.4) + - React-RCTLinking (0.63.4): + - FBReactNativeSpec (= 0.63.4) + - React-Core/RCTLinkingHeaders (= 0.63.4) + - React-jsi (= 0.63.4) + - ReactCommon/turbomodule/core (= 0.63.4) + - React-RCTNetwork (0.63.4): + - FBReactNativeSpec (= 0.63.4) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.4) + - React-Core/RCTNetworkHeaders (= 0.63.4) + - React-jsi (= 0.63.4) + - ReactCommon/turbomodule/core (= 0.63.4) + - React-RCTSettings (0.63.4): + - FBReactNativeSpec (= 0.63.4) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.4) + - React-Core/RCTSettingsHeaders (= 0.63.4) + - React-jsi (= 0.63.4) + - ReactCommon/turbomodule/core (= 0.63.4) + - React-RCTText (0.63.4): + - React-Core/RCTTextHeaders (= 0.63.4) + - React-RCTVibration (0.63.4): + - FBReactNativeSpec (= 0.63.4) + - Folly (= 2020.01.13.00) + - React-Core/RCTVibrationHeaders (= 0.63.4) + - React-jsi (= 0.63.4) + - ReactCommon/turbomodule/core (= 0.63.4) + - ReactCommon/turbomodule/core (0.63.4): + - DoubleConversion + - Folly (= 2020.01.13.00) + - glog + - React-callinvoker (= 0.63.4) + - React-Core (= 0.63.4) + - React-cxxreact (= 0.63.4) + - React-jsi (= 0.63.4) + - RNCPicker (1.16.8): + - React-Core + - Yoga (1.14.0) + +DEPENDENCIES: + - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) + - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) + - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`) + - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) + - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) + - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) + - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) + - React (from `../node_modules/react-native/`) + - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) + - React-Core (from `../node_modules/react-native/`) + - React-Core/DevSupport (from `../node_modules/react-native/`) + - React-Core/RCTWebSocket (from `../node_modules/react-native/`) + - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) + - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) + - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) + - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) + - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) + - react-native-video (from `../node_modules/react-native-video`) + - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) + - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) + - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) + - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) + - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) + - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) + - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) + - React-RCTText (from `../node_modules/react-native/Libraries/Text`) + - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) + - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "RNCPicker (from `../node_modules/@react-native-picker/picker`)" + - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) + +SPEC REPOS: + trunk: + - boost-for-react-native + - PromisesObjC + - PromisesSwift + +EXTERNAL SOURCES: + DoubleConversion: + :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + FBLazyVector: + :path: "../node_modules/react-native/Libraries/FBLazyVector" + FBReactNativeSpec: + :path: "../node_modules/react-native/Libraries/FBReactNativeSpec" + Folly: + :podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec" + glog: + :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" + RCTRequired: + :path: "../node_modules/react-native/Libraries/RCTRequired" + RCTTypeSafety: + :path: "../node_modules/react-native/Libraries/TypeSafety" + React: + :path: "../node_modules/react-native/" + React-callinvoker: + :path: "../node_modules/react-native/ReactCommon/callinvoker" + React-Core: + :path: "../node_modules/react-native/" + React-CoreModules: + :path: "../node_modules/react-native/React/CoreModules" + React-cxxreact: + :path: "../node_modules/react-native/ReactCommon/cxxreact" + React-jsi: + :path: "../node_modules/react-native/ReactCommon/jsi" + React-jsiexecutor: + :path: "../node_modules/react-native/ReactCommon/jsiexecutor" + React-jsinspector: + :path: "../node_modules/react-native/ReactCommon/jsinspector" + react-native-video: + :path: "../node_modules/react-native-video" + React-RCTActionSheet: + :path: "../node_modules/react-native/Libraries/ActionSheetIOS" + React-RCTAnimation: + :path: "../node_modules/react-native/Libraries/NativeAnimation" + React-RCTBlob: + :path: "../node_modules/react-native/Libraries/Blob" + React-RCTImage: + :path: "../node_modules/react-native/Libraries/Image" + React-RCTLinking: + :path: "../node_modules/react-native/Libraries/LinkingIOS" + React-RCTNetwork: + :path: "../node_modules/react-native/Libraries/Network" + React-RCTSettings: + :path: "../node_modules/react-native/Libraries/Settings" + React-RCTText: + :path: "../node_modules/react-native/Libraries/Text" + React-RCTVibration: + :path: "../node_modules/react-native/Libraries/Vibration" + ReactCommon: + :path: "../node_modules/react-native/ReactCommon" + RNCPicker: + :path: "../node_modules/@react-native-picker/picker" + Yoga: + :path: "../node_modules/react-native/ReactCommon/yoga" + +SPEC CHECKSUMS: + boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c + DoubleConversion: cde416483dac037923206447da6e1454df403714 + FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e + FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e + Folly: b73c3869541e86821df3c387eb0af5f65addfab4 + glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3 + PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 + PromisesSwift: e0b2a6433469efb0b83a2b84c62a2abab8e5e5d4 + RCTRequired: 082f10cd3f905d6c124597fd1c14f6f2655ff65e + RCTTypeSafety: 8c9c544ecbf20337d069e4ae7fd9a377aadf504b + React: b0a957a2c44da4113b0c4c9853d8387f8e64e615 + React-callinvoker: c3f44dd3cb195b6aa46621fff95ded79d59043fe + React-Core: d3b2a1ac9a2c13c3bcde712d9281fc1c8a5b315b + React-CoreModules: 0581ff36cb797da0943d424f69e7098e43e9be60 + React-cxxreact: c1480d4fda5720086c90df537ee7d285d4c57ac3 + React-jsi: a0418934cf48f25b485631deb27c64dc40fb4c31 + React-jsiexecutor: 93bd528844ad21dc07aab1c67cb10abae6df6949 + React-jsinspector: 58aef7155bc9a9683f5b60b35eccea8722a4f53a + react-native-video: 7ce6d16e47b8a6a1ccdc5d9f4f3ce50e42acaf3b + React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336 + React-RCTAnimation: 1bde3ecc0c104c55df246eda516e0deb03c4e49b + React-RCTBlob: a97d378b527740cc667e03ebfa183a75231ab0f0 + React-RCTImage: c1b1f2d3f43a4a528c8946d6092384b5c880d2f0 + React-RCTLinking: 35ae4ab9dc0410d1fcbdce4d7623194a27214fb2 + React-RCTNetwork: 29ec2696f8d8cfff7331fac83d3e893c95ef43ae + React-RCTSettings: 60f0691bba2074ef394f95d4c2265ec284e0a46a + React-RCTText: 5c51df3f08cb9dedc6e790161195d12bac06101c + React-RCTVibration: ae4f914cfe8de7d4de95ae1ea6cc8f6315d73d9d + ReactCommon: 73d79c7039f473b76db6ff7c6b159c478acbbb3b + RNCPicker: 0991c56da7815c0cf946d6f63cf920b25296e5f6 + Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6 + +PODFILE CHECKSUM: 51ba394e28c02b1e60e7686b5b2926921af36c49 + +COCOAPODS: 1.11.2 diff --git a/examples/basic/ios/VideoPlayer-Bridging-Header.h b/examples/basic/ios/VideoPlayer-Bridging-Header.h new file mode 100644 index 00000000..df4d15b2 --- /dev/null +++ b/examples/basic/ios/VideoPlayer-Bridging-Header.h @@ -0,0 +1,12 @@ +// +// VideoPlayer-Bridging-Header.h +// VideoPlayer +// +// Created by nick fujita on 2/1/22. +// + +#ifndef VideoPlayer_Bridging_Header_h +#define VideoPlayer_Bridging_Header_h + + +#endif /* VideoPlayer_Bridging_Header_h */ diff --git a/examples/basic/ios/VideoPlayer-tvOS.plist b/examples/basic/ios/VideoPlayer-tvOS/Info.plist similarity index 100% rename from examples/basic/ios/VideoPlayer-tvOS.plist rename to examples/basic/ios/VideoPlayer-tvOS/Info.plist diff --git a/examples/basic/ios/VideoPlayer-tvOSTests/Info.plist b/examples/basic/ios/VideoPlayer-tvOSTests/Info.plist new file mode 100644 index 00000000..ba72822e --- /dev/null +++ b/examples/basic/ios/VideoPlayer-tvOSTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/examples/basic/ios/VideoPlayer.xcodeproj/project.pbxproj b/examples/basic/ios/VideoPlayer.xcodeproj/project.pbxproj index db5f9a39..3baa0b3e 100644 --- a/examples/basic/ios/VideoPlayer.xcodeproj/project.pbxproj +++ b/examples/basic/ios/VideoPlayer.xcodeproj/project.pbxproj @@ -7,422 +7,122 @@ objects = { /* Begin PBXBuildFile section */ - 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; - 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; - 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; - 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; - 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; - 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; - 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; - 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; + 00E356F31AD99517003FC87E /* VideoPlayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* VideoPlayerTests.m */; }; + 012C5B8A27A9666F00485309 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012C5B8927A9666F00485309 /* File.swift */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; - 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; - 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; - 8C2A0F841E2560A100E31596 /* libRCTVideo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C2A0F791E25608300E31596 /* libRCTVideo.a */; }; - FA3566AB216D5D7000E01ABD /* libRCTImage-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */; }; - FA3566AC216D5D7000E01ABD /* libRCTLinking-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */; }; - FA3566AD216D5D7000E01ABD /* libRCTNetwork-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */; }; - FA3566AE216D5D7000E01ABD /* libRCTSettings-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */; }; - FA3566AF216D5D7000E01ABD /* libRCTText-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */; }; - FA3566B0216D5D7000E01ABD /* libRCTWebSocket-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */; }; - FA3566C8216D5DA900E01ABD /* libRCTVideo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D1107C542111145500073188 /* libRCTVideo.a */; }; - FA8681B8216D5C6D0010C92A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; - FA8681B9216D5C6D0010C92A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - FA8681C8216D5C6D0010C92A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - FA8B47A5216D777200AB07CF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3EA31DF850E9000B6D8A /* libReact.a */; }; + 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; + 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 2DCD954D1E0B4F2C00145EB5 /* VideoPlayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* VideoPlayerTests.m */; }; + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + 9B1EFF602197E16686E1242F /* Pods_VideoPlayer_tvOSTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45CE330CC5286CFD45768CB3 /* Pods_VideoPlayer_tvOSTests.framework */; }; + 9B5D5E89D2011E17AA5A1D01 /* Pods_VideoPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C39F265DD4E235203DD4B55 /* Pods_VideoPlayer.framework */; }; + A7D1647E3B839F0605443BE6 /* Pods_VideoPlayer_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 500B6A1A28D58CB3AF579C6F /* Pods_VideoPlayer_tvOS.framework */; }; + AA06571A8A30E4C38646C7C9 /* Pods_VideoPlayer_VideoPlayerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B828D82CDB8A42C60BF2BFA /* Pods_VideoPlayer_VideoPlayerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { + 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTActionSheet; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 13B07F861A680F5B00A75B9A; + remoteInfo = VideoPlayer; }; - 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { + 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTGeolocation; - }; - 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5115D1A9E6B3D00147676; - remoteInfo = RCTImage; - }; - 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B511DB1A9E6C8500147676; - remoteInfo = RCTNetwork; - }; - 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; - remoteInfo = RCTVibration; - }; - 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTSettings; - }; - 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3C86DF461ADF2C930047B81A; - remoteInfo = RCTWebSocket; - }; - 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; - remoteInfo = React; - }; - 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A283A1D9B042B00D4039D; - remoteInfo = "RCTImage-tvOS"; - }; - 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A28471D9B043800D4039D; - remoteInfo = "RCTLinking-tvOS"; - }; - 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A28541D9B044C00D4039D; - remoteInfo = "RCTNetwork-tvOS"; - }; - 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A28611D9B046600D4039D; - remoteInfo = "RCTSettings-tvOS"; - }; - 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A287B1D9B048500D4039D; - remoteInfo = "RCTText-tvOS"; - }; - 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A28881D9B049200D4039D; - remoteInfo = "RCTWebSocket-tvOS"; - }; - 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A28131D9B038B00D4039D; - remoteInfo = "React-tvOS"; - }; - 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3D3C059A1DE3340900C268FA; - remoteInfo = yoga; - }; - 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3D3C06751DE3340C00C268FA; - remoteInfo = "yoga-tvOS"; - }; - 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4; - remoteInfo = cxxreact; - }; - 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4; - remoteInfo = "cxxreact-tvOS"; - }; - 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4; - remoteInfo = jschelpers; - }; - 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4; - remoteInfo = "jschelpers-tvOS"; - }; - 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTAnimation; - }; - 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A28201D9B03D100D4039D; - remoteInfo = "RCTAnimation-tvOS"; - }; - 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTLinking; - }; - 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5119B1A9E6C1200147676; - remoteInfo = RCTText; - }; - 8C2A0F781E25608300E31596 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 8C2A0F651E25608300E31596 /* RCTVideo.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTVideo; - }; - D1107C532111145500073188 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 8C2A0F651E25608300E31596 /* RCTVideo.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 641E28441F0EEC8500443AF6; - remoteInfo = "RCTVideo-tvOS"; - }; - D1107C592111145500073188 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3DBE0D001F3B181A0099AA32; - remoteInfo = fishhook; - }; - D1107C5B2111145500073188 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32; - remoteInfo = "fishhook-tvOS"; - }; - D1107C6D2111145500073188 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = EBF21BDC1FC498900052F4D5; - remoteInfo = jsinspector; - }; - D1107C6F2111145500073188 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = EBF21BFA1FC4989A0052F4D5; - remoteInfo = "jsinspector-tvOS"; - }; - D1107C712111145500073188 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7; - remoteInfo = "third-party"; - }; - D1107C732111145500073188 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3D383D3C1EBD27B6005632C8; - remoteInfo = "third-party-tvOS"; - }; - D1107C752111145500073188 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 139D7E881E25C6D100323FB7; - remoteInfo = "double-conversion"; - }; - D1107C772111145500073188 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3D383D621EBD27B9005632C8; - remoteInfo = "double-conversion-tvOS"; - }; - D1107C792111145500073188 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 9936F3131F5F2E4B0010BF04; - remoteInfo = privatedata; - }; - D1107C7B2111145500073188 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04; - remoteInfo = "privatedata-tvOS"; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2D02E47A1E0B4A5D006451C7; + remoteInfo = "VideoPlayer-tvOS"; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; - 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; - 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; - 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; - 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; - 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; - 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; - 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; + 00E356EE1AD99517003FC87E /* VideoPlayerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VideoPlayerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 00E356F21AD99517003FC87E /* VideoPlayerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VideoPlayerTests.m; sourceTree = ""; }; + 012C5B8927A9666F00485309 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = File.swift; path = VideoPlayer/File.swift; sourceTree = ""; }; + 012C5B8B27A966CF00485309 /* VideoPlayer-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "VideoPlayer-Bridging-Header.h"; sourceTree = ""; }; + 0F08E874CF32137C5FEA7CD3 /* Pods-VideoPlayer-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-VideoPlayer-tvOSTests/Pods-VideoPlayer-tvOSTests.release.xcconfig"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* VideoPlayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VideoPlayer.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = VideoPlayer/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = VideoPlayer/AppDelegate.m; sourceTree = ""; }; - 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = VideoPlayer/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = VideoPlayer/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = VideoPlayer/main.m; sourceTree = ""; }; - 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; - 39CBB10045CEBFA9BBB9645E /* libPods-VideoPlayer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VideoPlayer.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; }; - 627363E07276C06249D7CEBF /* libPods-VideoPlayer-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VideoPlayer-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; - 8C2A0F651E25608300E31596 /* RCTVideo.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVideo.xcodeproj; path = "../node_modules/react-native-video/ios/RCTVideo.xcodeproj"; sourceTree = ""; }; - FA8681CE216D5C6D0010C92A /* VideoPlayer-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "VideoPlayer-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - FA8681D0216D5C6E0010C92A /* VideoPlayer-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "VideoPlayer-tvOS.plist"; path = "/Users/amishra/Development/react-native-video-nfb/examples/basic/ios/VideoPlayer-tvOS.plist"; sourceTree = ""; }; + 14F94A5638163C887577920D /* Pods-VideoPlayer-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer-tvOS.release.xcconfig"; path = "Target Support Files/Pods-VideoPlayer-tvOS/Pods-VideoPlayer-tvOS.release.xcconfig"; sourceTree = ""; }; + 1B6B0FDCD3A9A8AC30563711 /* Pods-VideoPlayer-VideoPlayerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer-VideoPlayerTests.release.xcconfig"; path = "Target Support Files/Pods-VideoPlayer-VideoPlayerTests/Pods-VideoPlayer-VideoPlayerTests.release.xcconfig"; sourceTree = ""; }; + 2C39F265DD4E235203DD4B55 /* Pods_VideoPlayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_VideoPlayer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D02E47B1E0B4A5D006451C7 /* VideoPlayer-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "VideoPlayer-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D02E4901E0B4A5D006451C7 /* VideoPlayer-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "VideoPlayer-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 45CE330CC5286CFD45768CB3 /* Pods_VideoPlayer_tvOSTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_VideoPlayer_tvOSTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 500B6A1A28D58CB3AF579C6F /* Pods_VideoPlayer_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_VideoPlayer_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5933B738F6C2A0A8C1CC0234 /* Pods-VideoPlayer-VideoPlayerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer-VideoPlayerTests.debug.xcconfig"; path = "Target Support Files/Pods-VideoPlayer-VideoPlayerTests/Pods-VideoPlayer-VideoPlayerTests.debug.xcconfig"; sourceTree = ""; }; + 5B828D82CDB8A42C60BF2BFA /* Pods_VideoPlayer_VideoPlayerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_VideoPlayer_VideoPlayerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 72ECC70C444201AEE7C9332F /* Pods-VideoPlayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer.debug.xcconfig"; path = "Target Support Files/Pods-VideoPlayer/Pods-VideoPlayer.debug.xcconfig"; sourceTree = ""; }; + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = VideoPlayer/LaunchScreen.storyboard; sourceTree = ""; }; + 960F0735700C97E215D22AD0 /* Pods-VideoPlayer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer.release.xcconfig"; path = "Target Support Files/Pods-VideoPlayer/Pods-VideoPlayer.release.xcconfig"; sourceTree = ""; }; + CEA7B85E17314C1BE0F74AE7 /* Pods-VideoPlayer-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-VideoPlayer-tvOS/Pods-VideoPlayer-tvOS.debug.xcconfig"; sourceTree = ""; }; + E048F2A1D012F1BB4C6925E8 /* Pods-VideoPlayer-tvOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer-tvOSTests.debug.xcconfig"; path = "Target Support Files/Pods-VideoPlayer-tvOSTests/Pods-VideoPlayer-tvOSTests.debug.xcconfig"; sourceTree = ""; }; + ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; + ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 00E356EB1AD99517003FC87E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AA06571A8A30E4C38646C7C9 /* Pods_VideoPlayer_VideoPlayerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 146834051AC3E58100842450 /* libReact.a in Frameworks */, - 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */, - 8C2A0F841E2560A100E31596 /* libRCTVideo.a in Frameworks */, - 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, - 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, - 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, - 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, - 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, - 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, - 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, - 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, - 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, + 9B5D5E89D2011E17AA5A1D01 /* Pods_VideoPlayer.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - FA8681BA216D5C6D0010C92A /* Frameworks */ = { + 2D02E4781E0B4A5D006451C7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - FA8B47A5216D777200AB07CF /* libReact.a in Frameworks */, - FA3566AB216D5D7000E01ABD /* libRCTImage-tvOS.a in Frameworks */, - FA3566AC216D5D7000E01ABD /* libRCTLinking-tvOS.a in Frameworks */, - FA3566AD216D5D7000E01ABD /* libRCTNetwork-tvOS.a in Frameworks */, - FA3566AE216D5D7000E01ABD /* libRCTSettings-tvOS.a in Frameworks */, - FA3566AF216D5D7000E01ABD /* libRCTText-tvOS.a in Frameworks */, - FA3566B0216D5D7000E01ABD /* libRCTWebSocket-tvOS.a in Frameworks */, - FA3566C8216D5DA900E01ABD /* libRCTVideo.a in Frameworks */, + A7D1647E3B839F0605443BE6 /* Pods_VideoPlayer_tvOS.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E48D1E0B4A5D006451C7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9B1EFF602197E16686E1242F /* Pods_VideoPlayer_tvOSTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 00C302A81ABCB8CE00DB3ED1 /* Products */ = { + 00E356EF1AD99517003FC87E /* VideoPlayerTests */ = { isa = PBXGroup; children = ( - 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, + 00E356F21AD99517003FC87E /* VideoPlayerTests.m */, + 00E356F01AD99517003FC87E /* Supporting Files */, ); - name = Products; + path = VideoPlayerTests; sourceTree = ""; }; - 00C302B61ABCB90400DB3ED1 /* Products */ = { + 00E356F01AD99517003FC87E /* Supporting Files */ = { isa = PBXGroup; children = ( - 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, + 00E356F11AD99517003FC87E /* Info.plist */, ); - name = Products; - sourceTree = ""; - }; - 00C302BC1ABCB91800DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, - 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302D41ABCB9D200DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, - 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302E01ABCB9EE00DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, - ); - name = Products; - sourceTree = ""; - }; - 139105B71AF99BAD00B5F7CC /* Products */ = { - isa = PBXGroup; - children = ( - 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, - 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */, - ); - name = Products; - sourceTree = ""; - }; - 139FDEE71B06529A00C62182 /* Products */ = { - isa = PBXGroup; - children = ( - 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, - 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */, - D1107C5A2111145500073188 /* libfishhook.a */, - D1107C5C2111145500073188 /* libfishhook-tvOS.a */, - ); - name = Products; + name = "Supporting Files"; sourceTree = ""; }; 13B07FAE1A68108700A75B9A /* VideoPlayer */ = { @@ -431,182 +131,192 @@ 008F07F21AC5B25A0029DE68 /* main.jsbundle */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 13B07FB01A68108700A75B9A /* AppDelegate.m */, + 012C5B8927A9666F00485309 /* File.swift */, + 012C5B8B27A966CF00485309 /* VideoPlayer-Bridging-Header.h */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB61A68108700A75B9A /* Info.plist */, - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, 13B07FB71A68108700A75B9A /* main.m */, ); name = VideoPlayer; sourceTree = ""; }; - 146834001AC3E56700842450 /* Products */ = { + 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { isa = PBXGroup; children = ( - 146834041AC3E56700842450 /* libReact.a */, - 3DAD3EA31DF850E9000B6D8A /* libReact.a */, - 3DAD3EA51DF850E9000B6D8A /* libyoga.a */, - 3DAD3EA71DF850E9000B6D8A /* libyoga.a */, - 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */, - 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */, - 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */, - 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */, - D1107C6E2111145500073188 /* libjsinspector.a */, - D1107C702111145500073188 /* libjsinspector-tvOS.a */, - D1107C722111145500073188 /* libthird-party.a */, - D1107C742111145500073188 /* libthird-party.a */, - D1107C762111145500073188 /* libdouble-conversion.a */, - D1107C782111145500073188 /* libdouble-conversion.a */, - D1107C7A2111145500073188 /* libprivatedata.a */, - D1107C7C2111145500073188 /* libprivatedata-tvOS.a */, + ED297162215061F000B7C4FE /* JavaScriptCore.framework */, + ED2971642150620600B7C4FE /* JavaScriptCore.framework */, + 2C39F265DD4E235203DD4B55 /* Pods_VideoPlayer.framework */, + 5B828D82CDB8A42C60BF2BFA /* Pods_VideoPlayer_VideoPlayerTests.framework */, + 500B6A1A28D58CB3AF579C6F /* Pods_VideoPlayer_tvOS.framework */, + 45CE330CC5286CFD45768CB3 /* Pods_VideoPlayer_tvOSTests.framework */, ); - name = Products; - sourceTree = ""; - }; - 5E91572E1DD0AC6500FF2AA8 /* Products */ = { - isa = PBXGroup; - children = ( - 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */, - 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */, - ); - name = Products; - sourceTree = ""; - }; - 78C398B11ACF4ADC00677621 /* Products */ = { - isa = PBXGroup; - children = ( - 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, - 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */, - ); - name = Products; + name = Frameworks; sourceTree = ""; }; 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( - 8C2A0F651E25608300E31596 /* RCTVideo.xcodeproj */, - 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */, - 146833FF1AC3E56700842450 /* React.xcodeproj */, - 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, - 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, - 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, - 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, - 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, - 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, - 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, - 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, ); name = Libraries; sourceTree = ""; }; - 832341B11AAA6A8300B99B32 /* Products */ = { - isa = PBXGroup; - children = ( - 832341B51AAA6A8300B99B32 /* libRCTText.a */, - 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */, - ); - name = Products; - sourceTree = ""; - }; 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( - FA8681CF216D5C6D0010C92A /* Resources-iPad */, 13B07FAE1A68108700A75B9A /* VideoPlayer */, 832341AE1AAA6A7D00B99B32 /* Libraries */, + 00E356EF1AD99517003FC87E /* VideoPlayerTests */, 83CBBA001A601CBA00E9B192 /* Products */, - FA35669C216D5D7000E01ABD /* Frameworks */, + 2D16E6871FA4F8E400B85C8A /* Frameworks */, + B09E1827D988A8F7B7FABCD7 /* Pods */, ); indentWidth = 2; sourceTree = ""; tabWidth = 2; + usesTabs = 0; }; 83CBBA001A601CBA00E9B192 /* Products */ = { isa = PBXGroup; children = ( 13B07F961A680F5B00A75B9A /* VideoPlayer.app */, - FA8681CE216D5C6D0010C92A /* VideoPlayer-tvOS.app */, + 00E356EE1AD99517003FC87E /* VideoPlayerTests.xctest */, + 2D02E47B1E0B4A5D006451C7 /* VideoPlayer-tvOS.app */, + 2D02E4901E0B4A5D006451C7 /* VideoPlayer-tvOSTests.xctest */, ); name = Products; sourceTree = ""; }; - 8C2A0F661E25608300E31596 /* Products */ = { + B09E1827D988A8F7B7FABCD7 /* Pods */ = { isa = PBXGroup; children = ( - 8C2A0F791E25608300E31596 /* libRCTVideo.a */, - D1107C542111145500073188 /* libRCTVideo.a */, + 72ECC70C444201AEE7C9332F /* Pods-VideoPlayer.debug.xcconfig */, + 960F0735700C97E215D22AD0 /* Pods-VideoPlayer.release.xcconfig */, + 5933B738F6C2A0A8C1CC0234 /* Pods-VideoPlayer-VideoPlayerTests.debug.xcconfig */, + 1B6B0FDCD3A9A8AC30563711 /* Pods-VideoPlayer-VideoPlayerTests.release.xcconfig */, + CEA7B85E17314C1BE0F74AE7 /* Pods-VideoPlayer-tvOS.debug.xcconfig */, + 14F94A5638163C887577920D /* Pods-VideoPlayer-tvOS.release.xcconfig */, + E048F2A1D012F1BB4C6925E8 /* Pods-VideoPlayer-tvOSTests.debug.xcconfig */, + 0F08E874CF32137C5FEA7CD3 /* Pods-VideoPlayer-tvOSTests.release.xcconfig */, ); - name = Products; - sourceTree = ""; - }; - FA35669C216D5D7000E01ABD /* Frameworks */ = { - isa = PBXGroup; - children = ( - 39CBB10045CEBFA9BBB9645E /* libPods-VideoPlayer.a */, - 627363E07276C06249D7CEBF /* libPods-VideoPlayer-tvOS.a */, - ); - name = Frameworks; - sourceTree = ""; - }; - FA8681CF216D5C6D0010C92A /* Resources-iPad */ = { - isa = PBXGroup; - children = ( - FA8681D0216D5C6E0010C92A /* VideoPlayer-tvOS.plist */, - ); - name = "Resources-iPad"; + path = Pods; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 00E356ED1AD99517003FC87E /* VideoPlayerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "VideoPlayerTests" */; + buildPhases = ( + 664B3F472CE9752E13793E66 /* [CP] Check Pods Manifest.lock */, + 00E356EA1AD99517003FC87E /* Sources */, + 00E356EB1AD99517003FC87E /* Frameworks */, + 00E356EC1AD99517003FC87E /* Resources */, + 9930F79A083DBC2465E15F7C /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 00E356F51AD99517003FC87E /* PBXTargetDependency */, + ); + name = VideoPlayerTests; + productName = VideoPlayerTests; + productReference = 00E356EE1AD99517003FC87E /* VideoPlayerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 13B07F861A680F5B00A75B9A /* VideoPlayer */ = { isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "VideoPlayer" */; buildPhases = ( + C727CDFD7820FD2B492BFCE2 /* [CP] Check Pods Manifest.lock */, + FD10A7F022414F080027D42C /* Start Packager */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + 617FE7057887EE750FF2A943 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = VideoPlayer; - productName = "Hello World"; + productName = VideoPlayer; productReference = 13B07F961A680F5B00A75B9A /* VideoPlayer.app */; productType = "com.apple.product-type.application"; }; - FA8681B6216D5C6D0010C92A /* VideoPlayer-tvOS */ = { + 2D02E47A1E0B4A5D006451C7 /* VideoPlayer-tvOS */ = { isa = PBXNativeTarget; - buildConfigurationList = FA8681CB216D5C6D0010C92A /* Build configuration list for PBXNativeTarget "VideoPlayer-tvOS" */; + buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "VideoPlayer-tvOS" */; buildPhases = ( - FA8681B7216D5C6D0010C92A /* Sources */, - FA8681BA216D5C6D0010C92A /* Frameworks */, - FA8681C7216D5C6D0010C92A /* Resources */, - FA8681CA216D5C6D0010C92A /* Bundle React Native code and images */, + 65451BD1F0D11FF4A48FC837 /* [CP] Check Pods Manifest.lock */, + FD10A7F122414F3F0027D42C /* Start Packager */, + 2D02E4771E0B4A5D006451C7 /* Sources */, + 2D02E4781E0B4A5D006451C7 /* Frameworks */, + 2D02E4791E0B4A5D006451C7 /* Resources */, + 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */, ); buildRules = ( ); dependencies = ( ); name = "VideoPlayer-tvOS"; - productName = "Hello World"; - productReference = FA8681CE216D5C6D0010C92A /* VideoPlayer-tvOS.app */; + productName = "VideoPlayer-tvOS"; + productReference = 2D02E47B1E0B4A5D006451C7 /* VideoPlayer-tvOS.app */; productType = "com.apple.product-type.application"; }; + 2D02E48F1E0B4A5D006451C7 /* VideoPlayer-tvOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "VideoPlayer-tvOSTests" */; + buildPhases = ( + E1CD132A3DDBBC2FFC81826F /* [CP] Check Pods Manifest.lock */, + 2D02E48C1E0B4A5D006451C7 /* Sources */, + 2D02E48D1E0B4A5D006451C7 /* Frameworks */, + 2D02E48E1E0B4A5D006451C7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */, + ); + name = "VideoPlayer-tvOSTests"; + productName = "VideoPlayer-tvOSTests"; + productReference = 2D02E4901E0B4A5D006451C7 /* VideoPlayer-tvOSTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 83CBB9F71A601CBA00E9B192 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0610; - ORGANIZATIONNAME = Facebook; + LastUpgradeCheck = 1130; + TargetAttributes = { + 00E356ED1AD99517003FC87E = { + CreatedOnToolsVersion = 6.2; + LastSwiftMigration = 1310; + TestTargetID = 13B07F861A680F5B00A75B9A; + }; + 13B07F861A680F5B00A75B9A = { + LastSwiftMigration = 1310; + }; + 2D02E47A1E0B4A5D006451C7 = { + CreatedOnToolsVersion = 8.2.1; + LastSwiftMigration = 1310; + ProvisioningStyle = Automatic; + }; + 2D02E48F1E0B4A5D006451C7 = { + CreatedOnToolsVersion = 8.2.1; + LastSwiftMigration = 1310; + ProvisioningStyle = Automatic; + TestTargetID = 2D02E47A1E0B4A5D006451C7; + }; + }; }; buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "VideoPlayer" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -615,341 +325,45 @@ mainGroup = 83CBB9F61A601CBA00E9B192; productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; - ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; - }, - { - ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */; - ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; - }, - { - ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; - ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; - }, - { - ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; - ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; - }, - { - ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; - ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; - }, - { - ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; - ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; - }, - { - ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; - ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; - }, - { - ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; - ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; - }, - { - ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; - ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; - }, - { - ProductGroup = 8C2A0F661E25608300E31596 /* Products */; - ProjectRef = 8C2A0F651E25608300E31596 /* RCTVideo.xcodeproj */; - }, - { - ProductGroup = 139FDEE71B06529A00C62182 /* Products */; - ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; - }, - { - ProductGroup = 146834001AC3E56700842450 /* Products */; - ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; - }, - ); projectRoot = ""; targets = ( 13B07F861A680F5B00A75B9A /* VideoPlayer */, - FA8681B6216D5C6D0010C92A /* VideoPlayer-tvOS */, + 00E356ED1AD99517003FC87E /* VideoPlayerTests */, + 2D02E47A1E0B4A5D006451C7 /* VideoPlayer-tvOS */, + 2D02E48F1E0B4A5D006451C7 /* VideoPlayer-tvOSTests */, ); }; /* End PBXProject section */ -/* Begin PBXReferenceProxy section */ - 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTActionSheet.a; - remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTGeolocation.a; - remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTImage.a; - remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTNetwork.a; - remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTVibration.a; - remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTSettings.a; - remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTWebSocket.a; - remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 146834041AC3E56700842450 /* libReact.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libReact.a; - remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libRCTImage-tvOS.a"; - remoteRef = 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libRCTLinking-tvOS.a"; - remoteRef = 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libRCTNetwork-tvOS.a"; - remoteRef = 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libRCTSettings-tvOS.a"; - remoteRef = 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libRCTText-tvOS.a"; - remoteRef = 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libRCTWebSocket-tvOS.a"; - remoteRef = 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3EA31DF850E9000B6D8A /* libReact.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libReact.a; - remoteRef = 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3EA51DF850E9000B6D8A /* libyoga.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libyoga.a; - remoteRef = 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3EA71DF850E9000B6D8A /* libyoga.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libyoga.a; - remoteRef = 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libcxxreact.a; - remoteRef = 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libcxxreact.a; - remoteRef = 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libjschelpers.a; - remoteRef = 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libjschelpers.a; - remoteRef = 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTAnimation.a; - remoteRef = 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTAnimation.a; - remoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTLinking.a; - remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTText.a; - remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 8C2A0F791E25608300E31596 /* libRCTVideo.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTVideo.a; - remoteRef = 8C2A0F781E25608300E31596 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - D1107C542111145500073188 /* libRCTVideo.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTVideo.a; - remoteRef = D1107C532111145500073188 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - D1107C5A2111145500073188 /* libfishhook.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libfishhook.a; - remoteRef = D1107C592111145500073188 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - D1107C5C2111145500073188 /* libfishhook-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libfishhook-tvOS.a"; - remoteRef = D1107C5B2111145500073188 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - D1107C6E2111145500073188 /* libjsinspector.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libjsinspector.a; - remoteRef = D1107C6D2111145500073188 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - D1107C702111145500073188 /* libjsinspector-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libjsinspector-tvOS.a"; - remoteRef = D1107C6F2111145500073188 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - D1107C722111145500073188 /* libthird-party.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libthird-party.a"; - remoteRef = D1107C712111145500073188 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - D1107C742111145500073188 /* libthird-party.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libthird-party.a"; - remoteRef = D1107C732111145500073188 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - D1107C762111145500073188 /* libdouble-conversion.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libdouble-conversion.a"; - remoteRef = D1107C752111145500073188 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - D1107C782111145500073188 /* libdouble-conversion.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libdouble-conversion.a"; - remoteRef = D1107C772111145500073188 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - D1107C7A2111145500073188 /* libprivatedata.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libprivatedata.a; - remoteRef = D1107C792111145500073188 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - D1107C7C2111145500073188 /* libprivatedata-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libprivatedata-tvOS.a"; - remoteRef = D1107C7B2111145500073188 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - /* Begin PBXResourcesBuildPhase section */ + 00E356EC1AD99517003FC87E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 13B07F8E1A680F5B00A75B9A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - FA8681C7216D5C6D0010C92A /* Resources */ = { + 2D02E4791E0B4A5D006451C7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E48E1E0B4A5D006451C7 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - FA8681C8216D5C6D0010C92A /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -968,65 +382,298 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; }; - FA8681CA216D5C6D0010C92A /* Bundle React Native code and images */ = { + 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Bundle React Native code and images"; + name = "Bundle React Native Code And Images"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; + }; + 617FE7057887EE750FF2A943 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-VideoPlayer/Pods-VideoPlayer-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VideoPlayer/Pods-VideoPlayer-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 65451BD1F0D11FF4A48FC837 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-VideoPlayer-tvOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 664B3F472CE9752E13793E66 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-VideoPlayer-VideoPlayerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9930F79A083DBC2465E15F7C /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-VideoPlayer-VideoPlayerTests/Pods-VideoPlayer-VideoPlayerTests-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VideoPlayer-VideoPlayerTests/Pods-VideoPlayer-VideoPlayerTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + C727CDFD7820FD2B492BFCE2 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-VideoPlayer-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + E1CD132A3DDBBC2FFC81826F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-VideoPlayer-tvOSTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + FD10A7F022414F080027D42C /* Start Packager */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Start Packager"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; + showEnvVarsInLog = 0; + }; + FD10A7F122414F3F0027D42C /* Start Packager */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Start Packager"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; + showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 00E356EA1AD99517003FC87E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00E356F31AD99517003FC87E /* VideoPlayerTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 13B07F871A680F5B00A75B9A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, + 012C5B8A27A9666F00485309 /* File.swift in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - FA8681B7216D5C6D0010C92A /* Sources */ = { + 2D02E4771E0B4A5D006451C7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - FA8681B8216D5C6D0010C92A /* AppDelegate.m in Sources */, - FA8681B9216D5C6D0010C92A /* main.m in Sources */, + 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */, + 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E48C1E0B4A5D006451C7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2DCD954D1E0B4F2C00145EB5 /* VideoPlayerTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXVariantGroup section */ - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { - isa = PBXVariantGroup; - children = ( - 13B07FB21A68108700A75B9A /* Base */, - ); - name = LaunchScreen.xib; - path = VideoPlayer; - sourceTree = ""; +/* Begin PBXTargetDependency section */ + 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 13B07F861A680F5B00A75B9A /* VideoPlayer */; + targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; }; -/* End PBXVariantGroup section */ + 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2D02E47A1E0B4A5D006451C7 /* VideoPlayer-tvOS */; + targetProxy = 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 00E356F61AD99517003FC87E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5933B738F6C2A0A8C1CC0234 /* Pods-VideoPlayer-VideoPlayerTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = VideoPlayerTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "VideoPlayerTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VideoPlayer.app/VideoPlayer"; + }; + name = Debug; + }; + 00E356F71AD99517003FC87E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1B6B0FDCD3A9A8AC30563711 /* Pods-VideoPlayer-VideoPlayerTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + COPY_PHASE_STRIP = NO; + INFOPLIST_FILE = VideoPlayerTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "VideoPlayerTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VideoPlayer.app/VideoPlayer"; + }; + name = Release; + }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 72ECC70C444201AEE7C9332F /* Pods-VideoPlayer.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; - DEAD_CODE_STRIPPING = NO; - HEADER_SEARCH_PATHS = "$(inherited)"; + ENABLE_BITCODE = NO; INFOPLIST_FILE = VideoPlayer/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( @@ -1034,17 +681,22 @@ "-ObjC", "-lc++", ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = VideoPlayer; + SWIFT_OBJC_BRIDGING_HEADER = "VideoPlayer-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 960F0735700C97E215D22AD0 /* Pods-VideoPlayer.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; - HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = VideoPlayer/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( @@ -1052,33 +704,173 @@ "-ObjC", "-lc++", ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = VideoPlayer; + SWIFT_OBJC_BRIDGING_HEADER = "VideoPlayer-Bridging-Header.h"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; + 2D02E4971E0B4A5E006451C7 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = CEA7B85E17314C1BE0F74AE7 /* Pods-VideoPlayer-tvOS.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "VideoPlayer-tvOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.VideoPlayer-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + SWIFT_OBJC_BRIDGING_HEADER = "VideoPlayer-tvOS-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 10.0; + }; + name = Debug; + }; + 2D02E4981E0B4A5E006451C7 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 14F94A5638163C887577920D /* Pods-VideoPlayer-tvOS.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "VideoPlayer-tvOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.VideoPlayer-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + SWIFT_OBJC_BRIDGING_HEADER = "VideoPlayer-tvOS-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 10.0; + }; + name = Release; + }; + 2D02E4991E0B4A5E006451C7 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E048F2A1D012F1BB4C6925E8 /* Pods-VideoPlayer-tvOSTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "VideoPlayer-tvOSTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.VideoPlayer-tvOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + SWIFT_OBJC_BRIDGING_HEADER = "VideoPlayer-tvOSTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VideoPlayer-tvOS.app/VideoPlayer-tvOS"; + TVOS_DEPLOYMENT_TARGET = 10.1; + }; + name = Debug; + }; + 2D02E49A1E0B4A5E006451C7 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0F08E874CF32137C5FEA7CD3 /* Pods-VideoPlayer-tvOSTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "VideoPlayer-tvOSTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.VideoPlayer-tvOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + SWIFT_OBJC_BRIDGING_HEADER = "VideoPlayer-tvOSTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VideoPlayer-tvOS.app/VideoPlayer-tvOS"; + TVOS_DEPLOYMENT_TARGET = 10.1; + }; + name = Release; + }; 83CBBA201A601CBA00E9B192 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -1091,7 +883,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; + LIBRARY_SEARCH_PATHS = ( + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -1102,17 +900,28 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -1120,67 +929,38 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; + LIBRARY_SEARCH_PATHS = ( + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; - FA8681CC216D5C6D0010C92A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = 1; - DEAD_CODE_STRIPPING = NO; - INFOPLIST_FILE = "VideoPlayer-tvOS.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.VideoPlayer-tvOS"; - PRODUCT_NAME = "VideoPlayer-tvOS"; - SDKROOT = appletvos; - SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; - TARGETED_DEVICE_FAMILY = 3; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - FA8681CD216D5C6D0010C92A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = 1; - INFOPLIST_FILE = "VideoPlayer-tvOS.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.VideoPlayer-tvOS"; - PRODUCT_NAME = "VideoPlayer-tvOS"; - SDKROOT = appletvos; - SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; - TARGETED_DEVICE_FAMILY = 3; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "VideoPlayerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 00E356F61AD99517003FC87E /* Debug */, + 00E356F71AD99517003FC87E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "VideoPlayer" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -1190,6 +970,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "VideoPlayer-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D02E4971E0B4A5E006451C7 /* Debug */, + 2D02E4981E0B4A5E006451C7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "VideoPlayer-tvOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D02E4991E0B4A5E006451C7 /* Debug */, + 2D02E49A1E0B4A5E006451C7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "VideoPlayer" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -1199,15 +997,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - FA8681CB216D5C6D0010C92A /* Build configuration list for PBXNativeTarget "VideoPlayer-tvOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - FA8681CC216D5C6D0010C92A /* Debug */, - FA8681CD216D5C6D0010C92A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; diff --git a/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer-tvOS.xcscheme b/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer-tvOS.xcscheme index 75b3d2eb..d76773e3 100644 --- a/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer-tvOS.xcscheme +++ b/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer-tvOS.xcscheme @@ -1,9 +1,9 @@ - - - - @@ -42,18 +28,17 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + - - - - - - - - diff --git a/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer.xcscheme b/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer.xcscheme index 48a64467..ffe3e473 100644 --- a/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer.xcscheme +++ b/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer.xcscheme @@ -1,25 +1,11 @@ - - - - + + + + - - - - - - - - + + + + + + diff --git a/examples/basic/ios/VideoPlayer.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/basic/ios/VideoPlayer.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/examples/basic/ios/VideoPlayer.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/basic/ios/VideoPlayer/AppDelegate.h b/examples/basic/ios/VideoPlayer/AppDelegate.h index a9654d5e..556946d4 100644 --- a/examples/basic/ios/VideoPlayer/AppDelegate.h +++ b/examples/basic/ios/VideoPlayer/AppDelegate.h @@ -7,9 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#import #import -@interface AppDelegate : UIResponder +@interface AppDelegate : UIResponder @property (nonatomic, strong) UIWindow *window; diff --git a/examples/basic/ios/VideoPlayer/AppDelegate.m b/examples/basic/ios/VideoPlayer/AppDelegate.m index 31bf2249..9e137ec5 100644 --- a/examples/basic/ios/VideoPlayer/AppDelegate.m +++ b/examples/basic/ios/VideoPlayer/AppDelegate.m @@ -9,21 +9,42 @@ #import "AppDelegate.h" +#import #import #import +#ifdef FB_SONARKIT_ENABLED +#import +#import +#import +#import +#import +#import + +static void InitializeFlipper(UIApplication *application) { + FlipperClient *client = [FlipperClient sharedClient]; + SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; + [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; + [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; + [client addPlugin:[FlipperKitReactPlugin new]]; + [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; + [client start]; +} +#endif + @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - NSURL *jsCodeLocation; +#ifdef FB_SONARKIT_ENABLED + InitializeFlipper(application); +#endif - jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; + RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; + RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge + moduleName:@"VideoPlayer" + initialProperties:nil]; - RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation - moduleName:@"VideoPlayer" - initialProperties:nil - launchOptions:launchOptions]; rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; @@ -34,4 +55,13 @@ return YES; } +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge +{ +#if DEBUG + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"src/index" fallbackResource:nil]; +#else + return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; +#endif +} + @end diff --git a/examples/basic/ios/VideoPlayer/Base.lproj/LaunchScreen.xib b/examples/basic/ios/VideoPlayer/Base.lproj/LaunchScreen.xib deleted file mode 100644 index 9d8b0c89..00000000 --- a/examples/basic/ios/VideoPlayer/Base.lproj/LaunchScreen.xib +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/basic/ios/VideoPlayer/File.swift b/examples/basic/ios/VideoPlayer/File.swift new file mode 100644 index 00000000..fecc4ab4 --- /dev/null +++ b/examples/basic/ios/VideoPlayer/File.swift @@ -0,0 +1 @@ +import Foundation diff --git a/examples/basic/ios/VideoPlayer/Images.xcassets/Contents.json b/examples/basic/ios/VideoPlayer/Images.xcassets/Contents.json new file mode 100644 index 00000000..2d92bd53 --- /dev/null +++ b/examples/basic/ios/VideoPlayer/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/examples/basic/ios/VideoPlayer/Info.plist b/examples/basic/ios/VideoPlayer/Info.plist index 2fb6a11c..e2a0d716 100644 --- a/examples/basic/ios/VideoPlayer/Info.plist +++ b/examples/basic/ios/VideoPlayer/Info.plist @@ -4,10 +4,12 @@ CFBundleDevelopmentRegion en + CFBundleDisplayName + VideoPlayer CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -22,6 +24,21 @@ 1 LSRequiresIPhoneOS + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + + + NSLocationWhenInUseUsageDescription + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities @@ -36,19 +53,5 @@ UIViewControllerBasedStatusBarAppearance - NSLocationWhenInUseUsageDescription - - NSAppTransportSecurity - - - NSExceptionDomains - - localhost - - NSExceptionAllowsInsecureHTTPLoads - - - - diff --git a/examples/basic/ios/VideoPlayer/LaunchScreen.storyboard b/examples/basic/ios/VideoPlayer/LaunchScreen.storyboard new file mode 100644 index 00000000..556e4be5 --- /dev/null +++ b/examples/basic/ios/VideoPlayer/LaunchScreen.storyboard @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/basic/ios/VideoPlayerTests/Info.plist b/examples/basic/ios/VideoPlayerTests/Info.plist new file mode 100644 index 00000000..ba72822e --- /dev/null +++ b/examples/basic/ios/VideoPlayerTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/examples/basic/ios/VideoPlayerTests/VideoPlayerTests.m b/examples/basic/ios/VideoPlayerTests/VideoPlayerTests.m new file mode 100644 index 00000000..809995a9 --- /dev/null +++ b/examples/basic/ios/VideoPlayerTests/VideoPlayerTests.m @@ -0,0 +1,65 @@ +#import +#import + +#import +#import + +#define TIMEOUT_SECONDS 600 +#define TEXT_TO_LOOK_FOR @"Welcome to React" + +@interface VideoPlayerTests : XCTestCase + +@end + +@implementation VideoPlayerTests + +- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test +{ + if (test(view)) { + return YES; + } + for (UIView *subview in [view subviews]) { + if ([self findSubviewInView:subview matching:test]) { + return YES; + } + } + return NO; +} + +- (void)testRendersWelcomeScreen +{ + UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; + BOOL foundElement = NO; + + __block NSString *redboxError = nil; +#ifdef DEBUG + RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { + if (level >= RCTLogLevelError) { + redboxError = message; + } + }); +#endif + + while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + + foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { + if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { + return YES; + } + return NO; + }]; + } + +#ifdef DEBUG + RCTSetLogFunction(RCTDefaultLogFunction); +#endif + + XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); + XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); +} + + +@end diff --git a/examples/basic/ios/main.jsbundle b/examples/basic/ios/main.jsbundle deleted file mode 100644 index 5d7f29bf..00000000 --- a/examples/basic/ios/main.jsbundle +++ /dev/null @@ -1,5 +0,0 @@ -// Offline JS -// To re-generate the offline bundle, run this from root of your project -// $ curl 'http://localhost:8081/index.bundle?dev=false&minify=true' -o iOS/main.jsbundle - -throw new Error('Offline JS file is empty. See iOS/main.jsbundle for instructions'); diff --git a/examples/basic/ios/main.m b/examples/basic/ios/main.m deleted file mode 100644 index cb502bc1..00000000 --- a/examples/basic/ios/main.m +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} - diff --git a/examples/basic/jsconfig.json b/examples/basic/jsconfig.json deleted file mode 100644 index 068d5775..00000000 --- a/examples/basic/jsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "target": "ES6", - "module": "commonjs", - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "baseUrl": "./src", - "paths": { - "*": [ - "./src/*" - ] - } - }, - "exclude": [ - "node_modules", - "**/node_modules/*" - ] -} diff --git a/examples/basic/metro.config.js b/examples/basic/metro.config.js index 59a9b33a..c78e7ef8 100644 --- a/examples/basic/metro.config.js +++ b/examples/basic/metro.config.js @@ -4,45 +4,19 @@ * * @format */ -const fs = require('fs'); const path = require('path'); const blacklist = require('metro-config/src/defaults/blacklist'); -const rnPath = fs.realpathSync( - path.resolve(require.resolve('react-native/package.json'), '..'), -); -const rnwPath = fs.realpathSync( - path.resolve(require.resolve('react-native-windows/package.json'), '..'), -); - module.exports = { resolver: { - extraNodeModules: { - // Redirect react-native to react-native-windows - 'react-native': rnwPath, - 'react-native-windows': rnwPath, - }, - // Include the macos platform in addition to the defaults because the fork includes macos, but doesn't declare it - platforms: ['ios', 'android', 'windesktop', 'windows', 'web', 'macos'], - // Since there are multiple copies of react-native, we need to ensure that metro only sees one of them - // This should go in RN 0.61 when haste is removed blacklistRE: blacklist([ - new RegExp( - `${(path.resolve(rnPath) + path.sep).replace(/[/\\]/g, '/')}.*`, - ), - // This stops "react-native run-windows" from causing the metro server to crash if its already running new RegExp( `${path.resolve(__dirname, 'windows').replace(/[/\\]/g, '/')}.*`, ), - // Prevent recursive node_modules from local react-native-video - new RegExp( - `${path.resolve(__dirname, 'node_modules/react-native-video/node_modules').replace(/[/\\]/g, '/')}.*`, - ), - // Prevent recursive examples from local react-native-video - new RegExp( - `${path.resolve(__dirname, 'node_modules/react-native-video/examples').replace(/[/\\]/g, '/')}.*`, - ), + // This prevents "react-native run-windows" from hitting: EBUSY: resource busy or locked, open msbuild.ProjectImports.zip + /.*\.ProjectImports\.zip/, + /(.*\/react-native-video\/node_modules\/.*)$/, ]), }, transformer: { diff --git a/examples/basic/package.json b/examples/basic/package.json index cf71263d..530e822d 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -3,30 +3,34 @@ "version": "1.0.0", "private": true, "scripts": { - "start": "react-native start", "android": "react-native run-android", - "postinstall": "rm -rf node_modules/react-native-video/{examples,node_modules}", + "ios": "cd ios && pod install && cd .. && react-native run-ios", + "windows": "react-native run-windows", + "start": "node node_modules/react-native/local-cli/cli.js start", "test": "jest", "lint": "eslint ." }, "dependencies": { "@react-native-picker/picker": "^1.9.11", "babel-plugin-module-resolver": "^4.1.0", - "react": "^16.12.0", - "react-native": "0.61.5", - "react-native-video": "../../", - "react-native-windows": "^0.61.0-0" + "react": "16.13.1", + "react-native": "0.63.4", + "react-native-video": "../..", + "react-native-windows": "0.63.41" }, "devDependencies": { - "@babel/core": "^7.6.0", - "@babel/runtime": "^7.6.0", - "@react-native-community/eslint-config": "^0.0.5", + "@babel/core": "^7.8.4", + "@babel/runtime": "^7.8.4", + "@react-native-community/eslint-config": "^1.1.0", "@types/react": "^18.0.6", "@types/react-native": "^0.67.6", - "babel-jest": "^24.9.0", - "eslint": "^6.4.0", - "jest": "^24.9.0", - "metro-react-native-babel-preset": "^0.56.0", - "react-test-renderer": "16.8.6" + "babel-jest": "^25.1.0", + "eslint": "^6.5.1", + "jest": "^25.1.0", + "metro-react-native-babel-preset": "^0.59.0", + "react-test-renderer": "16.13.1" + }, + "jest": { + "preset": "react-native" } -} +} \ No newline at end of file diff --git a/examples/basic/react-native.config.js b/examples/basic/react-native.config.js deleted file mode 100644 index cedd9d38..00000000 --- a/examples/basic/react-native.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -module.exports = { - reactNativePath: fs.realpathSync(path.resolve(require.resolve('react-native-windows/package.json'), '..')), -}; diff --git a/examples/basic/src/VideoPlayer.android.tsx b/examples/basic/src/VideoPlayer.android.tsx index 7065c024..7572174f 100644 --- a/examples/basic/src/VideoPlayer.android.tsx +++ b/examples/basic/src/VideoPlayer.android.tsx @@ -48,16 +48,17 @@ class VideoPlayer extends Component { seekerWidth = 0 srcList = [ + require('./broadchurch.mp4'), + { + description: 'subtitles', + uri: 'https://bitmovin-a.akamaihd.net/content/sintel/sintel.mpd', + }, { description: 'demo with sintel Subtitles', uri: 'http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7.8506521BFC350652163895D4C26DEE124209AA9E&key=ik0', type: 'mpd', }, - { - description: 'subtitles', - uri: 'https://bitmovin-a.akamaihd.net/content/sintel/sintel.mpd', - }, { description: 'Stopped playback', uri: undefined }, { description: 'no View', @@ -521,7 +522,7 @@ class VideoPlayer extends Component { {this.IndicatorLoadingView()} - {this.srcList[this.state.srcListId]?.description} + {this.srcList[this.state.srcListId]?.description || 'local file'} @@ -814,4 +815,4 @@ const styles = StyleSheet.create({ }, }); -export default VideoPlayer +export default VideoPlayer \ No newline at end of file diff --git a/examples/basic/src/VideoPlayer.ios.tsx b/examples/basic/src/VideoPlayer.ios.tsx index de6f328a..74025104 100644 --- a/examples/basic/src/VideoPlayer.ios.tsx +++ b/examples/basic/src/VideoPlayer.ios.tsx @@ -4,7 +4,7 @@ import React, { } from 'react'; import { - AlertIOS, + Alert, Platform, StyleSheet, Text, @@ -187,7 +187,7 @@ class VideoPlayer extends Component { onLoad={this.onLoad} onBuffer={this.onBuffer} onProgress={this.onProgress} - onEnd={() => { AlertIOS.alert('Done!') }} + onEnd={() => { Alert.alert('Done!') }} repeat={true} filter={this.state.filter} filterEnabled={this.state.filterEnabled} @@ -281,7 +281,7 @@ class VideoPlayer extends Component { onLoad={this.onLoad} onBuffer={this.onBuffer} onProgress={this.onProgress} - onEnd={() => { AlertIOS.alert('Done!') }} + onEnd={() => { Alert.alert('Done!') }} repeat={true} controls={this.state.controls} filter={this.state.filter} @@ -450,4 +450,4 @@ const styles = StyleSheet.create({ justifyContent: 'center', }, }); -export default VideoPlayer +export default VideoPlayer \ No newline at end of file diff --git a/examples/basic/src/VideoPlayer.windows.tsx b/examples/basic/src/VideoPlayer.windows.tsx index 4894a8ae..217fcc6e 100644 --- a/examples/basic/src/VideoPlayer.windows.tsx +++ b/examples/basic/src/VideoPlayer.windows.tsx @@ -212,4 +212,4 @@ const styles = StyleSheet.create({ justifyContent: 'center', }, }); -export default VideoPlayer +export default VideoPlayer \ No newline at end of file diff --git a/examples/basic/src/index.js b/examples/basic/src/index.js index d6ecd293..add8ab16 100644 --- a/examples/basic/src/index.js +++ b/examples/basic/src/index.js @@ -1,5 +1,5 @@ +import {AppRegistry} from 'react-native'; import VideoPlayer from './VideoPlayer'; +import {name as appName} from '../app.json'; -import { AppRegistry } from 'react-native'; - -AppRegistry.registerComponent('VideoPlayer', () => VideoPlayer); +AppRegistry.registerComponent(appName, () => VideoPlayer); diff --git a/examples/basic/tsconfig.json b/examples/basic/tsconfig.json index 70d85743..a415e49c 100644 --- a/examples/basic/tsconfig.json +++ b/examples/basic/tsconfig.json @@ -1,20 +1,20 @@ { - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "isolatedModules": true, - "jsx": "react-native", - "lib": ["es2017"], - "moduleResolution": "node", - "noEmit": true, - "strict": true, - "target": "esnext" - }, - "exclude": [ - "node_modules", - "babel.config.js", - "metro.config.js", - "jest.config.js" - ] - } \ No newline at end of file + "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "isolatedModules": true, + "jsx": "react-native", + "lib": ["es2017"], + "moduleResolution": "node", + "noEmit": true, + "strict": true, + "target": "esnext" + }, + "exclude": [ + "node_modules", + "babel.config.js", + "metro.config.js", + "jest.config.js" + ] +} \ No newline at end of file diff --git a/examples/basic/windows/VideoPlayer.sln b/examples/basic/windows/VideoPlayer.sln index d4490e0a..9c6c57cd 100644 --- a/examples/basic/windows/VideoPlayer.sln +++ b/examples/basic/windows/VideoPlayer.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29215.179 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoPlayer", "VideoPlayer\VideoPlayer.vcxproj", "{ADF1CF02-8224-4167-A737-8CBE1A0D5208}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "videoplayer", "videoplayer\videoplayer.vcxproj", "{BF9FF035-B976-4990-86FE-09D015E91C20}" ProjectSection(ProjectDependencies) = postProject {F7D32BD0-2749-483E-9A0D-1635EF7E3136} = {F7D32BD0-2749-483E-9A0D-1635EF7E3136} EndProjectSection @@ -15,11 +15,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactCommon", "..\node_modu {A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {A990658C-CE31-4BCC-976F-0FC6B1AF693D} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactWindowsCore", "..\node_modules\react-native-windows\ReactWindowsCore\ReactWindowsCore.vcxproj", "{11C084A3-A57C-4296-A679-CAC17B603144}" - ProjectSection(ProjectDependencies) = postProject - {A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {A990658C-CE31-4BCC-976F-0FC6B1AF693D} - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Chakra", "..\node_modules\react-native-windows\Chakra\Chakra.vcxitems", "{C38970C0-5FBF-4D69-90D8-CBAC225AE895}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative", "..\node_modules\react-native-windows\Microsoft.ReactNative\Microsoft.ReactNative.vcxproj", "{F7D32BD0-2749-483E-9A0D-1635EF7E3136}" @@ -30,145 +25,163 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JSI.Universal", "..\node_mo EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx", "..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems", "{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B}" EndProject -Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.ReactNative.SharedManaged", "..\node_modules\react-native-windows\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.shproj", "{67A1076F-7790-4203-86EA-4402CCB5E782}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "..\node_modules\react-native-windows\Common\Common.vcxproj", "{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReactNative", "ReactNative", "{5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Shared", "..\node_modules\react-native-windows\Shared\Shared.vcxitems", "{2049DBE9-8D13-42C9-AE4B-413AE38FFFD0}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Shared", "..\node_modules\react-native-windows\Shared\Shared.vcxitems", "{2049DBE9-8D13-42C9-AE4B-413AE38FFFD0}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mso", "..\node_modules\react-native-windows\Mso\Mso.vcxitems", "{84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactNativeVideoCPP61", "..\node_modules\react-native-video\windows\ReactNativeVideoCPP61\ReactNativeVideoCPP61.vcxproj", "{765365E4-9553-4900-9F69-E26D4309C8DA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Include", "..\node_modules\react-native-windows\include\Include.vcxitems", "{EF074BA1-2D54-4D49-A28E-5E040B47CD2E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactNativeVideoCPP", "..\node_modules\react-native-video\windows\ReactNativeVideoCPP\ReactNativeVideoCPP.vcxproj", "{0D1E54D3-4BE1-4DAF-98BF-124C28C85014}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{0cc28589-39e4-4288-b162-97b959f8b843}*SharedItemsImports = 9 ..\node_modules\react-native-windows\Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 9 - ..\node_modules\react-native-windows\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{67a1076f-7790-4203-86ea-4402ccb5e782}*SharedItemsImports = 13 - ..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{765365e4-9553-4900-9f69-e26d4309c8da}*SharedItemsImports = 4 ..\node_modules\react-native-windows\Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9 ..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{a62d504a-16b8-41d2-9f19-e2e86019e5e4}*SharedItemsImports = 4 - ..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{adf1cf02-8224-4167-a737-8cbe1a0d5208}*SharedItemsImports = 4 ..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{c38970c0-5fbf-4d69-90d8-cbac225ae895}*SharedItemsImports = 9 ..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9 + ..\node_modules\react-native-windows\include\Include.vcxitems*{ef074ba1-2d54-4d49-a28e-5e040b47cd2e}*SharedItemsImports = 9 ..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 - ..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 + ..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 ..\node_modules\react-native-windows\Mso\Mso.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 ..\node_modules\react-native-windows\Shared\Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|ARM = Release|ARM Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|ARM64.Build.0 = Debug|ARM64 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|ARM64.Deploy.0 = Debug|ARM64 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|x64.ActiveCfg = Debug|x64 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|x64.Build.0 = Debug|x64 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|x64.Deploy.0 = Debug|x64 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|x86.ActiveCfg = Debug|Win32 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|x86.Build.0 = Debug|Win32 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|x86.Deploy.0 = Debug|Win32 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|ARM64.ActiveCfg = Release|ARM64 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|ARM64.Build.0 = Release|ARM64 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|ARM64.Deploy.0 = Release|ARM64 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|x64.ActiveCfg = Release|x64 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|x64.Build.0 = Release|x64 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|x64.Deploy.0 = Release|x64 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|x86.ActiveCfg = Release|Win32 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|x86.Build.0 = Release|Win32 - {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|x86.Deploy.0 = Release|Win32 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|ARM.ActiveCfg = Debug|ARM + {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|ARM.Build.0 = Debug|ARM + {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|ARM.Deploy.0 = Debug|ARM + {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|ARM64.Build.0 = Debug|ARM64 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|x64.ActiveCfg = Debug|x64 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|x64.Build.0 = Debug|x64 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|x64.Deploy.0 = Debug|x64 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|x86.ActiveCfg = Debug|Win32 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|x86.Build.0 = Debug|Win32 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|x86.Deploy.0 = Debug|Win32 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|ARM.ActiveCfg = Release|ARM + {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|ARM.Build.0 = Release|ARM + {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|ARM.Deploy.0 = Release|ARM + {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|ARM64.ActiveCfg = Release|ARM64 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|ARM64.Build.0 = Release|ARM64 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|ARM64.Deploy.0 = Release|ARM64 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|x64.ActiveCfg = Release|x64 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|x64.Build.0 = Release|x64 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|x64.Deploy.0 = Release|x64 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|x86.ActiveCfg = Release|Win32 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|x86.Build.0 = Release|Win32 + {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|x86.Deploy.0 = Release|Win32 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.ActiveCfg = Debug|ARM + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.Build.0 = Debug|ARM {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.ActiveCfg = Debug|ARM64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.Build.0 = Debug|ARM64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.ActiveCfg = Debug|x64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.Build.0 = Debug|x64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.ActiveCfg = Debug|Win32 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.Build.0 = Debug|Win32 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.ActiveCfg = Release|ARM + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.Build.0 = Release|ARM {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.ActiveCfg = Release|ARM64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.Build.0 = Release|ARM64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.ActiveCfg = Release|x64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.Build.0 = Release|x64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.ActiveCfg = Release|Win32 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.Build.0 = Release|Win32 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.ActiveCfg = Debug|ARM + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.Build.0 = Debug|ARM {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.ActiveCfg = Debug|ARM64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.Build.0 = Debug|ARM64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.ActiveCfg = Debug|x64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.Build.0 = Debug|x64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.ActiveCfg = Debug|Win32 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.Build.0 = Debug|Win32 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.ActiveCfg = Release|ARM + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.Build.0 = Release|ARM {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.ActiveCfg = Release|ARM64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.Build.0 = Release|ARM64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.ActiveCfg = Release|x64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.Build.0 = Release|x64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.ActiveCfg = Release|Win32 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.Build.0 = Release|Win32 - {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.Build.0 = Debug|ARM64 - {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.ActiveCfg = Debug|x64 - {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.Build.0 = Debug|x64 - {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.ActiveCfg = Debug|Win32 - {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.Build.0 = Debug|Win32 - {11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.ActiveCfg = Release|ARM64 - {11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.Build.0 = Release|ARM64 - {11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.ActiveCfg = Release|x64 - {11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.Build.0 = Release|x64 - {11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.ActiveCfg = Release|Win32 - {11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.Build.0 = Release|Win32 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.ActiveCfg = Debug|ARM + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.Build.0 = Debug|ARM {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.ActiveCfg = Debug|ARM64 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.Build.0 = Debug|ARM64 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.ActiveCfg = Debug|x64 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.Build.0 = Debug|x64 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.ActiveCfg = Debug|Win32 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.Build.0 = Debug|Win32 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.ActiveCfg = Release|ARM + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.Build.0 = Release|ARM {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.ActiveCfg = Release|ARM64 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.Build.0 = Release|ARM64 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.ActiveCfg = Release|x64 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.Build.0 = Release|x64 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.ActiveCfg = Release|Win32 {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.Build.0 = Release|Win32 + {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.ActiveCfg = Debug|ARM + {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.Build.0 = Debug|ARM {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM64.ActiveCfg = Debug|ARM64 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM64.Build.0 = Debug|ARM64 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.ActiveCfg = Debug|x64 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.Build.0 = Debug|x64 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.ActiveCfg = Debug|Win32 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.Build.0 = Debug|Win32 + {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.ActiveCfg = Release|ARM + {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.Build.0 = Release|ARM {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM64.ActiveCfg = Release|ARM64 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM64.Build.0 = Release|ARM64 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.ActiveCfg = Release|x64 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.Build.0 = Release|x64 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.ActiveCfg = Release|Win32 {A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.Build.0 = Release|Win32 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.ActiveCfg = Debug|ARM + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.Build.0 = Debug|ARM {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.ActiveCfg = Debug|ARM64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.Build.0 = Debug|ARM64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.ActiveCfg = Debug|x64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.Build.0 = Debug|x64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.ActiveCfg = Debug|Win32 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.Build.0 = Debug|Win32 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.ActiveCfg = Release|ARM + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.Build.0 = Release|ARM {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.ActiveCfg = Release|ARM64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.Build.0 = Release|ARM64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.ActiveCfg = Release|x64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.Build.0 = Release|x64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.ActiveCfg = Release|Win32 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.Build.0 = Release|Win32 - {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|ARM64.Build.0 = Debug|ARM64 - {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|x64.ActiveCfg = Debug|x64 - {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|x64.Build.0 = Debug|x64 - {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|x86.ActiveCfg = Debug|Win32 - {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|x86.Build.0 = Debug|Win32 - {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|ARM64.ActiveCfg = Release|ARM64 - {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|ARM64.Build.0 = Release|ARM64 - {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|x64.ActiveCfg = Release|x64 - {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|x64.Build.0 = Release|x64 - {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|x86.ActiveCfg = Release|Win32 - {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|x86.Build.0 = Release|Win32 + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|ARM.ActiveCfg = Debug|ARM + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|ARM.Build.0 = Debug|ARM + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|ARM64.Build.0 = Debug|ARM64 + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|x64.ActiveCfg = Debug|x64 + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|x64.Build.0 = Debug|x64 + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|x86.ActiveCfg = Debug|Win32 + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|x86.Build.0 = Debug|Win32 + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|ARM.ActiveCfg = Release|ARM + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|ARM.Build.0 = Release|ARM + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|ARM64.ActiveCfg = Release|ARM64 + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|ARM64.Build.0 = Release|ARM64 + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|x64.ActiveCfg = Release|x64 + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|x64.Build.0 = Release|x64 + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|x86.ActiveCfg = Release|Win32 + {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -176,16 +189,15 @@ Global GlobalSection(NestedProjects) = preSolution {A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} - {11C084A3-A57C-4296-A679-CAC17B603144} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {C38970C0-5FBF-4D69-90D8-CBAC225AE895} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {F7D32BD0-2749-483E-9A0D-1635EF7E3136} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {0CC28589-39E4-4288-B162-97B959F8B843} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {A62D504A-16B8-41D2-9F19-E2E86019E5E4} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} - {67A1076F-7790-4203-86EA-4402CCB5E782} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {2049DBE9-8D13-42C9-AE4B-413AE38FFFD0} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {EF074BA1-2D54-4D49-A28E-5E040B47CD2E} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D43FAD39-F619-437D-BB40-04A3982ACB6A} diff --git a/examples/basic/windows/VideoPlayer/.gitignore b/examples/basic/windows/VideoPlayer/.gitignore new file mode 100644 index 00000000..917243bd --- /dev/null +++ b/examples/basic/windows/VideoPlayer/.gitignore @@ -0,0 +1 @@ +/Bundle diff --git a/examples/basic/windows/VideoPlayer/App.cpp b/examples/basic/windows/VideoPlayer/App.cpp index 7630a218..2379674e 100644 --- a/examples/basic/windows/VideoPlayer/App.cpp +++ b/examples/basic/windows/VideoPlayer/App.cpp @@ -1,50 +1,79 @@ -#include "pch.h" - -#include "App.h" -#include "ReactPackageProvider.h" - -#include "winrt/ReactNativeVideoCPP.h" - -using namespace winrt::VideoPlayer; -using namespace winrt::VideoPlayer::implementation; - -///

-/// Initializes the singleton application object. This is the first line of -/// authored code executed, and as such is the logical equivalent of main() or -/// WinMain(). -/// -App::App() noexcept -{ - MainComponentName(L"VideoPlayer"); - -#if BUNDLE - JavaScriptBundleFile(L"index"); - InstanceSettings().UseWebDebugger(false); - InstanceSettings().UseFastRefresh(false); -#else - JavaScriptMainModuleName(L"index"); - InstanceSettings().UseWebDebugger(true); - InstanceSettings().UseFastRefresh(true); -#endif - -#if _DEBUG - InstanceSettings().EnableDeveloperMenu(true); -#else - InstanceSettings().EnableDeveloperMenu(false); -#endif - - PackageProviders().Append(make()); // Includes all modules in this project - - PackageProviders().Append(winrt::ReactNativeVideoCPP::ReactPackageProvider()); - - REACT_REGISTER_NATIVE_MODULE_PACKAGES(); //code-gen macro from autolink - - InitializeComponent(); - - // This works around a cpp/winrt bug with composable/aggregable types tracked - // by 22116519 - AddRef(); - m_inner.as<::IUnknown>()->Release(); -} - - +#include "pch.h" + +#include "App.h" + +#include "AutolinkedNativeModules.g.h" +#include "ReactPackageProvider.h" + +using namespace winrt::videoplayer; +using namespace winrt::videoplayer::implementation; +using namespace winrt; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Navigation; +using namespace Windows::ApplicationModel; + +/// +/// Initializes the singleton application object. This is the first line of +/// authored code executed, and as such is the logical equivalent of main() or +/// WinMain(). +/// +App::App() noexcept +{ +#if BUNDLE + JavaScriptBundleFile(L"src/index.windows"); + InstanceSettings().UseWebDebugger(false); + InstanceSettings().UseFastRefresh(false); +#else + JavaScriptMainModuleName(L"src/index"); + InstanceSettings().UseWebDebugger(true); + InstanceSettings().UseFastRefresh(true); +#endif + +#if _DEBUG + InstanceSettings().UseDeveloperSupport(true); +#else + InstanceSettings().UseDeveloperSupport(false); +#endif + + RegisterAutolinkedNativeModulePackages(PackageProviders()); // Includes any autolinked modules + + PackageProviders().Append(make()); // Includes all modules in this project + + InitializeComponent(); +} + +/// +/// Invoked when the application is launched normally by the end user. Other entry points +/// will be used such as when the application is launched to open a specific file. +/// +/// Details about the launch request and process. +void App::OnLaunched(activation::LaunchActivatedEventArgs const& e) +{ + super::OnLaunched(e); + + Frame rootFrame = Window::Current().Content().as(); + rootFrame.Navigate(xaml_typename(), box_value(e.Arguments())); +} + +/// +/// Invoked when application execution is being suspended. Application state is saved +/// without knowing whether the application will be terminated or resumed with the contents +/// of memory still intact. +/// +/// The source of the suspend request. +/// Details about the suspend request. +void App::OnSuspending([[maybe_unused]] IInspectable const& sender, [[maybe_unused]] SuspendingEventArgs const& e) +{ + // Save application state and stop any background activity +} + +/// +/// Invoked when Navigation to a certain page fails +/// +/// The Frame which failed navigation +/// Details about the navigation failure +void App::OnNavigationFailed(IInspectable const&, NavigationFailedEventArgs const& e) +{ + throw hresult_error(E_FAIL, hstring(L"Failed to load Page ") + e.SourcePageType().Name); +} diff --git a/examples/basic/windows/VideoPlayer/App.h b/examples/basic/windows/VideoPlayer/App.h index f2dc98d3..16a53393 100644 --- a/examples/basic/windows/VideoPlayer/App.h +++ b/examples/basic/windows/VideoPlayer/App.h @@ -1,15 +1,18 @@ -#pragma once - -#include "App.xaml.g.h" - - - -namespace winrt::VideoPlayer::implementation -{ - struct App : AppT - { - App() noexcept; - }; -} // namespace winrt::VideoPlayer::implementation - - +#pragma once + +#include "App.xaml.g.h" + +namespace activation = winrt::Windows::ApplicationModel::Activation; + +namespace winrt::videoplayer::implementation +{ + struct App : AppT + { + App() noexcept; + void OnLaunched(activation::LaunchActivatedEventArgs const&); + void OnSuspending(IInspectable const&, Windows::ApplicationModel::SuspendingEventArgs const&); + void OnNavigationFailed(IInspectable const&, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs const&); + private: + using super = AppT; + }; +} // namespace winrt::videoplayer::implementation diff --git a/examples/basic/windows/VideoPlayer/App.idl b/examples/basic/windows/VideoPlayer/App.idl index fca0646c..e327b433 100644 --- a/examples/basic/windows/VideoPlayer/App.idl +++ b/examples/basic/windows/VideoPlayer/App.idl @@ -1,3 +1,3 @@ -namespace VideoPlayer -{ -} +namespace videoplayer +{ +} diff --git a/examples/basic/windows/VideoPlayer/App.xaml b/examples/basic/windows/VideoPlayer/App.xaml index 1cf8c542..f1d79c8c 100644 --- a/examples/basic/windows/VideoPlayer/App.xaml +++ b/examples/basic/windows/VideoPlayer/App.xaml @@ -1,8 +1,8 @@  diff --git a/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.cpp b/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.cpp new file mode 100644 index 00000000..440fd52f --- /dev/null +++ b/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.cpp @@ -0,0 +1,18 @@ +// AutolinkedNativeModules.g.cpp contents generated by "react-native autolink-windows" +// clang-format off +#include "pch.h" +#include "AutolinkedNativeModules.g.h" + +// Includes from react-native-video +#include + +namespace winrt::Microsoft::ReactNative +{ + +void RegisterAutolinkedNativeModulePackages(winrt::Windows::Foundation::Collections::IVector const& packageProviders) +{ + // IReactPackageProviders from react-native-video + packageProviders.Append(winrt::ReactNativeVideoCPP::ReactPackageProvider()); +} + +} diff --git a/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.h b/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.h new file mode 100644 index 00000000..f28bb8be --- /dev/null +++ b/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.h @@ -0,0 +1,10 @@ +// AutolinkedNativeModules.g.h contents generated by "react-native autolink-windows" +// clang-format off +#pragma once + +namespace winrt::Microsoft::ReactNative +{ + +void RegisterAutolinkedNativeModulePackages(winrt::Windows::Foundation::Collections::IVector const& packageProviders); + +} diff --git a/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.props b/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.props new file mode 100644 index 00000000..f5db933f --- /dev/null +++ b/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.props @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.targets b/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.targets new file mode 100644 index 00000000..c2025402 --- /dev/null +++ b/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.targets @@ -0,0 +1,10 @@ + + + + + + + {0d1e54d3-4be1-4daf-98bf-124c28c85014} + + + diff --git a/examples/basic/windows/VideoPlayer/Bundle/.gitignore b/examples/basic/windows/VideoPlayer/Bundle/.gitignore deleted file mode 100644 index 005717ea..00000000 --- a/examples/basic/windows/VideoPlayer/Bundle/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/examples/basic/windows/VideoPlayer/MainPage.cpp b/examples/basic/windows/VideoPlayer/MainPage.cpp new file mode 100644 index 00000000..b94a5052 --- /dev/null +++ b/examples/basic/windows/VideoPlayer/MainPage.cpp @@ -0,0 +1,20 @@ +#include "pch.h" +#include "MainPage.h" +#if __has_include("MainPage.g.cpp") +#include "MainPage.g.cpp" +#endif + +#include "App.h" + +using namespace winrt; +using namespace Windows::UI::Xaml; + +namespace winrt::videoplayer::implementation +{ + MainPage::MainPage() + { + InitializeComponent(); + auto app = Application::Current().as(); + ReactRootView().ReactNativeHost(app->Host()); + } +} diff --git a/examples/basic/windows/VideoPlayer/MainPage.h b/examples/basic/windows/VideoPlayer/MainPage.h new file mode 100644 index 00000000..390ab853 --- /dev/null +++ b/examples/basic/windows/VideoPlayer/MainPage.h @@ -0,0 +1,19 @@ +#pragma once +#include "MainPage.g.h" +#include + +namespace winrt::videoplayer::implementation +{ + struct MainPage : MainPageT + { + MainPage(); + }; +} + +namespace winrt::videoplayer::factory_implementation +{ + struct MainPage : MainPageT + { + }; +} + diff --git a/examples/basic/windows/VideoPlayer/MainPage.idl b/examples/basic/windows/VideoPlayer/MainPage.idl new file mode 100644 index 00000000..051fd75e --- /dev/null +++ b/examples/basic/windows/VideoPlayer/MainPage.idl @@ -0,0 +1,8 @@ +namespace videoplayer +{ + [default_interface] + runtimeclass MainPage : Windows.UI.Xaml.Controls.Page + { + MainPage(); + } +} diff --git a/examples/basic/windows/VideoPlayer/MainPage.xaml b/examples/basic/windows/VideoPlayer/MainPage.xaml new file mode 100644 index 00000000..ca697091 --- /dev/null +++ b/examples/basic/windows/VideoPlayer/MainPage.xaml @@ -0,0 +1,16 @@ + + + diff --git a/examples/basic/windows/VideoPlayer/Package.appxmanifest b/examples/basic/windows/VideoPlayer/Package.appxmanifest index 13b3564f..f594008c 100644 --- a/examples/basic/windows/VideoPlayer/Package.appxmanifest +++ b/examples/basic/windows/VideoPlayer/Package.appxmanifest @@ -6,16 +6,16 @@ xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp"> - - + - VideoPlayer - react-native-video + videoplayer + nickf Assets\StoreLogo.png @@ -28,14 +28,15 @@ - + EntryPoint="videoplayer.App"> diff --git a/examples/basic/windows/VideoPlayer/PropertySheet.props b/examples/basic/windows/VideoPlayer/PropertySheet.props index 3e15bb90..5942ba39 100644 --- a/examples/basic/windows/VideoPlayer/PropertySheet.props +++ b/examples/basic/windows/VideoPlayer/PropertySheet.props @@ -1,16 +1,16 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/examples/basic/windows/VideoPlayer/ReactPackageProvider.cpp b/examples/basic/windows/VideoPlayer/ReactPackageProvider.cpp index e6127dd4..713453d9 100644 --- a/examples/basic/windows/VideoPlayer/ReactPackageProvider.cpp +++ b/examples/basic/windows/VideoPlayer/ReactPackageProvider.cpp @@ -1,20 +1,15 @@ -#include "pch.h" -#include "ReactPackageProvider.h" - -#include "NativeModules.h" - - - -using namespace winrt::Microsoft::ReactNative; - -namespace winrt::VideoPlayer::implementation -{ - -void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept -{ - AddAttributedModules(packageBuilder); -} - -} // namespace winrt::VideoPlayer::implementation - - +#include "pch.h" +#include "ReactPackageProvider.h" +#include "NativeModules.h" + +using namespace winrt::Microsoft::ReactNative; + +namespace winrt::videoplayer::implementation +{ + +void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept +{ + AddAttributedModules(packageBuilder); +} + +} // namespace winrt::videoplayer::implementation diff --git a/examples/basic/windows/VideoPlayer/ReactPackageProvider.h b/examples/basic/windows/VideoPlayer/ReactPackageProvider.h index d8e8df89..5f8a4cfe 100644 --- a/examples/basic/windows/VideoPlayer/ReactPackageProvider.h +++ b/examples/basic/windows/VideoPlayer/ReactPackageProvider.h @@ -1,20 +1,13 @@ -#pragma once - -#include "winrt/Microsoft.ReactNative.h" - - - -using namespace winrt::Microsoft::ReactNative; - -namespace winrt::VideoPlayer::implementation -{ - - struct ReactPackageProvider : winrt::implements - { - public: // IReactPackageProvider - void CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept; - }; - -} // namespace winrt::VideoPlayer::implementation - - +#pragma once + +#include "winrt/Microsoft.ReactNative.h" + +namespace winrt::videoplayer::implementation +{ + struct ReactPackageProvider : winrt::implements + { + public: // IReactPackageProvider + void CreatePackage(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept; + }; +} // namespace winrt::videoplayer::implementation + diff --git a/examples/basic/windows/VideoPlayer/VideoPlayer_TemporaryKey.pfx b/examples/basic/windows/VideoPlayer/VideoPlayer_TemporaryKey.pfx index b16850da859f614d01277a74b3d6d7853cf54807..b399e7402c435b3fe37fc955b0e32e2935bc497f 100644 GIT binary patch delta 2534 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 delta 2497 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 - - - + + + + \ No newline at end of file diff --git a/examples/basic/windows/VideoPlayer/pch.cpp b/examples/basic/windows/VideoPlayer/pch.cpp index e0d2ef1a..bcb5590b 100644 --- a/examples/basic/windows/VideoPlayer/pch.cpp +++ b/examples/basic/windows/VideoPlayer/pch.cpp @@ -1 +1 @@ -#include "pch.h" +#include "pch.h" diff --git a/examples/basic/windows/VideoPlayer/pch.h b/examples/basic/windows/VideoPlayer/pch.h index 6cd79994..13f3eecb 100644 --- a/examples/basic/windows/VideoPlayer/pch.h +++ b/examples/basic/windows/VideoPlayer/pch.h @@ -1,28 +1,26 @@ -#pragma once - -#define NOMINMAX - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "nativeModules.g.h" +#pragma once + +#define NOMINMAX + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include diff --git a/examples/basic/windows/VideoPlayer/VideoPlayer.vcxproj b/examples/basic/windows/VideoPlayer/videoplayer.vcxproj similarity index 58% rename from examples/basic/windows/VideoPlayer/VideoPlayer.vcxproj rename to examples/basic/windows/VideoPlayer/videoplayer.vcxproj index ebb90852..ef950d93 100644 --- a/examples/basic/windows/VideoPlayer/VideoPlayer.vcxproj +++ b/examples/basic/windows/VideoPlayer/videoplayer.vcxproj @@ -1,172 +1,196 @@ - - - - - true - true - true - {adf1cf02-8224-4167-a737-8cbe1a0d5208} - VideoPlayer - VideoPlayer - en-US - 15.0 - true - Windows Store - 10.0 - 10.0.18362.0 - 10.0.15063.0 - VideoPlayer_TemporaryKey.pfx - EB6A55E244E582B009FB1C12D3D915243789F63A - - - - - Debug - ARM64 - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM64 - - - Release - Win32 - - - Release - x64 - - - - Application - $(DefaultPlatformToolset) - Unicode - - - true - true - - - false - true - false - - - - - - - - - - - - - - - - - Use - pch.h - $(IntDir)pch.pch - Level4 - %(AdditionalOptions) /bigobj - 4453;28204 - - - - - _DEBUG;%(PreprocessorDefinitions) - - - - - NDEBUG;%(PreprocessorDefinitions) - - - - - - - App.xaml - - - - - Designer - - - - - Designer - - - - - - - - - - - - - - - Create - - - App.xaml - - - - - - App.xaml - - - - - - - - false - - - - - {765365e4-9553-4900-9f69-e26d4309c8da} - - - {f7d32bd0-2749-483e-9a0d-1635ef7e3136} - - - - - - npx --no-install react-native bundle --platform windows --entry-file index.js --bundle-output $(MSBuildThisFileDirectory)/Bundle/index.bundle --assets-dest $(MSBuildThisFileDirectory)/Bundle - - True - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - \ No newline at end of file + + + + + true + true + true + {bf9ff035-b976-4990-86fe-09d015e91c20} + videoplayer + videoplayer + en-US + 16.0 + true + Windows Store + 10.0 + 10.0.18362.0 + 10.0.16299.0 + videoplayer_TemporaryKey.pfx + 740AAD66EBCE590C21351A8D4323BDD9FB477469 + password + + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\ + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + Application + Unicode + + + true + true + + + false + true + false + + + + + + + + + + + + + + + + + Use + pch.h + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + 4453;28204 + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + + + MainPage.xaml + Code + + + + + + App.xaml + + + + + Designer + + + + + Designer + + + + + + + + + + + + + + MainPage.xaml + Code + + + + + Create + + + App.xaml + + + + + + App.xaml + + + MainPage.xaml + Code + + + + + + + false + + + + + Designer + + + + + + + + + This project references targets in your node_modules\react-native-windows folder. The missing file is {0}. + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + diff --git a/examples/basic/windows/VideoPlayer/VideoPlayer.vcxproj.filters b/examples/basic/windows/VideoPlayer/videoplayer.vcxproj.filters similarity index 90% rename from examples/basic/windows/VideoPlayer/VideoPlayer.vcxproj.filters rename to examples/basic/windows/VideoPlayer/videoplayer.vcxproj.filters index 28e3f728..2aa08b71 100644 --- a/examples/basic/windows/VideoPlayer/VideoPlayer.vcxproj.filters +++ b/examples/basic/windows/VideoPlayer/videoplayer.vcxproj.filters @@ -1,59 +1,63 @@ - - - - - - - - - - - - - - - - - - - - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - - - - - - {e48dc53e-40b1-40cb-970a-f89935452892} - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + + + + + + {e48dc53e-40b1-40cb-970a-f89935452892} + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/basic/yarn.lock b/examples/basic/yarn.lock index 6b8424bd..6aa6b9a0 100644 --- a/examples/basic/yarn.lock +++ b/examples/basic/yarn.lock @@ -2,681 +2,738 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" - integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: - "@babel/highlight" "^7.0.0" + "@babel/highlight" "^7.16.7" -"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.6.0.tgz#9b00f73554edd67bebc86df8303ef678be3d7b48" - integrity sha512-FuRhDRtsd6IptKpHXAa+4WPZYY2ZzgowkbLBecEDDSje1X/apG7jQM33or3NdOmjXBKWGOg4JmSiRfUfuTtHXw== +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.8.tgz#31560f9f29fdf1868de8cb55049538a1b9732a60" + integrity sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q== + +"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.5", "@babel/core@^7.8.4": + version "7.16.12" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.12.tgz#5edc53c1b71e54881315923ae2aedea2522bb784" + integrity sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg== dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.6.0" - "@babel/helpers" "^7.6.0" - "@babel/parser" "^7.6.0" - "@babel/template" "^7.6.0" - "@babel/traverse" "^7.6.0" - "@babel/types" "^7.6.0" - convert-source-map "^1.1.0" + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.16.8" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helpers" "^7.16.7" + "@babel/parser" "^7.16.12" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.10" + "@babel/types" "^7.16.8" + convert-source-map "^1.7.0" debug "^4.1.0" - json5 "^2.1.0" - lodash "^4.17.13" - resolve "^1.3.2" - semver "^5.4.1" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.0.0", "@babel/generator@^7.4.0", "@babel/generator@^7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.0.tgz#e2c21efbfd3293ad819a2359b448f002bfdfda56" - integrity sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA== +"@babel/generator@^7.16.8", "@babel/generator@^7.5.0": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe" + integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw== dependencies: - "@babel/types" "^7.6.0" + "@babel/types" "^7.16.8" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" - trim-right "^1.0.1" -"@babel/helper-annotate-as-pure@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32" - integrity sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q== +"@babel/helper-annotate-as-pure@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" + integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== dependencies: - "@babel/types" "^7.0.0" + "@babel/types" "^7.16.7" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz#6b69628dfe4087798e0c4ed98e3d4a6b2fbd2f5f" - integrity sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" + integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== dependencies: - "@babel/helper-explode-assignable-expression" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/helper-explode-assignable-expression" "^7.16.7" + "@babel/types" "^7.16.7" -"@babel/helper-builder-react-jsx@^7.3.0": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.3.0.tgz#a1ac95a5d2b3e88ae5e54846bf462eeb81b318a4" - integrity sha512-MjA9KgwCuPEkQd9ncSXvSyJ5y+j2sICHyrI0M3L+6fnS4wMSNDc1ARXsbTfbb2cXHn17VisSnU/sHFTCxVxSMw== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" + integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== dependencies: - "@babel/types" "^7.3.0" - esutils "^2.0.0" + "@babel/compat-data" "^7.16.4" + "@babel/helper-validator-option" "^7.16.7" + browserslist "^4.17.5" + semver "^6.3.0" -"@babel/helper-call-delegate@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz#87c1f8ca19ad552a736a7a27b1c1fcf8b1ff1f43" - integrity sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ== +"@babel/helper-create-class-features-plugin@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.10.tgz#8a6959b9cc818a88815ba3c5474619e9c0f2c21c" + integrity sha512-wDeej0pu3WN/ffTxMNCPW5UCiOav8IcLRxSIyp/9+IF2xJUM9h/OYjg0IJLHaL6F8oU8kqMz9nc1vryXhMsgXg== dependencies: - "@babel/helper-hoist-variables" "^7.4.4" - "@babel/traverse" "^7.4.4" - "@babel/types" "^7.4.4" + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" -"@babel/helper-create-class-features-plugin@^7.5.5", "@babel/helper-create-class-features-plugin@^7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.6.0.tgz#769711acca889be371e9bc2eb68641d55218021f" - integrity sha512-O1QWBko4fzGju6VoVvrZg0RROCVifcLxiApnGP3OWfWzvxRZFCoBD81K5ur5e3bVY2Vf/5rIJm8cqPKn8HUJng== +"@babel/helper-create-regexp-features-plugin@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz#0cb82b9bac358eb73bfbd73985a776bfa6b14d48" + integrity sha512-fk5A6ymfp+O5+p2yCkXAu5Kyj6v0xh0RBeNcAkYUMDvvAAoxvSKXn+Jb37t/yWFiQVDFK1ELpUTD8/aLhCPu+g== dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-member-expression-to-functions" "^7.5.5" - "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.5.5" - "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/helper-annotate-as-pure" "^7.16.7" + regexpu-core "^4.7.1" -"@babel/helper-define-map@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz#3dec32c2046f37e09b28c93eb0b103fd2a25d369" - integrity sha512-fTfxx7i0B5NJqvUOBBGREnrqbTxRh7zinBANpZXAVDlsZxYdclDp467G1sQ8VZYMnAURY3RpBUAgOYT9GfzHBg== +"@babel/helper-define-polyfill-provider@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" + integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/types" "^7.5.5" - lodash "^4.17.13" + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" -"@babel/helper-explode-assignable-expression@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz#537fa13f6f1674df745b0c00ec8fe4e99681c8f6" - integrity sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA== +"@babel/helper-environment-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== dependencies: - "@babel/traverse" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/types" "^7.16.7" -"@babel/helper-function-name@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" - integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw== +"@babel/helper-explode-assignable-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" + integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== dependencies: - "@babel/helper-get-function-arity" "^7.0.0" - "@babel/template" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/types" "^7.16.7" -"@babel/helper-get-function-arity@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3" - integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ== +"@babel/helper-function-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" + integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== dependencies: - "@babel/types" "^7.0.0" + "@babel/helper-get-function-arity" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/types" "^7.16.7" -"@babel/helper-hoist-variables@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz#0298b5f25c8c09c53102d52ac4a98f773eb2850a" - integrity sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w== +"@babel/helper-get-function-arity@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" + integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== dependencies: - "@babel/types" "^7.4.4" + "@babel/types" "^7.16.7" -"@babel/helper-member-expression-to-functions@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz#1fb5b8ec4453a93c439ee9fe3aeea4a84b76b590" - integrity sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA== +"@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== dependencies: - "@babel/types" "^7.5.5" + "@babel/types" "^7.16.7" -"@babel/helper-module-imports@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d" - integrity sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A== +"@babel/helper-member-expression-to-functions@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" + integrity sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q== dependencies: - "@babel/types" "^7.0.0" + "@babel/types" "^7.16.7" -"@babel/helper-module-transforms@^7.4.4": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz#f84ff8a09038dcbca1fd4355661a500937165b4a" - integrity sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw== +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/helper-simple-access" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.4.4" - "@babel/template" "^7.4.4" - "@babel/types" "^7.5.5" - lodash "^4.17.13" + "@babel/types" "^7.16.7" -"@babel/helper-optimise-call-expression@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz#a2920c5702b073c15de51106200aa8cad20497d5" - integrity sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g== +"@babel/helper-module-transforms@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" + integrity sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng== dependencies: - "@babel/types" "^7.0.0" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-simple-access" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" -"@babel/helper-plugin-utils@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" - integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== - -"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351" - integrity sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw== +"@babel/helper-optimise-call-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" + integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== dependencies: - lodash "^4.17.13" + "@babel/types" "^7.16.7" -"@babel/helper-remap-async-to-generator@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz#361d80821b6f38da75bd3f0785ece20a88c5fe7f" - integrity sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-wrap-function" "^7.1.0" - "@babel/template" "^7.1.0" - "@babel/traverse" "^7.1.0" - "@babel/types" "^7.0.0" +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" + integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== -"@babel/helper-replace-supers@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz#f84ce43df031222d2bad068d2626cb5799c34bc2" - integrity sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg== +"@babel/helper-replace-supers@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" + integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== dependencies: - "@babel/helper-member-expression-to-functions" "^7.5.5" - "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/traverse" "^7.5.5" - "@babel/types" "^7.5.5" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" -"@babel/helper-simple-access@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz#65eeb954c8c245beaa4e859da6188f39d71e585c" - integrity sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w== +"@babel/helper-simple-access@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" + integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== dependencies: - "@babel/template" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/types" "^7.16.7" -"@babel/helper-split-export-declaration@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" - integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q== +"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" + integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== dependencies: - "@babel/types" "^7.4.4" + "@babel/types" "^7.16.0" -"@babel/helper-wrap-function@^7.1.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa" - integrity sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ== +"@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/template" "^7.1.0" - "@babel/traverse" "^7.1.0" - "@babel/types" "^7.2.0" + "@babel/types" "^7.16.7" -"@babel/helpers@^7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.6.0.tgz#21961d16c6a3c3ab597325c34c465c0887d31c6e" - integrity sha512-W9kao7OBleOjfXtFGgArGRX6eCP0UEcA2ZWEWNkJdRZnHhW4eEbeswbG3EwaRsnQUAEGWYgMq1HsIXuNNNy2eQ== - dependencies: - "@babel/template" "^7.6.0" - "@babel/traverse" "^7.6.0" - "@babel/types" "^7.6.0" +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== -"@babel/highlight@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" - integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== +"@babel/helper-validator-option@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== + +"@babel/helpers@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.7.tgz#7e3504d708d50344112767c3542fc5e357fffefc" + integrity sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw== dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/highlight@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" - esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.0.tgz#3e05d0647432a8326cb28d0de03895ae5a57f39b" - integrity sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.10", "@babel/parser@^7.16.12", "@babel/parser@^7.16.7", "@babel/parser@^7.7.0": + version "7.16.12" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.12.tgz#9474794f9a650cf5e2f892444227f98e28cdf8b6" + integrity sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A== "@babel/plugin-external-helpers@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-external-helpers/-/plugin-external-helpers-7.2.0.tgz#7f4cb7dee651cd380d2034847d914288467a6be4" - integrity sha512-QFmtcCShFkyAsNtdCM3lJPmRe1iB+vPZymlB4LnDIKEBj2yKQLQKtoxXxJ8ePT5fwMl4QGg303p4mB0UsSI2/g== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-external-helpers/-/plugin-external-helpers-7.16.7.tgz#dd91e9b22d52f606461e7fcb4dc99a8b3392dd84" + integrity sha512-3MvRbPgl957CR3ZMeW/ukGrKDM3+m5vtTkgrBAKKbUgrAkb1molwjRqUvAYsCnwboN1vXgHStotdhAvTgQS/Gw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-proposal-class-properties@^7.0.0": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz#a974cfae1e37c3110e71f3c6a2e48b8e71958cd4" - integrity sha512-AF79FsnWFxjlaosgdi421vmYG6/jg79bVD0dpD44QdgobzHKuLZ6S3vl8la9qIeSwGi8i1fS0O1mfuDAAdo1/A== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" + integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== dependencies: - "@babel/helper-create-class-features-plugin" "^7.5.5" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-proposal-export-default-from@^7.0.0": - version "7.5.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.5.2.tgz#2c0ac2dcc36e3b2443fead2c3c5fc796fb1b5145" - integrity sha512-wr9Itk05L1/wyyZKVEmXWCdcsp/e185WUNl6AfYZeEKYaUPPvHXRDqO5K1VH7/UamYqGJowFRuCv30aDYZawsg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.16.7.tgz#a40ab158ca55627b71c5513f03d3469026a9e929" + integrity sha512-+cENpW1rgIjExn+o5c8Jw/4BuH4eGKKYvkMB8/0ZxFQ9mC0t4z09VsPIwNg6waF69QYC81zxGeAsREGuqQoKeg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-export-default-from" "^7.2.0" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-export-default-from" "^7.16.7" "@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.4.4.tgz#41c360d59481d88e0ce3a3f837df10121a769b39" - integrity sha512-Amph7Epui1Dh/xxUxS2+K22/MUi6+6JVTvy3P58tja3B6yKTSjwwx0/d83rF7551D6PVSSoplQb8GCwqec7HRw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" + integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.2.0" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58" - integrity sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz#94593ef1ddf37021a25bdcb5754c4a8d534b01d8" + integrity sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + "@babel/compat-data" "^7.16.4" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.16.7" "@babel/plugin-proposal-optional-catch-binding@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5" - integrity sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" + integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.0.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.6.0.tgz#e9bf1f9b9ba10c77c033082da75f068389041af8" - integrity sha512-kj4gkZ6qUggkprRq3Uh5KP8XnE1MdIO0J7MhdDX8+rAbB6dJ2UrensGIS+0NPZAaaJ1Vr0PN6oLUgXMU1uMcSg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" + integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-optional-chaining" "^7.2.0" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-syntax-class-properties@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.2.0.tgz#23b3b7b9bcdabd73672a9149f728cd3be6214812" - integrity sha512-UxYaGXYQ7rrKJS/PxIKRkv3exi05oH7rokBAsmCSsCxz1sVPZ7Fu6FzKoGgUvmY+0YgSkYHgUoCh5R5bCNBQlw== +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.0.0", "@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-dynamic-import@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz#69c159ffaf4998122161ad8ebc5e6d1f55df8612" - integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w== + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.2.0.tgz#edd83b7adc2e0d059e2467ca96c650ab6d2f3820" - integrity sha512-c7nqUnNST97BWPtoe+Ssi+fJukc9P9/JMZ71IOMNQWza2E+Psrd46N6AEvtw6pqK+gt7ChjXyrw4SPDO79f3Lw== +"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.16.7.tgz#fa89cf13b60de2c3f79acdc2b52a21174c6de060" + integrity sha512-4C3E4NsrLOgftKaTYTULhHsuQrGv3FHrBzOMDiS7UYKIpgGBkAdawg4h+EI8zPeK9M0fiIIh72hIwsI24K7MbA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.2.0.tgz#a765f061f803bc48f240c26f8747faf97c26bf7c" - integrity sha512-r6YMuZDWLtLlu0kqIim5o/3TNRAlWb073HwT3e2nKf9I8IIvOggPrnILYPsrrKilmn/mYEMCf/Z07w3yQJF6dg== +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.16.7", "@babel/plugin-syntax-flow@^7.2.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" + integrity sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz#0b85a3b4bc7cdf4cc4b8bf236335b907ca22e7c7" - integrity sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw== +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-nullish-coalescing-operator@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.2.0.tgz#f75083dfd5ade73e783db729bbd87e7b9efb7624" - integrity sha512-lRCEaKE+LTxDQtgbYajI04ddt6WW0WJq57xqkAZ+s11h4YgfRHhVA/Y2VhfPzzFD4qeLHWg32DMp9HooY4Kqlg== +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" - integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" + integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-syntax-optional-catch-binding@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c" - integrity sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w== +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-optional-chaining@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.2.0.tgz#a59d6ae8c167e7608eaa443fda9fa8fa6bf21dff" - integrity sha512-HtGCtvp5Uq/jH/WNUPkK6b7rufnCPLLlDAFN7cmACoIjaOOiXxUt3SswU5loHqrhtqTsa/WoLQ1OQ1AGuZqaWA== +"@babel/plugin-syntax-nullish-coalescing-operator@^7.0.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-typescript@^7.2.0": - version "7.3.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz#a7cc3f66119a9f7ebe2de5383cce193473d65991" - integrity sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag== +"@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.0.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-typescript@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" + integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550" - integrity sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" + integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-async-to-generator@^7.0.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e" - integrity sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-remap-async-to-generator" "^7.1.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190" - integrity sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" + integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.0.tgz#c49e21228c4bbd4068a35667e6d951c75439b1dc" - integrity sha512-tIt4E23+kw6TgL/edACZwP1OUKrjOTyMrFMLoT5IOFrfMRabCgekjqFd5o6PaAMildBu46oFkekIdMuGkkPEpA== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" + integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - lodash "^4.17.13" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-classes@^7.0.0": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz#d094299d9bd680a14a2a0edae38305ad60fb4de9" - integrity sha512-U2htCNK/6e9K7jGyJ++1p5XRU+LJjrwtoiVn9SzRlDT2KubcZ11OOwy3s24TjHxPgxNwonCYP7U2K51uVYCMDg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" + integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-define-map" "^7.5.5" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.5.5" - "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da" - integrity sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" + integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz#44bbe08b57f4480094d57d9ffbcd96d309075ba6" - integrity sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz#ca9588ae2d63978a4c29d3f33282d8603f618e23" + integrity sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-exponentiation-operator@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008" - integrity sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" + integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.4.4.tgz#d267a081f49a8705fc9146de0768c6b58dccd8f7" - integrity sha512-WyVedfeEIILYEaWGAUWzVNyqG4sfsNooMhXWsu/YzOvVGcsnPb5PguysjJqI3t3qiaYj0BR8T2f5njdjTGe44Q== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" + integrity sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.2.0" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-flow" "^7.16.7" "@babel/plugin-transform-for-of@^7.0.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556" - integrity sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" + integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-function-name@^7.0.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz#e1436116abb0610c2259094848754ac5230922ad" - integrity sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" + integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-literals@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1" - integrity sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" + integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz#fa10aa5c58a2cb6afcf2c9ffa8cb4d8b3d489a2d" - integrity sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" + integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.6.0.tgz#39dfe957de4420445f1fcf88b68a2e4aa4515486" - integrity sha512-Ma93Ix95PNSEngqomy5LSBMAQvYKVe3dy+JlVJSHEXZR5ASL9lQBedMiCyVtmTLraIDVRE3ZjTZvmXXD2Ozw3g== + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" + integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA== dependencies: - "@babel/helper-module-transforms" "^7.4.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-simple-access" "^7.1.0" - babel-plugin-dynamic-import-node "^2.3.0" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-simple-access" "^7.16.7" + babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-object-assign@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.2.0.tgz#6fdeea42be17040f119e38e23ea0f49f31968bde" - integrity sha512-nmE55cZBPFgUktbF2OuoZgPRadfxosLOpSgzEPYotKSls9J4pEPcembi8r78RU37Rph6UApCpNmsQA4QMWK9Ng== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.16.7.tgz#5fe08d63dccfeb6a33aa2638faf98e5c584100f8" + integrity sha512-R8mawvm3x0COTJtveuoqZIjNypn2FjfvXZr4pSQ8VhEFBuQGBz4XhHasZtHXjgXU4XptZ4HtGof3NoYc93ZH9Q== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-object-super@^7.0.0": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz#c70021df834073c65eb613b8679cc4a381d1a9f9" - integrity sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" + integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.5.5" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" -"@babel/plugin-transform-parameters@^7.0.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16" - integrity sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw== +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" + integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== dependencies: - "@babel/helper-call-delegate" "^7.4.4" - "@babel/helper-get-function-arity" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-property-literals@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz#03e33f653f5b25c4eb572c98b9485055b389e905" - integrity sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" + integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-display-name@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz#ebfaed87834ce8dc4279609a4f0c324c156e3eb0" - integrity sha512-Htf/tPa5haZvRMiNSQSFifK12gtr/8vwfr+A9y69uF0QcU77AVu4K7MiHEkTxF7lQoHOL0F9ErqgfNEAKgXj7A== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" + integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-react-jsx-self@^7.0.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz#f432ad0cba14c4a1faf44f0076c69e42a4d4479e" + integrity sha512-oe5VuWs7J9ilH3BCCApGoYjHoSO48vkjX2CbA5bFVhIuO2HKxA3vyF7rleA4o6/4rTDbk6r8hBW7Ul8E+UZrpA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx-source@^7.0.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.5.0.tgz#583b10c49cf057e237085bcbd8cc960bd83bd96b" - integrity sha512-58Q+Jsy4IDCZx7kqEZuSDdam/1oW8OdDX8f+Loo6xyxdfg1yF0GE2XNJQSTZCaMol93+FBzpWiPEwtbMloAcPg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz#1879c3f23629d287cc6186a6c683154509ec70c0" + integrity sha512-rONFiQz9vgbsnaMtQlZCjIRwhJvlrPET8TabIUK2hzlXw9B9s2Ieaxte1SCOOXMbWRHodbKixNf3BLcWVOQ8Bw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-jsx" "^7.2.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.3.0.tgz#f2cab99026631c767e2745a5368b331cfe8f5290" - integrity sha512-a/+aRb7R06WcKvQLOu4/TpjKOdvVEKRLWFpKcNuHhiREPgGRB4TQJxq07+EZLS8LFVYpfq1a5lDUnuMdcCpBKg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz#86a6a220552afd0e4e1f0388a68a372be7add0d4" + integrity sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag== dependencies: - "@babel/helper-builder-react-jsx" "^7.3.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-jsx" "^7.2.0" + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-jsx" "^7.16.7" + "@babel/types" "^7.16.7" "@babel/plugin-transform-regenerator@^7.0.0": - version "7.4.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz#629dc82512c55cee01341fb27bdfcb210354680f" - integrity sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" + integrity sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q== dependencies: - regenerator-transform "^0.14.0" + regenerator-transform "^0.14.2" "@babel/plugin-transform-runtime@^7.0.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.6.0.tgz#85a3cce402b28586138e368fce20ab3019b9713e" - integrity sha512-Da8tMf7uClzwUm/pnJ1S93m/aRXmoYNDD7TkHua8xBDdaAs54uZpTWvEt6NGwmoVMb9mZbntfTqmG2oSzN/7Vg== + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz#53d9fd3496daedce1dd99639097fa5d14f4c7c2c" + integrity sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w== dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - resolve "^1.8.1" - semver "^5.5.1" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + babel-plugin-polyfill-corejs2 "^0.3.0" + babel-plugin-polyfill-corejs3 "^0.5.0" + babel-plugin-polyfill-regenerator "^0.3.0" + semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0" - integrity sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" + integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-spread@^7.0.0": - version "7.2.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz#3103a9abe22f742b6d406ecd3cd49b774919b406" - integrity sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" + integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-transform-sticky-regex@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1" - integrity sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" + integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0" - integrity sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" + integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== dependencies: - "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-typescript@^7.0.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.6.0.tgz#48d78405f1aa856ebeea7288a48a19ed8da377a6" - integrity sha512-yzw7EopOOr6saONZ3KA3lpizKnWRTe+rfBqg4AmQbSow7ik7fqmzrfIqt053osLwLE2AaTqGinLM2tl6+M/uog== +"@babel/plugin-transform-typescript@^7.5.0": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" + integrity sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.6.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-typescript" "^7.2.0" + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-typescript" "^7.16.7" "@babel/plugin-transform-unicode-regex@^7.0.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz#ab4634bb4f14d36728bf5978322b35587787970f" - integrity sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" + integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.5.4" + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/register@^7.0.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.6.0.tgz#76b6f466714680f4becafd45beeb2a7b87431abf" - integrity sha512-78BomdN8el+x/nkup9KwtjJXuptW5oXMFmP11WoM2VJBjxrKv4grC3qjpLL8RGGUYUGsm57xnjYFM2uom+jWUQ== + version "7.16.9" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.16.9.tgz#fcfb23cfdd9ad95c9771e58183de83b513857806" + integrity sha512-jJ72wcghdRIlENfvALcyODhNoGE5j75cYHdC+aQMh6cU/P86tiiXTp9XYZct1UxUMo/4+BgQRyNZEGx0KWGS+g== dependencies: + clone-deep "^4.0.1" find-cache-dir "^2.0.0" - lodash "^4.17.13" - mkdirp "^0.5.1" + make-dir "^2.1.0" pirates "^4.0.0" - source-map-support "^0.5.9" + source-map-support "^0.5.16" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.0.tgz#4fc1d642a9fd0299754e8b5de62c631cf5568205" - integrity sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ== +"@babel/runtime-corejs3@^7.12.1": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.16.8.tgz#ea533d96eda6fdc76b1812248e9fbd0c11d4a1a7" + integrity sha512-3fKhuICS1lMz0plI5ktOE/yEtBRMVxplzRkdn6mJQ197XiY0JnrzYV0+Mxozq3JZ8SBV9Ecurmw1XsGbwOf+Sg== dependencies: - regenerator-runtime "^0.13.2" + core-js-pure "^3.20.2" + regenerator-runtime "^0.13.4" -"@babel/runtime@^7.4.0": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" - integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.8.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa" + integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6" - integrity sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ== +"@babel/template@^7.0.0", "@babel/template@^7.16.7", "@babel/template@^7.3.3": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.6.0" - "@babel/types" "^7.6.0" + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5", "@babel/traverse@^7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.6.0.tgz#389391d510f79be7ce2ddd6717be66d3fed4b516" - integrity sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.10", "@babel/traverse@^7.16.7", "@babel/traverse@^7.7.0": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.10.tgz#448f940defbe95b5a8029975b051f75993e8239f" + integrity sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw== dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.6.0" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.4.4" - "@babel/parser" "^7.6.0" - "@babel/types" "^7.6.0" + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.16.8" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.16.10" + "@babel/types" "^7.16.8" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5", "@babel/types@^7.6.0": - version "7.6.1" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.6.1.tgz#53abf3308add3ac2a2884d539151c57c4b3ac648" - integrity sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g== +"@babel/types@^7.0.0", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.7.0": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1" + integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg== dependencies: - esutils "^2.0.2" - lodash "^4.17.13" + "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + "@cnakazawa/watch@^1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" - integrity sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== dependencies: exec-sh "^0.3.2" minimist "^1.2.0" "@hapi/address@2.x.x": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.1.tgz#61395b5ed94c4cb19c2dc4c85969cff3d40d583f" - integrity sha512-DYuHzu978pP1XW1GD3HGvLnAFjbQTIgc2+V153FGkbS2pgo9haigCdwBnUDrbhaOkgiJlbZvoEqDrcxSLHpiWA== + version "2.1.4" + resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" + integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== "@hapi/bourne@1.x.x": version "1.3.2" resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a" integrity sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA== -"@hapi/hoek@8.x.x": - version "8.2.4" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.2.4.tgz#684a14f4ca35d46f44abc87dfc696e5e4fe8a020" - integrity sha512-Ze5SDNt325yZvNO7s5C4fXDscjJ6dcqLFXJQ/M7dZRQCewuDj2iDUuBi6jLQt+APbW9RjjVEvLr35FXuOEqjow== +"@hapi/hoek@8.x.x", "@hapi/hoek@^8.3.0": + version "8.5.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" + integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== "@hapi/joi@^15.0.3": version "15.1.1" @@ -689,13 +746,29 @@ "@hapi/topo" "3.x.x" "@hapi/topo@3.x.x": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.3.tgz#c7a02e0d936596d29f184e6d7fdc07e8b5efce11" - integrity sha512-JmS9/vQK6dcUYn7wc2YZTqzIKubAQcJKu2KCKAru6es482U5RT5fP1EXCPtlXpiK7PR0On/kpQKI4fRKkzpZBQ== + version "3.1.6" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29" + integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ== dependencies: - "@hapi/hoek" "8.x.x" + "@hapi/hoek" "^8.3.0" -"@jest/console@^24.7.1", "@jest/console@^24.9.0": +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== @@ -704,49 +777,59 @@ chalk "^2.0.1" slash "^2.0.0" -"@jest/core@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4" - integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A== +"@jest/console@^25.5.0": + version "25.5.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.5.0.tgz#770800799d510f37329c508a9edd0b7b447d9abb" + integrity sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw== dependencies: - "@jest/console" "^24.7.1" - "@jest/reporters" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - ansi-escapes "^3.0.0" - chalk "^2.0.1" - exit "^0.1.2" - graceful-fs "^4.1.15" - jest-changed-files "^24.9.0" - jest-config "^24.9.0" - jest-haste-map "^24.9.0" - jest-message-util "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-resolve-dependencies "^24.9.0" - jest-runner "^24.9.0" - jest-runtime "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - jest-watcher "^24.9.0" - micromatch "^3.1.10" - p-each-series "^1.0.0" - realpath-native "^1.1.0" - rimraf "^2.5.4" - slash "^2.0.0" - strip-ansi "^5.0.0" + "@jest/types" "^25.5.0" + chalk "^3.0.0" + jest-message-util "^25.5.0" + jest-util "^25.5.0" + slash "^3.0.0" -"@jest/environment@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18" - integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ== +"@jest/core@^25.5.4": + version "25.5.4" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-25.5.4.tgz#3ef7412f7339210f003cdf36646bbca786efe7b4" + integrity sha512-3uSo7laYxF00Dg/DMgbn4xMJKmDdWvZnf89n8Xj/5/AeQ2dOQmn6b6Hkj/MleyzZWXpwv+WSdYWl4cLsy2JsoA== dependencies: - "@jest/fake-timers" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" + "@jest/console" "^25.5.0" + "@jest/reporters" "^25.5.1" + "@jest/test-result" "^25.5.0" + "@jest/transform" "^25.5.1" + "@jest/types" "^25.5.0" + ansi-escapes "^4.2.1" + chalk "^3.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-changed-files "^25.5.0" + jest-config "^25.5.4" + jest-haste-map "^25.5.1" + jest-message-util "^25.5.0" + jest-regex-util "^25.2.6" + jest-resolve "^25.5.1" + jest-resolve-dependencies "^25.5.4" + jest-runner "^25.5.4" + jest-runtime "^25.5.4" + jest-snapshot "^25.5.1" + jest-util "^25.5.0" + jest-validate "^25.5.0" + jest-watcher "^25.5.0" + micromatch "^4.0.2" + p-each-series "^2.1.0" + realpath-native "^2.0.0" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^25.5.0": + version "25.5.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-25.5.0.tgz#aa33b0c21a716c65686638e7ef816c0e3a0c7b37" + integrity sha512-U2VXPEqL07E/V7pSZMSQCvV5Ea4lqOlT+0ZFijl/i316cRMHvZ4qC+jBdryd+lmRetjQo0YIQr6cVPNxxK87mA== + dependencies: + "@jest/fake-timers" "^25.5.0" + "@jest/types" "^25.5.0" + jest-mock "^25.5.0" "@jest/fake-timers@^24.9.0": version "24.9.0" @@ -757,34 +840,59 @@ jest-message-util "^24.9.0" jest-mock "^24.9.0" -"@jest/reporters@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43" - integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw== +"@jest/fake-timers@^25.5.0": + version "25.5.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.5.0.tgz#46352e00533c024c90c2bc2ad9f2959f7f114185" + integrity sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ== dependencies: - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" + "@jest/types" "^25.5.0" + jest-message-util "^25.5.0" + jest-mock "^25.5.0" + jest-util "^25.5.0" + lolex "^5.0.0" + +"@jest/globals@^25.5.2": + version "25.5.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-25.5.2.tgz#5e45e9de8d228716af3257eeb3991cc2e162ca88" + integrity sha512-AgAS/Ny7Q2RCIj5kZ+0MuKM1wbF0WMLxbCVl/GOMoCNbODRdJ541IxJ98xnZdVSZXivKpJlNPIWa3QmY0l4CXA== + dependencies: + "@jest/environment" "^25.5.0" + "@jest/types" "^25.5.0" + expect "^25.5.0" + +"@jest/reporters@^25.5.1": + version "25.5.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-25.5.1.tgz#cb686bcc680f664c2dbaf7ed873e93aa6811538b" + integrity sha512-3jbd8pPDTuhYJ7vqiHXbSwTJQNavczPs+f1kRprRDxETeE3u6srJ+f0NPuwvOmk+lmunZzPkYWIFZDLHQPkviw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^25.5.0" + "@jest/test-result" "^25.5.0" + "@jest/transform" "^25.5.1" + "@jest/types" "^25.5.0" + chalk "^3.0.0" + collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" - istanbul-lib-coverage "^2.0.2" - istanbul-lib-instrument "^3.0.1" - istanbul-lib-report "^2.0.4" - istanbul-lib-source-maps "^3.0.1" - istanbul-reports "^2.2.6" - jest-haste-map "^24.9.0" - jest-resolve "^24.9.0" - jest-runtime "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.6.0" - node-notifier "^5.4.2" - slash "^2.0.0" + graceful-fs "^4.2.4" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^4.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + jest-haste-map "^25.5.1" + jest-resolve "^25.5.1" + jest-util "^25.5.0" + jest-worker "^25.5.0" + slash "^3.0.0" source-map "^0.6.0" - string-length "^2.0.0" + string-length "^3.1.0" + terminal-link "^2.0.0" + v8-to-istanbul "^4.1.3" + optionalDependencies: + node-notifier "^6.0.0" -"@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0": +"@jest/source-map@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== @@ -793,6 +901,15 @@ graceful-fs "^4.1.15" source-map "^0.6.0" +"@jest/source-map@^25.5.0": + version "25.5.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.5.0.tgz#df5c20d6050aa292c2c6d3f0d2c7606af315bd1b" + integrity sha512-eIGx0xN12yVpMcPaVpjXPnn3N30QGJCJQSkEDUt9x1fI1Gdvb07Ml6K5iN2hG7NmMP6FDmtPEssE3z6doOYUwQ== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.4" + source-map "^0.6.0" + "@jest/test-result@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" @@ -802,37 +919,48 @@ "@jest/types" "^24.9.0" "@types/istanbul-lib-coverage" "^2.0.0" -"@jest/test-sequencer@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31" - integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A== +"@jest/test-result@^25.5.0": + version "25.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.5.0.tgz#139a043230cdeffe9ba2d8341b27f2efc77ce87c" + integrity sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A== dependencies: - "@jest/test-result" "^24.9.0" - jest-haste-map "^24.9.0" - jest-runner "^24.9.0" - jest-runtime "^24.9.0" + "@jest/console" "^25.5.0" + "@jest/types" "^25.5.0" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" -"@jest/transform@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" - integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== +"@jest/test-sequencer@^25.5.4": + version "25.5.4" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-25.5.4.tgz#9b4e685b36954c38d0f052e596d28161bdc8b737" + integrity sha512-pTJGEkSeg1EkCO2YWq6hbFvKNXk8ejqlxiOg1jBNLnWrgXOkdY6UmqZpwGFXNnRt9B8nO1uWMzLLZ4eCmhkPNA== + dependencies: + "@jest/test-result" "^25.5.0" + graceful-fs "^4.2.4" + jest-haste-map "^25.5.1" + jest-runner "^25.5.4" + jest-runtime "^25.5.4" + +"@jest/transform@^25.5.1": + version "25.5.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-25.5.1.tgz#0469ddc17699dd2bf985db55fa0fb9309f5c2db3" + integrity sha512-Y8CEoVwXb4QwA6Y/9uDkn0Xfz0finGkieuV0xkdF9UtZGJeLukD5nLkaVrVsODB1ojRWlaoD0AJZpVHCSnJEvg== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^24.9.0" - babel-plugin-istanbul "^5.1.0" - chalk "^2.0.1" + "@jest/types" "^25.5.0" + babel-plugin-istanbul "^6.0.0" + chalk "^3.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.1.15" - jest-haste-map "^24.9.0" - jest-regex-util "^24.9.0" - jest-util "^24.9.0" - micromatch "^3.1.10" + graceful-fs "^4.2.4" + jest-haste-map "^25.5.1" + jest-regex-util "^25.2.6" + jest-util "^25.5.0" + micromatch "^4.0.2" pirates "^4.0.1" - realpath-native "^1.1.0" - slash "^2.0.0" + realpath-native "^2.0.0" + slash "^3.0.0" source-map "^0.6.1" - write-file-atomic "2.4.1" + write-file-atomic "^3.0.0" "@jest/types@^24.9.0": version "24.9.0" @@ -853,123 +981,196 @@ "@types/yargs" "^15.0.0" chalk "^3.0.0" -"@react-native-community/cli-debugger-ui@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-3.0.0.tgz#d01d08d1e5ddc1633d82c7d84d48fff07bd39416" - integrity sha512-m3X+iWLsK/H7/b7PpbNO33eQayR/+M26la4ZbYe1KRke5Umg4PIWsvg21O8Tw4uJcY8LA5hsP+rBi/syBkBf0g== +"@react-native-community/cli-debugger-ui@^4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-4.13.1.tgz#07de6d4dab80ec49231de1f1fbf658b4ad39b32c" + integrity sha512-UFnkg5RTq3s2X15fSkrWY9+5BKOFjihNSnJjTV2H5PtTUFbd55qnxxPw8CxSfK0bXb1IrSvCESprk2LEpqr5cg== dependencies: serve-static "^1.13.1" -"@react-native-community/cli-platform-android@^3.0.0": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-3.1.4.tgz#61f964dc311623e60b0fb29c5f3732cc8a6f076f" - integrity sha512-ClSdY20F0gzWVLTqCv7vHjnUqOcuq10jd9GgHX6lGSc2GI+Ql3/aQg3tmG4uY3KXNNwAv3U8QCoYgg1WGfwiHA== +"@react-native-community/cli-hermes@^4.13.0": + version "4.13.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-4.13.0.tgz#6243ed9c709dad5e523f1ccd7d21066b32f2899d" + integrity sha512-oG+w0Uby6rSGsUkJGLvMQctZ5eVRLLfhf84lLyz942OEDxFRa9U19YJxOe9FmgCKtotbYiM3P/XhK+SVCuerPQ== dependencies: - "@react-native-community/cli-tools" "^3.0.0" - chalk "^2.4.2" + "@react-native-community/cli-platform-android" "^4.13.0" + "@react-native-community/cli-tools" "^4.13.0" + chalk "^3.0.0" + hermes-profile-transformer "^0.0.6" + ip "^1.1.5" + +"@react-native-community/cli-platform-android@^4.10.0", "@react-native-community/cli-platform-android@^4.13.0": + version "4.13.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-4.13.0.tgz#922681ec82ee1aadd993598b814df1152118be02" + integrity sha512-3i8sX8GklEytUZwPnojuoFbCjIRzMugCdzDIdZ9UNmi/OhD4/8mLGO0dgXfT4sMWjZwu3qjy45sFfk2zOAgHbA== + dependencies: + "@react-native-community/cli-tools" "^4.13.0" + chalk "^3.0.0" execa "^1.0.0" + fs-extra "^8.1.0" + glob "^7.1.3" jetifier "^1.6.2" - logkitty "^0.6.0" + lodash "^4.17.15" + logkitty "^0.7.1" slash "^3.0.0" xmldoc "^1.1.2" -"@react-native-community/cli-platform-ios@^3.0.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-3.2.0.tgz#c469444f5993c9e6737a4b16d78cf033e3702f00" - integrity sha512-pzEnx68H6+mHBq5jsMrr3UmAmkrLSMlC9BZ4yoUdfUXCQq6/R70zNYvH4hjUw8h2Al7Kgq53UzHUsM0ph8TSWQ== +"@react-native-community/cli-platform-ios@^4.10.0": + version "4.13.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-4.13.0.tgz#a738915c68cac86df54e578b59a1311ea62b1aef" + integrity sha512-6THlTu8zp62efkzimfGr3VIuQJ2514o+vScZERJCV1xgEi8XtV7mb/ZKt9o6Y9WGxKKkc0E0b/aVAtgy+L27CA== dependencies: - "@react-native-community/cli-tools" "^3.0.0" - chalk "^2.4.2" + "@react-native-community/cli-tools" "^4.13.0" + chalk "^3.0.0" + glob "^7.1.3" js-yaml "^3.13.1" + lodash "^4.17.15" + plist "^3.0.1" xcode "^2.0.0" -"@react-native-community/cli-tools@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-3.0.0.tgz#fe48b80822ed7e49b8af051f9fe41e22a2a710b1" - integrity sha512-8IhQKZdf3E4CR8T7HhkPGgorot/cLkRDgneJFDSWk/wCYZAuUh4NEAdumQV7N0jLSMWX7xxiWUPi94lOBxVY9g== +"@react-native-community/cli-server-api@^4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-4.13.1.tgz#bee7ee9702afce848e9d6ca3dcd5669b99b125bd" + integrity sha512-vQzsFKD9CjHthA2ehTQX8c7uIzlI9A7ejaIow1I9RlEnLraPH2QqVDmzIdbdh5Od47UPbRzamCgAP8Bnqv3qwQ== dependencies: - chalk "^2.4.2" - lodash "^4.17.5" - mime "^2.4.1" - node-fetch "^2.5.0" - -"@react-native-community/cli-types@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-3.0.0.tgz#488d46605cb05e88537e030f38da236eeda74652" - integrity sha512-ng6Tm537E/M42GjE4TRUxQyL8sRfClcL7bQWblOCoxPZzJ2J3bdALsjeG3vDnVCIfI/R0AeFalN9KjMt0+Z/Zg== - -"@react-native-community/cli@^3.0.0": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-3.2.1.tgz#2a466801eb6080a1f73358c5d740c53c24ed8c6f" - integrity sha512-bZ/bfZ+9r1gQSxp6t7+00DcpC6vmbVYSvzUCFM/yo5k8bhsDdcy8aocscIaXXVGG+v9Edri/Q7hH9ks7L18/Rg== - dependencies: - "@hapi/joi" "^15.0.3" - "@react-native-community/cli-debugger-ui" "^3.0.0" - "@react-native-community/cli-tools" "^3.0.0" - "@react-native-community/cli-types" "^3.0.0" - chalk "^2.4.2" - command-exists "^1.2.8" - commander "^2.19.0" + "@react-native-community/cli-debugger-ui" "^4.13.1" + "@react-native-community/cli-tools" "^4.13.0" compression "^1.7.1" connect "^3.6.5" + errorhandler "^1.5.0" + nocache "^2.1.0" + pretty-format "^25.1.0" + serve-static "^1.13.1" + ws "^1.1.0" + +"@react-native-community/cli-tools@^4.13.0": + version "4.13.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-4.13.0.tgz#b406463d33af16cedc4305a9a9257ed32845cf1b" + integrity sha512-s4f489h5+EJksn4CfheLgv5PGOM0CDmK1UEBLw2t/ncWs3cW2VI7vXzndcd/WJHTv3GntJhXDcJMuL+Z2IAOgg== + dependencies: + chalk "^3.0.0" + lodash "^4.17.15" + mime "^2.4.1" + node-fetch "^2.6.0" + open "^6.2.0" + shell-quote "1.6.1" + +"@react-native-community/cli-types@^4.10.1": + version "4.10.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-4.10.1.tgz#d68a2dcd1649d3b3774823c64e5e9ce55bfbe1c9" + integrity sha512-ael2f1onoPF3vF7YqHGWy7NnafzGu+yp88BbFbP0ydoCP2xGSUzmZVw0zakPTC040Id+JQ9WeFczujMkDy6jYQ== + +"@react-native-community/cli@^4.10.0": + version "4.14.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-4.14.0.tgz#bb106a98341bfa2db36060091ff90bfe82ea4f55" + integrity sha512-EYJKBuxFxAu/iwNUfwDq41FjORpvSh1wvQ3qsHjzcR5uaGlWEOJrd3uNJDuKBAS0TVvbEesLF9NEXipjyRVr4Q== + dependencies: + "@hapi/joi" "^15.0.3" + "@react-native-community/cli-debugger-ui" "^4.13.1" + "@react-native-community/cli-hermes" "^4.13.0" + "@react-native-community/cli-server-api" "^4.13.1" + "@react-native-community/cli-tools" "^4.13.0" + "@react-native-community/cli-types" "^4.10.1" + chalk "^3.0.0" + command-exists "^1.2.8" + commander "^2.19.0" cosmiconfig "^5.1.0" deepmerge "^3.2.0" - didyoumean "^1.2.1" - envinfo "^7.1.0" - errorhandler "^1.5.0" + envinfo "^7.7.2" execa "^1.0.0" find-up "^4.1.0" - fs-extra "^7.0.1" - glob "^7.1.1" + fs-extra "^8.1.0" + glob "^7.1.3" graceful-fs "^4.1.3" inquirer "^3.0.6" - lodash "^4.17.5" - metro "^0.56.0" - metro-config "^0.56.0" - metro-core "^0.56.0" - metro-react-native-babel-transformer "^0.56.0" + leven "^3.1.0" + lodash "^4.17.15" + metro "^0.59.0" + metro-config "^0.59.0" + metro-core "^0.59.0" + metro-react-native-babel-transformer "^0.59.0" + metro-resolver "^0.59.0" minimist "^1.2.0" mkdirp "^0.5.1" - morgan "^1.9.0" - node-notifier "^5.2.1" - open "^6.2.0" + node-stream-zip "^1.9.1" ora "^3.4.0" - plist "^3.0.0" - pretty-format "^25.1.0" + pretty-format "^25.2.0" semver "^6.3.0" serve-static "^1.13.1" - shell-quote "1.6.1" strip-ansi "^5.2.0" sudo-prompt "^9.0.0" wcwidth "^1.0.1" - ws "^1.1.0" -"@react-native-community/eslint-config@^0.0.5": - version "0.0.5" - resolved "https://registry.yarnpkg.com/@react-native-community/eslint-config/-/eslint-config-0.0.5.tgz#584f6493258202a57efc22e7be66966e43832795" - integrity sha512-jwO2tnKaTPTLX5XYXMHGEnFdf543SU7jz98/OF5mDH3b7lP+BOaCD+jVfqqHoDRkcqyPlYiR1CgwVGWpi0vMWg== +"@react-native-community/eslint-config@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@react-native-community/eslint-config/-/eslint-config-1.1.0.tgz#2dacad06dd44d13e778510864473fc6091f080c0" + integrity sha512-hwb1hC28BhkwLwnO6vDISV6XZbipw2RIEhBVBN+pE7AUG9HjFXxoksiiOSoYgox9C8g86VJwHnKpak/3NnVBkQ== dependencies: - "@typescript-eslint/eslint-plugin" "^1.5.0" - "@typescript-eslint/parser" "^1.5.0" - babel-eslint "10.0.1" - eslint-plugin-eslint-comments "^3.1.1" + "@react-native-community/eslint-plugin" "^1.1.0" + "@typescript-eslint/eslint-plugin" "^2.25.0" + "@typescript-eslint/parser" "^2.25.0" + babel-eslint "10.1.0" + eslint-config-prettier "^6.10.1" + eslint-plugin-eslint-comments "^3.1.2" eslint-plugin-flowtype "2.50.3" eslint-plugin-jest "22.4.1" - eslint-plugin-prettier "2.6.2" - eslint-plugin-react "7.12.4" - eslint-plugin-react-hooks "^1.5.1" - eslint-plugin-react-native "3.6.0" - prettier "1.16.4" + eslint-plugin-prettier "3.1.2" + eslint-plugin-react "7.19.0" + eslint-plugin-react-hooks "^3.0.0" + eslint-plugin-react-native "3.8.1" + prettier "^2.0.2" + +"@react-native-community/eslint-plugin@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.1.0.tgz#e42b1bef12d2415411519fd528e64b593b1363dc" + integrity sha512-W/J0fNYVO01tioHjvYWQ9m6RgndVtbElzYozBq1ZPrHO/iCzlqoySHl4gO/fpCl9QEFjvJfjPgtPMTMlsoq5DQ== "@react-native-picker/picker@^1.9.11": version "1.16.8" resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-1.16.8.tgz#2126ca54d4a5a3e9ea5e3f39ad1e6643f8e4b3d4" integrity sha512-pacdQDX6V6EmjF+HoiIh6u++qx4mTK0WnhgUHRc01B+Qt5eoeUwseBqmqfTSXTx/aHDEd6PiIw7UGvKgFoqgFQ== -"@types/babel__core@^7.1.0": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.3.tgz#e441ea7df63cd080dfcd02ab199e6d16a735fc30" - integrity sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA== +"@react-native-windows/cli@0.63.14": + version "0.63.14" + resolved "https://registry.yarnpkg.com/@react-native-windows/cli/-/cli-0.63.14.tgz#b077f2d090f2d63f2defa71926c4de77b0041ea8" + integrity sha512-RqKnAwrsA2aKqP4gYLktOrqorqvIiUMXUhe07GPh/GDzoizVgSjcgRD4x8hoxNXbU66fYFm+whvCBgfn88Ffpg== + dependencies: + "@react-native-windows/telemetry" "^0.63.5" + "@xmldom/xmldom" "^0.7.5" + chalk "^3.0.0" + cli-spinners "^2.2.0" + envinfo "^7.5.0" + find-up "^4.1.0" + glob "^7.1.1" + inquirer "^3.0.6" + mustache "^4.0.1" + nuget-exe "5.8.0" + ora "^3.4.0" + semver "^7.1.3" + shelljs "^0.8.4" + username "^5.1.0" + uuid "^3.3.2" + xml-parser "^1.2.1" + xpath "^0.0.27" + +"@react-native-windows/telemetry@^0.63.5": + version "0.63.5" + resolved "https://registry.yarnpkg.com/@react-native-windows/telemetry/-/telemetry-0.63.5.tgz#77d3a490b26490a642e7d7ce21cd5c06b768f4be" + integrity sha512-Ifd54IIcGbPjwAGfjJkRfdk5+SNtc9oCWgKMrJszIrlJ1REYVUXBo+6Xw3taKyA1Jf1eCB7DWlb98/NbqdLgcg== + dependencies: + applicationinsights "^1.8.8" + +"@sinonjs/commons@^1.7.0": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== + dependencies: + type-detect "4.0.8" + +"@types/babel__core@^7.1.7": + version "7.1.18" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" + integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -978,24 +1179,24 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.0.2.tgz#d2112a6b21fad600d7674274293c85dce0cb47fc" - integrity sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ== + version "7.6.4" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" + integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" - integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg== + version "7.4.1" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" + integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.0.7" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.7.tgz#2496e9ff56196cc1429c72034e07eab6121b6f3f" - integrity sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw== + version "7.14.2" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" + integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== dependencies: "@babel/types" "^7.3.0" @@ -1004,30 +1205,52 @@ resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" - integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== +"@types/graceful-fs@^4.1.2": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== "@types/istanbul-lib-report@*": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#e5471e7fa33c61358dd38426189c037a58433b8c" - integrity sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg== + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz#7a8cbf6a406f36c8add871625b278eaf0b0d255a" - integrity sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA== + version "1.1.2" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz#e875cc689e47bce549ec81f3df5e6f6f11cfaeb2" + integrity sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" "@types/json-schema@^7.0.3": - version "7.0.3" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" - integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A== + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + +"@types/node@*": + version "17.0.14" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.14.tgz#33b9b94f789a8fedd30a68efdbca4dbb06b61f20" + integrity sha512-SbjLmERksKOGzWzPNuW7fJM7fk3YXVTFiZWB/Hs99gwhk+/dnrQRPBQjPW9aO+fi1tAffi9PrwFvsmOKmDTyng== + +"@types/normalize-package-data@^2.4.0": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== + +"@types/prettier@^1.19.0": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" + integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== "@types/prop-types@*": version "15.7.5" @@ -1035,16 +1258,16 @@ integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== "@types/react-native@^0.67.6": - version "0.67.6" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.67.6.tgz#9a7de5feba6065aec9f44f9a1e8f6e55ee5d015c" - integrity sha512-NM6atxrefIXMLE/PyQ1bIQjQ/lWLdls3uVxItzKvNUUVZlGqgn/uGN4MarM9quSf90uSqJYPIAeAgTtBTUjhgg== + version "0.67.7" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.67.7.tgz#903b7d56fb6a95ca0de897f2925c04ee0d4adee3" + integrity sha512-G7vi9vE226diNNXVNbIa8HH/wPxMWHTHkn9iQtQSezaWzO5WNsKZZW2/TOzCehtBDVx4MZieTs6GsdtpBBttMA== dependencies: "@types/react" "*" "@types/react@*", "@types/react@^18.0.6": - version "18.0.6" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.6.tgz#30206c3830af6ce8639b91ace5868bc2d3d1d96c" - integrity sha512-bPqwzJRzKtfI0mVYr5R+1o9BOE8UEXefwc1LwcBtfnaAn6OoqMhLa/91VA8aeWfDPJt1kHvYKI8RHcQybZLHHA== + version "18.0.9" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.9.tgz#d6712a38bd6cd83469603e7359511126f122e878" + integrity sha512-9bjbg1hJHUm4De19L1cHiW0Jvx3geel6Qczhjd0qY5VKVE2X5+x77YxAepuCwVh4vrgZJdgEJw48zrhRIeF4Nw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -1061,70 +1284,76 @@ integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== "@types/yargs-parser@*": - version "13.1.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228" - integrity sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg== + version "20.2.1" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" + integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== "@types/yargs@^13.0.0": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.2.tgz#a64674fc0149574ecd90ba746e932b5a5f7b3653" - integrity sha512-lwwgizwk/bIIU+3ELORkyuOgDjCh7zuWDFqRtPPhhVgq9N1F7CvLNKg1TX4f2duwtKQ0p044Au9r1PLIXHrIzQ== + version "13.0.12" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.12.tgz#d895a88c703b78af0465a9de88aa92c61430b092" + integrity sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ== dependencies: "@types/yargs-parser" "*" "@types/yargs@^15.0.0": - version "15.0.11" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.11.tgz#361d7579ecdac1527687bcebf9946621c12ab78c" - integrity sha512-jfcNBxHFYJ4nPIacsi3woz1+kvUO6s1CyeEhtnDHBjHUMNj5UlW2GynmnSgiJJEdNg9yW5C8lfoNRZrHGv5EqA== + version "15.0.14" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" + integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^1.5.0": - version "1.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz#22fed9b16ddfeb402fd7bcde56307820f6ebc49f" - integrity sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g== +"@typescript-eslint/eslint-plugin@^2.25.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9" + integrity sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ== dependencies: - "@typescript-eslint/experimental-utils" "1.13.0" - eslint-utils "^1.3.1" + "@typescript-eslint/experimental-utils" "2.34.0" functional-red-black-tree "^1.0.1" - regexpp "^2.0.1" - tsutils "^3.7.0" + regexpp "^3.0.0" + tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@1.13.0": - version "1.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz#b08c60d780c0067de2fb44b04b432f540138301e" - integrity sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg== +"@typescript-eslint/experimental-utils@2.34.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" + integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "1.13.0" - eslint-scope "^4.0.0" + "@typescript-eslint/typescript-estree" "2.34.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" -"@typescript-eslint/parser@^1.5.0": - version "1.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.13.0.tgz#61ac7811ea52791c47dc9fd4dd4a184fae9ac355" - integrity sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ== +"@typescript-eslint/parser@^2.25.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.34.0.tgz#50252630ca319685420e9a39ca05fe185a256bc8" + integrity sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "1.13.0" - "@typescript-eslint/typescript-estree" "1.13.0" - eslint-visitor-keys "^1.0.0" + "@typescript-eslint/experimental-utils" "2.34.0" + "@typescript-eslint/typescript-estree" "2.34.0" + eslint-visitor-keys "^1.1.0" -"@typescript-eslint/typescript-estree@1.13.0": - version "1.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz#8140f17d0f60c03619798f1d628b8434913dc32e" - integrity sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw== +"@typescript-eslint/typescript-estree@2.34.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" + integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== dependencies: - lodash.unescape "4.0.1" - semver "5.5.0" + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + +"@xmldom/xmldom@^0.7.5": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.5.tgz#09fa51e356d07d0be200642b0e4f91d8e6dd408d" + integrity sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A== abab@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" - integrity sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w== - -abbrev@1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== abort-controller@^3.0.0: version "3.0.0" @@ -1136,6 +1365,7 @@ abort-controller@^3.0.0: absolute-path@^0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7" + integrity sha1-p4di+9rftSl76ZsV01p4Wy8JW/c= accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" @@ -1145,58 +1375,48 @@ accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-globals@^4.1.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.2.tgz#4e2c2313a597fd589720395f6354b41cd5ec8006" - integrity sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ== +acorn-globals@^4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" + integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== dependencies: acorn "^6.0.1" acorn-walk "^6.0.1" -acorn-jsx@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f" - integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw== +acorn-jsx@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^6.0.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913" - integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw== - -acorn@^5.5.3: - version "5.7.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" - integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== + version "6.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" + integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== acorn@^6.0.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" - integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== -acorn@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a" - integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ== +acorn@^7.1.0, acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -ajv@^6.10.0, ajv@^6.10.2: - version "6.10.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" - integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: - fast-deep-equal "^2.0.1" + fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^6.5.5: - version "6.10.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" - integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" +anser@^1.4.9: + version "1.4.10" + resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b" + integrity sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww== ansi-colors@^1.0.1: version "1.1.0" @@ -1212,11 +1432,18 @@ ansi-cyan@^0.1.1: dependencies: ansi-wrap "0.1.0" -ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: +ansi-escapes@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + ansi-fragments@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/ansi-fragments/-/ansi-fragments-0.2.1.tgz#24409c56c4cc37817c3d7caa99d8969e2de5a05e" @@ -1240,10 +1467,6 @@ ansi-red@^0.1.1: dependencies: ansi-wrap "0.1.0" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" @@ -1254,10 +1477,10 @@ ansi-regex@^4.0.0, ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.0, ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" @@ -1286,17 +1509,23 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -are-we-there-yet@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz#80e470e95a084794fe1899262c5667c6e88de1b3" +anymatch@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: - delegates "^1.0.0" - readable-stream "^2.0.0 || ^1.1.13" + normalize-path "^3.0.0" + picomatch "^2.0.4" + +applicationinsights@^1.8.8: + version "1.8.10" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.8.10.tgz#fffa482cd1519880fb888536a87081ac05130667" + integrity sha512-ZLDA7mShh4mP2Z/HlFolmvhBPX1LfnbIWXrselyYVA7EKjHhri1fZzpu2EiWAmfbRxNBY6fRjoPJWbx5giKy4A== + dependencies: + cls-hooked "^4.2.2" + continuation-local-storage "^3.2.1" + diagnostic-channel "0.3.1" + diagnostic-channel-publishers "0.4.4" argparse@^1.0.7: version "1.0.10" @@ -1318,11 +1547,7 @@ arr-diff@^4.0.0: resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= -arr-flatten@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" - -arr-flatten@^1.1.0: +arr-flatten@^1.0.1, arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== @@ -1345,22 +1570,28 @@ array-equal@^1.0.0: array-filter@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= -array-includes@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" - integrity sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0= +array-includes@^3.1.1: + version "3.1.4" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" + integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== dependencies: - define-properties "^1.1.2" - es-abstract "^1.7.0" + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + is-string "^1.0.7" array-map@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI= array-reduce@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= array-slice@^0.2.3: version "0.2.3" @@ -1372,21 +1603,22 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -art@^0.10.0: - version "0.10.1" - resolved "https://registry.yarnpkg.com/art/-/art-0.10.1.tgz#38541883e399225c5e193ff246e8f157cf7b2146" - -asap@~2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" +asap@~2.0.3, asap@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" -assert-plus@^1.0.0: +assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= assign-symbols@^1.0.0: version "1.0.0" @@ -1398,23 +1630,34 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== -async-limiter@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" - integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== +async-hook-jl@^1.7.6: + version "1.7.6" + resolved "https://registry.yarnpkg.com/async-hook-jl/-/async-hook-jl-1.7.6.tgz#4fd25c2f864dbaf279c610d73bf97b1b28595e68" + integrity sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg== + dependencies: + stack-chain "^1.3.7" + +async-listener@^0.6.0: + version "0.6.10" + resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.10.tgz#a7c97abe570ba602d782273c0de60a51e3e17cbc" + integrity sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw== + dependencies: + semver "^5.3.0" + shimmer "^1.1.0" async@^2.4.0: - version "2.6.2" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" - integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== dependencies: - lodash "^4.17.11" + lodash "^4.17.14" asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -atob@^2.1.1: +atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== @@ -1425,57 +1668,61 @@ aws-sign2@~0.7.0: integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" - integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -babel-eslint@10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed" - integrity sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ== +babel-eslint@10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.0.0" - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" - eslint-scope "3.7.1" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" eslint-visitor-keys "^1.0.0" + resolve "^1.12.0" -babel-jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" - integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== +babel-jest@^25.1.0, babel-jest@^25.5.1: + version "25.5.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.5.1.tgz#bc2e6101f849d6f6aec09720ffc7bc5332e62853" + integrity sha512-9dA9+GmMjIzgPnYtkhBg73gOo/RHqPmLruP3BaGL4KEX3Dwz6pI8auSN8G8+iuEG90+GSswyKvslN+JYSaacaQ== dependencies: - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/babel__core" "^7.1.0" - babel-plugin-istanbul "^5.1.0" - babel-preset-jest "^24.9.0" - chalk "^2.4.2" - slash "^2.0.0" + "@jest/transform" "^25.5.1" + "@jest/types" "^25.5.0" + "@types/babel__core" "^7.1.7" + babel-plugin-istanbul "^6.0.0" + babel-preset-jest "^25.5.0" + chalk "^3.0.0" + graceful-fs "^4.2.4" + slash "^3.0.0" -babel-plugin-dynamic-import-node@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" - integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== dependencies: object.assign "^4.1.0" -babel-plugin-istanbul@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854" - integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw== +babel-plugin-istanbul@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - find-up "^3.0.0" - istanbul-lib-instrument "^3.3.0" - test-exclude "^5.2.3" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" -babel-plugin-jest-hoist@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" - integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw== +babel-plugin-jest-hoist@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.5.0.tgz#129c80ba5c7fc75baf3a45b93e2e372d57ca2677" + integrity sha512-u+/W+WAjMlvoocYGTwthAiQSxDcJAyHpQ6oWlHdFZaaN+Rlk8Q7iiwDPg2lN/FyJtAYnKjFxbn7xus4HCFkg5g== dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" "@types/babel__traverse" "^7.0.6" babel-plugin-module-resolver@^4.1.0: @@ -1489,15 +1736,56 @@ babel-plugin-module-resolver@^4.1.0: reselect "^4.0.0" resolve "^1.13.1" +babel-plugin-polyfill-corejs2@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" + integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.3.1" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.1.tgz#d66183bf10976ea677f4149a7fcc4d8df43d4060" + integrity sha512-TihqEe4sQcb/QcPJvxe94/9RZuLQuF1+To4WqQcRvc+3J3gLCPIPgDKzGLG6zmQLfH3nn25heRuDNkS2KR4I8A== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.1" + core-js-compat "^3.20.0" + +babel-plugin-polyfill-regenerator@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" + integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.1" + babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== -babel-preset-fbjs@^3.1.2, babel-preset-fbjs@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.2.0.tgz#c0e6347d3e0379ed84b3c2434d3467567aa05297" - integrity sha512-5Jo+JeWiVz2wHUUyAlvb/sSYnXNig9r+HqGAOSfh5Fzxp7SnAaR/tEGRJ1ZX7C77kfk82658w6R5Z+uPATTD9g== +babel-preset-current-node-syntax@^0.1.2: + version "0.1.4" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.4.tgz#826f1f8e7245ad534714ba001f84f7e906c3b615" + integrity sha512-5/INNCYhUGqw7VbVjT/hb3ucjgkVHKXY7lX3ZjlN4gm565VyFmJUrJ/h+h16ECVB38R/9SF6aACydpKMLZ/c9w== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +babel-preset-fbjs@^3.2.0, babel-preset-fbjs@^3.3.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" + integrity sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== dependencies: "@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-object-rest-spread" "^7.0.0" @@ -1527,31 +1815,23 @@ babel-preset-fbjs@^3.1.2, babel-preset-fbjs@^3.2.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" -babel-preset-jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" - integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== +babel-preset-jest@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-25.5.0.tgz#c1d7f191829487a907764c65307faa0e66590b49" + integrity sha512-8ZczygctQkBU+63DtSOKGh7tFL0CeCuz+1ieud9lJ1WPQ9O6A1a/r+LGn6Y705PA6whHQ3T1XuB/PmpfNYf8Fw== dependencies: - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - babel-plugin-jest-hoist "^24.9.0" - -balanced-match@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + babel-plugin-jest-hoist "^25.5.0" + babel-preset-current-node-syntax "^0.1.2" balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" - -base64-js@^1.2.3: - version "1.3.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" - integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== +base64-js@^1.1.2, base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== base@^0.11.1: version "0.11.2" @@ -1566,44 +1846,38 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -basic-auth@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" - integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== - dependencies: - safe-buffer "5.1.2" - bcrypt-pbkdf@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: tweetnacl "^0.14.3" -big-integer@^1.6.7: - version "1.6.44" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.44.tgz#4ee9ae5f5839fc11ade338fea216b4513454a539" - integrity sha512-7MzElZPTyJ2fNvBkPxtFQ2fWIkVmuzw41+BZHSzpEq3ymB2MfeKp1+yXl/tS75xCx+WnyV+yb0kp+K1C3UNwmQ== +big-integer@1.6.x: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== -bplist-creator@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.0.7.tgz#37df1536092824b87c42f957b01344117372ae45" - integrity sha1-N98VNgkoJLh8QvlXsBNEEXNyrkU= +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== dependencies: - stream-buffers "~2.2.0" + file-uri-to-path "1.0.0" -bplist-parser@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.1.1.tgz#d60d5dcc20cba6dc7e1f299b35d3e1f95dafbae6" - integrity sha1-1g1dzCDLptx+HymbNdPh+V2vuuY= +bplist-creator@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" + integrity sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg== dependencies: - big-integer "^1.6.7" + stream-buffers "2.2.x" -brace-expansion@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" +bplist-parser@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.3.0.tgz#ba50666370f61bbf94881636cd9f7d23c5286090" + integrity sha512-zgmaRvT6AN1JpPPV+S0a1/FAtoxSreYDccZGIqEMSvZl9DMe70mJ7MFzpxa1X+gHVdkToE2haRUHHMiW1OdejA== dependencies: - balanced-match "^0.4.1" - concat-map "0.0.1" + big-integer "1.6.x" brace-expansion@^1.1.7: version "1.1.11" @@ -1629,10 +1903,17 @@ braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" -browser-process-hrtime@^0.1.2: - version "0.1.3" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" - integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browser-resolve@^1.11.3: version "1.11.3" @@ -1641,10 +1922,21 @@ browser-resolve@^1.11.3: dependencies: resolve "1.1.7" -bser@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" - integrity sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk= +browserslist@^4.17.5, browserslist@^4.19.1: + version "4.19.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" + integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== + dependencies: + caniuse-lite "^1.0.30001286" + electron-to-chromium "^1.4.17" + escalade "^3.1.1" + node-releases "^2.0.1" + picocolors "^1.0.0" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" @@ -1654,17 +1946,9 @@ buffer-crc32@^0.2.13: integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -buffer-shims@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" - -builtin-modules@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== bytes@3.0.0: version "3.0.0" @@ -1686,6 +1970,14 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + caller-callsite@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" @@ -1710,16 +2002,16 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= - camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +caniuse-lite@^1.0.30001286: + version "1.0.30001304" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001304.tgz#38af55ed3fc8220cb13e35e6e7309c8c65a05559" + integrity sha512-bdsfZd6K6ap87AGqSHJP/s1V+U6Z5lyrcbBu3ovbCCf8cSYpwTtGrCBObMpJqwxfTbLW6YTIdbb1jEeTelcpYQ== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -1730,6 +2022,7 @@ capture-exit@^2.0.0: caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" @@ -1748,6 +2041,14 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chardet@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" @@ -1758,11 +2059,6 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chownr@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" - integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== - ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -1785,36 +2081,27 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-spinners@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" - integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" -cli-spinners@^2.2.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047" - integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ== +cli-spinners@^2.0.0, cli-spinners@^2.2.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== cli-width@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== -cliui@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" - integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^5.0.0: version "5.0.0" @@ -1825,18 +2112,47 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= +cls-hooked@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" + integrity sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw== + dependencies: + async-hook-jl "^1.7.6" + emitter-listener "^1.0.1" + semver "^5.4.1" + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== collection-visit@^1.0.0: version "1.0.0" @@ -1876,9 +2192,9 @@ color-support@^1.1.3: integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== colorette@^1.0.7: - version "1.1.0" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.1.0.tgz#1f943e5a357fac10b4e0f5aaef3b14cdc1af6ec7" - integrity sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg== + version "1.4.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" @@ -1892,10 +2208,10 @@ command-exists@^1.2.8: resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== -commander@^2.19.0, commander@~2.20.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" - integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== +commander@^2.19.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commander@~2.13.0: version "2.13.0" @@ -1905,6 +2221,7 @@ commander@~2.13.0: commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= component-emitter@^1.2.1: version "1.3.0" @@ -1912,11 +2229,11 @@ component-emitter@^1.2.1: integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== compressible@~2.0.16: - version "2.0.17" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1" - integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw== + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: - mime-db ">= 1.40.0 < 2" + mime-db ">= 1.43.0 < 2" compression@^1.7.1: version "1.7.4" @@ -1934,6 +2251,7 @@ compression@^1.7.1: concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= concat-stream@^1.6.0: version "1.6.2" @@ -1955,19 +2273,18 @@ connect@^3.6.5: parseurl "~1.3.3" utils-merge "1.0.1" -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= +continuation-local-storage@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#11f613f74e914fe9b34c92ad2d28fe6ae1db7ffb" + integrity sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA== + dependencies: + async-listener "^0.6.0" + emitter-listener "^1.1.1" -convert-source-map@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.4.0.tgz#e3dad195bf61bfe13a7a3c73e9876ec14a0268f3" - -convert-source-map@^1.4.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" - integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" @@ -1976,22 +2293,33 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-js@^1.0.0: - version "1.2.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" +core-js-compat@^3.20.0: + version "3.20.3" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.20.3.tgz#d71f85f94eb5e4bea3407412e549daa083d23bd6" + integrity sha512-c8M5h0IkNZ+I92QhIpuSijOxGAcj3lgpsWdkCqmUTZNwidujF4r3pi6x1DCN+Vcs5qTS2XWWMfWSuCqyupX8gw== + dependencies: + browserslist "^4.19.1" + semver "7.0.0" -core-js@^2.2.2: - version "2.4.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" +core-js-pure@^3.20.2: + version "3.20.3" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.20.3.tgz#6cc4f36da06c61d95254efc54024fe4797fd5d02" + integrity sha512-Q2H6tQ5MtPtcC7f3HxJ48i4Q7T9ybPKgvWyuH7JXIoNa2pm0KuBnycsET/qw1SLLZYfbsbrZQNMeIOClb+6WIA== core-js@^2.4.1: - version "2.6.9" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" - integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== + version "2.6.12" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" + integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== -core-util-is@~1.0.0: +core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: version "5.2.1" @@ -2003,16 +2331,7 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: js-yaml "^3.13.1" parse-json "^4.0.0" -create-react-class@^15.6.3: - version "15.6.3" - resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036" - integrity sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg== - dependencies: - fbjs "^0.8.9" - loose-envify "^1.3.1" - object-assign "^4.1.1" - -cross-spawn@^5.0.1, cross-spawn@^5.1.0: +cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= @@ -2032,17 +2351,31 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": - version "0.3.6" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.6.tgz#f85206cee04efa841f3c5982a74ba96ab20d65ad" - integrity sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A== - -cssstyle@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.2.2.tgz#427ea4d585b18624f6fdbf9de7a2a1a3ba713077" - integrity sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow== +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: - cssom "0.3.x" + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssom@^0.4.1: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" csstype@^3.0.2: version "3.0.11" @@ -2052,10 +2385,11 @@ csstype@^3.0.2: dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: assert-plus "^1.0.0" -data-urls@^1.0.0: +data-urls@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== @@ -2065,60 +2399,49 @@ data-urls@^1.0.0: whatwg-url "^7.0.0" dayjs@^1.8.15: - version "1.8.16" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.16.tgz#2a3771de537255191b947957af2fd90012e71e64" - integrity sha512-XPmqzWz/EJiaRHjBqSJ2s6hE/BUoCIHKgdS2QPtTQtKcS9E4/Qn0WomoH1lXanWCzri+g7zPcuNV4aTZ8PMORQ== + version "1.10.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" + integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== -debug@2.6.9, debug@^2.3.3: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^2.2.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351" - dependencies: - ms "0.7.2" - -debug@^3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== dependencies: - ms "^2.1.1" + ms "2.1.2" -decamelize@^1.1.1, decamelize@^1.2.0: +decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.3.0.tgz#d3c47fd6f3a93d517b14426b0628a17b0125f5f7" integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -2126,7 +2449,7 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-properties@^1.1.2, define-properties@^1.1.3: +define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== @@ -2158,14 +2481,12 @@ define-property@^2.0.2: delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= denodeify@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" + integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE= depd@~1.1.2: version "1.1.2" @@ -2175,26 +2496,29 @@ depd@~1.1.2: destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -detect-newline@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" - integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= +diagnostic-channel-publishers@0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.4.4.tgz#57c3b80b7e7f576f95be3a257d5e94550f0082d6" + integrity sha512-l126t01d2ZS9EreskvEtZPrcgstuvH3rbKy82oUhUrVmBaGx4hO9wECdl3cvZbKDYjMF3QJDB5z5dL9yWAjvZQ== -didyoumean@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.1.tgz#e92edfdada6537d484d73c0172fd1eba0c4976ff" - integrity sha1-6S7f2tplN9SE1zwBcv0eugxJdv8= +diagnostic-channel@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.3.1.tgz#7faa143e107f861be3046539eb4908faab3f53fd" + integrity sha512-6eb9YRrimz8oTr5+JDzGmSYnXy5V7YnK5y/hd8AUDK1MssHjQKm9LlD6NSrHx4vMDF3+e/spI2hmWTviElgWZA== + dependencies: + semver "^5.3.0" -diff-sequences@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" - integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== +diff-sequences@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" + integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== doctrine@^2.1.0: version "2.1.0" @@ -2218,58 +2542,68 @@ domexception@^1.0.1: webidl-conversions "^4.0.2" ecc-jsbn@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: jsbn "~0.1.0" + safer-buffer "^2.1.0" ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.4.17: + version "1.4.59" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.59.tgz#657f2588c048fb95975779f8fea101fad854de89" + integrity sha512-AOJ3cAE0TWxz4fQ9zkND5hWrQg16nsZKVz9INOot1oV//u4wWu5xrj9CQMmPTYskkZRunSRc9sAnr4EkexXokg== eme-encryption-scheme-polyfill@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.0.4.tgz#7d818302af3f3b19d5974255dcc92dc087413845" integrity sha512-MHYJX1v145Pjj2YJTrVVuJOYyXrxGVy8LWf6kV5M4jrV/GyoeuJKyTuD+GaD+VAiE8Ip+MptiH4dXk6ZVmMNow== +emitter-listener@^1.0.1, emitter-listener@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" + integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ== + dependencies: + shimmer "^1.2.0" + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= encoding@^0.1.11: - version "0.1.12" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== dependencies: - iconv-lite "~0.4.13" + iconv-lite "^0.6.2" end-of-stream@^1.1.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" - integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" -envinfo@^7.1.0: - version "7.3.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.3.1.tgz#892e42f7bf858b3446d9414ad240dbaf8da52f09" - integrity sha512-GvXiDTqLYrORVSCuJCsWHPXF5BFvoWMQA9xX4YVjPT1jyS3aZEHUBwjzxU/6LTPF9ReHgVEbX7IEN5UvSXHw/A== - -envinfo@^7.5.0: - version "7.7.3" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.7.3.tgz#4b2d8622e3e7366afb8091b23ed95569ea0208cc" - integrity sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA== - -error-ex@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" - dependencies: - is-arrayish "^0.2.1" +envinfo@^7.5.0, envinfo@^7.7.2: + version "7.8.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== error-ex@^1.3.1: version "1.3.2" @@ -2278,6 +2612,13 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-stack-parser@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" + integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ== + dependencies: + stackframe "^1.1.1" + errorhandler@^1.5.0: version "1.5.1" resolved "https://registry.yarnpkg.com/errorhandler/-/errorhandler-1.5.1.tgz#b9ba5d17cf90744cd1e851357a6e75bf806a9a91" @@ -2286,67 +2627,84 @@ errorhandler@^1.5.0: accepts "~1.3.7" escape-html "~1.0.3" -es-abstract@^1.11.0, es-abstract@^1.7.0: - version "1.14.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.2.tgz#7ce108fad83068c8783c3cdf62e504e084d8c497" - integrity sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg== +es-abstract@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== dependencies: - es-to-primitive "^1.2.0" + call-bind "^1.0.2" + es-to-primitive "^1.2.1" function-bind "^1.1.1" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" has "^1.0.3" - has-symbols "^1.0.0" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-inspect "^1.6.0" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.1" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.1" + object-inspect "^1.11.0" object-keys "^1.1.1" - string.prototype.trimleft "^2.0.0" - string.prototype.trimright "^2.0.0" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" -es-abstract@^1.5.1: - version "1.13.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" - integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== - dependencies: - es-to-primitive "^1.2.0" - function-bind "^1.1.1" - has "^1.0.3" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-keys "^1.0.12" - -es-to-primitive@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" - integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" is-date-object "^1.0.1" is-symbol "^1.0.2" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escodegen@^1.9.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510" - integrity sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw== +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escodegen@^1.11.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== dependencies: - esprima "^3.1.3" + esprima "^4.0.1" estraverse "^4.2.0" esutils "^2.0.2" optionator "^0.8.1" optionalDependencies: source-map "~0.6.1" -eslint-plugin-eslint-comments@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.1.2.tgz#4ef6c488dbe06aa1627fea107b3e5d059fc8a395" - integrity sha512-QexaqrNeteFfRTad96W+Vi4Zj1KFbkHHNMMaHZEYcovKav6gdomyGzaxSDSL3GoIyUOo078wRAdYlu1caiauIQ== +eslint-config-prettier@^6.10.1: + version "6.15.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz#7f93f6cb7d45a92f1537a70ecc06366e1ac6fed9" + integrity sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw== + dependencies: + get-stdin "^6.0.0" + +eslint-plugin-eslint-comments@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz#9e1cd7b4413526abb313933071d7aba05ca12ffa" + integrity sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ== dependencies: escape-string-regexp "^1.0.5" ignore "^5.0.5" @@ -2363,84 +2721,79 @@ eslint-plugin-jest@22.4.1: resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.4.1.tgz#a5fd6f7a2a41388d16f527073b778013c5189a9c" integrity sha512-gcLfn6P2PrFAVx3AobaOzlIEevpAEf9chTpFZz7bYfc7pz8XRv7vuKTIE4hxPKZSha6XWKKplDQ0x9Pq8xX2mg== -eslint-plugin-prettier@2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.2.tgz#71998c60aedfa2141f7bfcbf9d1c459bf98b4fad" - integrity sha512-tGek5clmW5swrAx1mdPYM8oThrBE83ePh7LeseZHBWfHVGrHPhKn7Y5zgRMbU/9D5Td9K4CEmUPjGxA7iw98Og== +eslint-plugin-prettier@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba" + integrity sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA== dependencies: - fast-diff "^1.1.1" - jest-docblock "^21.0.0" + prettier-linter-helpers "^1.0.0" -eslint-plugin-react-hooks@^1.5.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04" - integrity sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA== +eslint-plugin-react-hooks@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-3.0.0.tgz#9e80c71846eb68dd29c3b21d832728aa66e5bd35" + integrity sha512-EjxTHxjLKIBWFgDJdhKKzLh5q+vjTFrqNZX36uIxWS4OfyXe5DawqPj3U5qeJ1ngLwatjzQnmR0Lz0J0YH3kxw== eslint-plugin-react-native-globals@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz#ee1348bc2ceb912303ce6bdbd22e2f045ea86ea2" integrity sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g== -eslint-plugin-react-native@3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-native/-/eslint-plugin-react-native-3.6.0.tgz#7cad3b7c6159df6d26fe3252c6c5417a17f27b4b" - integrity sha512-BEQcHZ06hZSBYWFVuNEq0xuui5VEsWpHDsZGBtfadHfCRqRMUrkYPgdDb3bpc60qShHE83kqIv59uKdinEg91Q== +eslint-plugin-react-native@3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-native/-/eslint-plugin-react-native-3.8.1.tgz#92811e37191ecb0d29c0f0a0c9e5c943ee573821" + integrity sha512-6Z4s4nvgFRdda/1s1+uu4a6EMZwEjjJ9Bk/1yBImv0fd9U2CsGu2cUakAtV83cZKhizbWhSouXoaK4JtlScdFg== dependencies: eslint-plugin-react-native-globals "^0.1.1" -eslint-plugin-react@7.12.4: - version "7.12.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.12.4.tgz#b1ecf26479d61aee650da612e425c53a99f48c8c" - integrity sha512-1puHJkXJY+oS1t467MjbqjvX53uQ05HXwjqDgdbGBqf5j9eeydI54G3KwiJmWciQ0HTBacIKw2jgwSBSH3yfgQ== +eslint-plugin-react@7.19.0: + version "7.19.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz#6d08f9673628aa69c5559d33489e855d83551666" + integrity sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ== dependencies: - array-includes "^3.0.3" + array-includes "^3.1.1" doctrine "^2.1.0" has "^1.0.3" - jsx-ast-utils "^2.0.1" - object.fromentries "^2.0.0" - prop-types "^15.6.2" - resolve "^1.9.0" - -eslint-scope@3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" - integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug= - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-scope@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" + jsx-ast-utils "^2.2.3" + object.entries "^1.1.1" + object.fromentries "^2.0.2" + object.values "^1.1.1" + prop-types "^15.7.2" + resolve "^1.15.1" + semver "^6.3.0" + string.prototype.matchall "^4.0.2" + xregexp "^4.3.0" eslint-scope@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" - integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: - esrecurse "^4.1.0" + esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^1.3.1, eslint-utils@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" - integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q== +eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== dependencies: - eslint-visitor-keys "^1.0.0" + eslint-visitor-keys "^1.1.0" + +eslint-utils@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" - integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint@^6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.4.0.tgz#5aa9227c3fbe921982b2eda94ba0d7fae858611a" - integrity sha512-WTVEzK3lSFoXUovDHEbkJqCVPEPwbhCq4trDktNI6ygs7aO41d4cDT0JFAT5MivzZeVLWlg7vHL+bgrQv/t3vA== +eslint@^6.5.1: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.10.0" @@ -2449,19 +2802,19 @@ eslint@^6.4.0: debug "^4.0.1" doctrine "^3.0.0" eslint-scope "^5.0.0" - eslint-utils "^1.4.2" + eslint-utils "^1.4.3" eslint-visitor-keys "^1.1.0" - espree "^6.1.1" + espree "^6.1.2" esquery "^1.0.1" esutils "^2.0.2" file-entry-cache "^5.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" - globals "^11.7.0" + globals "^12.1.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^6.4.1" + inquirer "^7.0.0" is-glob "^4.0.0" js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" @@ -2470,7 +2823,7 @@ eslint@^6.4.0: minimatch "^3.0.4" mkdirp "^0.5.1" natural-compare "^1.4.0" - optionator "^0.8.2" + optionator "^0.8.3" progress "^2.0.0" regexpp "^2.0.1" semver "^6.1.2" @@ -2480,52 +2833,48 @@ eslint@^6.4.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de" - integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ== +espree@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== dependencies: - acorn "^7.0.0" - acorn-jsx "^5.0.2" + acorn "^7.1.1" + acorn-jsx "^5.2.0" eslint-visitor-keys "^1.1.0" -esprima@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= - -esprima@^4.0.0: +esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" - integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: - estraverse "^4.0.0" + estraverse "^5.1.0" -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: - estraverse "^4.1.0" + estraverse "^5.2.0" -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -esutils@^2.0.0, esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== etag@~1.8.1: version "1.8.1" @@ -2543,22 +2892,9 @@ eventemitter3@^3.0.0: integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== exec-sh@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b" - integrity sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg== - -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" + version "0.3.6" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== execa@^1.0.0: version "1.0.0" @@ -2573,6 +2909,22 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^3.2.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" + integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + p-finally "^2.0.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -2591,17 +2943,17 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" - integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== +expect@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-25.5.0.tgz#f07f848712a2813bb59167da3fb828ca21f58bba" + integrity sha512-w7KAXo0+6qqZZhovCaBVPSIqQp7/UTcx4M9uKt2m6pd2VB1voyC8JizLRqeEqud3AAVP02g+hbErDu5gu64tlA== dependencies: - "@jest/types" "^24.9.0" - ansi-styles "^3.2.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-regex-util "^24.9.0" + "@jest/types" "^25.5.0" + ansi-styles "^4.0.0" + jest-get-type "^25.2.6" + jest-matcher-utils "^25.5.0" + jest-message-util "^25.5.0" + jest-regex-util "^25.2.6" extend-shallow@^1.1.2: version "1.1.4" @@ -2613,6 +2965,7 @@ extend-shallow@^1.1.2: extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= dependencies: is-extendable "^0.1.0" @@ -2661,9 +3014,15 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extsprintf@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== fancy-log@^1.3.2: version "1.3.3" @@ -2675,31 +3034,32 @@ fancy-log@^1.3.2: parse-node-version "^1.0.0" time-stamp "^1.0.0" -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-diff@^1.1.1: +fast-diff@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.4: +fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fb-watchman@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" - integrity sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg= + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== dependencies: - bser "^2.0.0" + bser "2.1.1" fbjs-css-vars@^1.0.0: version "1.0.2" @@ -2722,19 +3082,6 @@ fbjs-scripts@^1.1.0: semver "^5.1.0" through2 "^2.0.0" -fbjs@^0.8.9: - version "0.8.17" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" - integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= - dependencies: - core-js "^1.0.0" - isomorphic-fetch "^2.1.1" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.18" - fbjs@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-1.0.0.tgz#52c215e0883a3c86af2a7a776ed51525ae8e0a5a" @@ -2756,6 +3103,13 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" @@ -2763,6 +3117,11 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -2773,6 +3132,13 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + finalhandler@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -2803,13 +3169,6 @@ find-cache-dir@^2.0.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-up@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -2817,7 +3176,7 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.1.0: +find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -2835,17 +3194,19 @@ flat-cache@^2.0.1: write "1.0.3" flatted@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" - integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@~2.3.2: version "2.3.3" @@ -2877,33 +3238,32 @@ fs-extra@^1.0.0: jsonfile "^2.1.0" klaw "^1.0.0" -fs-extra@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" - integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== dependencies: - graceful-fs "^4.1.2" + graceful-fs "^4.2.0" jsonfile "^4.0.0" universalify "^0.1.0" -fs-minipass@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" - integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== - dependencies: - minipass "^2.2.1" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^1.2.7: - version "1.2.9" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" - integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== dependencies: + bindings "^1.5.0" nan "^2.12.1" - node-pre-gyp "^0.12.0" + +fsevents@^2.1.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.1: version "1.1.1" @@ -2915,33 +3275,34 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -get-caller-file@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== get-stream@^4.0.0: version "4.1.0" @@ -2950,60 +3311,41 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= getpass@^0.1.1: - version "0.1.6" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: assert-plus "^1.0.0" glob-parent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" - integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@^7.0.0: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.5: - version "7.1.1" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.2" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: - version "7.1.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" - integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.1.6: +glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -3015,49 +3357,46 @@ glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^11.1.0, globals@^11.7.0: +globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" -graceful-fs@^4.1.15: - version "4.2.2" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" - integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== +graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4: + version "4.2.9" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -handlebars@^4.1.2: - version "4.2.0" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.2.0.tgz#57ce8d2175b9bbb3d8b3cf3e4217b1aec8ddcb2e" - integrity sha512-Kb4xn5Qh1cxAKvQnzNWZ512DhABzyFNmsaJf3OAkWNa4NkaqWcNI8Tao8Tasi0/F4JD9oyG0YxuFyvyR57d+Gw== - dependencies: - neo-async "^2.6.0" - optimist "^0.6.1" - source-map "^0.6.1" - optionalDependencies: - uglify-js "^3.1.4" - har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= -har-validator@~5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== dependencies: - ajv "^6.5.5" + ajv "^6.12.3" har-schema "^2.0.0" +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -3068,14 +3407,17 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" - integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" has-value@^0.3.1: version "0.3.1" @@ -3108,21 +3450,29 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has@^1.0.1, has@^1.0.3: +has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" -hermes-engine@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.2.1.tgz#25c0f1ff852512a92cb5c5cc47cf967e1e722ea2" - integrity sha512-eNHUQHuadDMJARpaqvlCZoK/Nitpj6oywq3vQ3wCwEsww5morX34mW5PmKWQTO7aU0ck0hgulxR+EVDlXygGxQ== +hermes-engine@~0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.5.1.tgz#601115e4b1e0a17d9aa91243b96277de4e926e09" + integrity sha512-hLwqh8dejHayjlpvZY40e1aDCDvyP98cWx/L5DhAjSJLH8g4z9Tp08D7y4+3vErDsncPOdf1bxm+zUWpx0/Fxg== + +hermes-profile-transformer@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz#bd0f5ecceda80dd0ddaae443469ab26fb38fc27b" + integrity sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ== + dependencies: + source-map "^0.7.3" hosted-git-info@^2.1.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.2.0.tgz#7a0d097863d886c0fabbdcd37bf1758d8becf8a5" + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== html-encoding-sniffer@^1.0.2: version "1.0.2" @@ -3131,16 +3481,21 @@ html-encoding-sniffer@^1.0.2: dependencies: whatwg-encoding "^1.0.1" -http-errors@~1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-errors@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== dependencies: depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" + inherits "2.0.4" + setprototypeof "1.2.0" statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" + toidentifier "1.0.1" http-signature@~1.2.0: version "1.2.0" @@ -3151,23 +3506,24 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4: +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@~0.4.13: - version "0.4.15" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" - -ignore-walk@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" - integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: - minimatch "^3.0.4" + safer-buffer ">= 2.1.2 < 3.0.0" ignore@^4.0.6: version "4.0.6" @@ -3175,9 +3531,9 @@ ignore@^4.0.6: integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.0.5: - version "5.1.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" - integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== image-size@^0.6.0: version "0.6.3" @@ -3193,45 +3549,39 @@ import-fresh@^2.0.0: resolve-from "^3.0.0" import-fresh@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118" - integrity sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ== + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" -import-local@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" - integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== dependencies: - pkg-dir "^3.0.0" - resolve-cwd "^2.0.0" + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.3, inherits@~2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@~1.3.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" - inquirer@^3.0.6: version "3.3.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" @@ -3252,25 +3602,34 @@ inquirer@^3.0.6: strip-ansi "^4.0.0" through "^2.3.6" -inquirer@^6.4.1: - version "6.5.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" - integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== +inquirer@^7.0.0: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.12" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" through "^2.3.6" +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" @@ -3283,14 +3642,15 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" - integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== +ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= is-accessor-descriptor@^0.1.6: version "0.1.6" @@ -3309,26 +3669,32 @@ is-accessor-descriptor@^1.0.0: is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-buffer@^1.0.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - dependencies: - builtin-modules "^1.0.0" - -is-callable@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" - integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== +is-callable@^1.1.4, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== is-ci@^2.0.0: version "2.0.0" @@ -3337,17 +3703,10 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" - integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== - dependencies: - has "^1.0.3" - is-core-module@^2.8.1: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" @@ -3366,9 +3725,11 @@ is-data-descriptor@^1.0.0: kind-of "^6.0.0" is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" - integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" is-descriptor@^0.1.0: version "0.1.6" @@ -3393,9 +3754,15 @@ is-directory@^0.3.1: resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= is-extendable@^1.0.1: version "1.0.1" @@ -3409,29 +3776,40 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + is-generator-fn@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-glob@^4.0.0, is-glob@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" +is-negative-zero@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + dependencies: + has-tostringtag "^1.0.0" + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3439,38 +3817,66 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: - has "^1.0.1" + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= -is-symbol@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" - integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: - has-symbols "^1.0.0" + has-tostringtag "^1.0.0" -is-typedarray@~1.0.0: +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-weakref@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" is-windows@^1.0.2: version "1.0.2" @@ -3482,13 +3888,17 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -isexe@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" @@ -3498,6 +3908,7 @@ isexe@^2.0.0: isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= dependencies: isarray "1.0.0" @@ -3509,6 +3920,7 @@ isobject@^3.0.0, isobject@^3.0.1: isomorphic-fetch@^2.1.1: version "2.2.1" resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= dependencies: node-fetch "^1.0.1" whatwg-fetch ">=0.10.0" @@ -3516,165 +3928,177 @@ isomorphic-fetch@^2.1.1: isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" - integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" - integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== +istanbul-lib-instrument@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== dependencies: - "@babel/generator" "^7.4.0" - "@babel/parser" "^7.4.3" - "@babel/template" "^7.4.0" - "@babel/traverse" "^7.4.3" - "@babel/types" "^7.4.0" - istanbul-lib-coverage "^2.0.5" - semver "^6.0.0" + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" -istanbul-lib-report@^2.0.4: - version "2.0.8" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" - integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== +istanbul-lib-instrument@^5.0.4: + version "5.1.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" + integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== dependencies: - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - supports-color "^6.1.0" + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" -istanbul-lib-source-maps@^3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" - integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== dependencies: debug "^4.1.1" - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - rimraf "^2.6.3" + istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^2.2.6: - version "2.2.6" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.6.tgz#7b4f2660d82b29303a8fe6091f8ca4bf058da1af" - integrity sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA== +istanbul-reports@^3.0.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.3.tgz#4bcae3103b94518117930d51283690960b50d3c2" + integrity sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg== dependencies: - handlebars "^4.1.2" + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" -jest-changed-files@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" - integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg== +jest-changed-files@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-25.5.0.tgz#141cc23567ceb3f534526f8614ba39421383634c" + integrity sha512-EOw9QEqapsDT7mKF162m8HFzRPbmP8qJQny6ldVOdOVBz3ACgPm/1nAn5fPQ/NDaYhX/AHkrGwwkCncpAVSXcw== dependencies: - "@jest/types" "^24.9.0" - execa "^1.0.0" - throat "^4.0.0" + "@jest/types" "^25.5.0" + execa "^3.2.0" + throat "^5.0.0" -jest-cli@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" - integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== +jest-cli@^25.5.4: + version "25.5.4" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-25.5.4.tgz#b9f1a84d1301a92c5c217684cb79840831db9f0d" + integrity sha512-rG8uJkIiOUpnREh1768/N3n27Cm+xPFkSNFO91tgg+8o2rXeVLStz+vkXkGr4UtzH6t1SNbjwoiswd7p4AhHTw== dependencies: - "@jest/core" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" + "@jest/core" "^25.5.4" + "@jest/test-result" "^25.5.0" + "@jest/types" "^25.5.0" + chalk "^3.0.0" exit "^0.1.2" - import-local "^2.0.0" + graceful-fs "^4.2.4" + import-local "^3.0.2" is-ci "^2.0.0" - jest-config "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" + jest-config "^25.5.4" + jest-util "^25.5.0" + jest-validate "^25.5.0" prompts "^2.0.1" - realpath-native "^1.1.0" - yargs "^13.3.0" + realpath-native "^2.0.0" + yargs "^15.3.1" -jest-config@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5" - integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ== +jest-config@^25.5.4: + version "25.5.4" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-25.5.4.tgz#38e2057b3f976ef7309b2b2c8dcd2a708a67f02c" + integrity sha512-SZwR91SwcdK6bz7Gco8qL7YY2sx8tFJYzvg216DLihTWf+LKY/DoJXpM9nTzYakSyfblbqeU48p/p7Jzy05Atg== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^24.9.0" - "@jest/types" "^24.9.0" - babel-jest "^24.9.0" - chalk "^2.0.1" + "@jest/test-sequencer" "^25.5.4" + "@jest/types" "^25.5.0" + babel-jest "^25.5.1" + chalk "^3.0.0" + deepmerge "^4.2.2" glob "^7.1.1" - jest-environment-jsdom "^24.9.0" - jest-environment-node "^24.9.0" - jest-get-type "^24.9.0" - jest-jasmine2 "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - micromatch "^3.1.10" - pretty-format "^24.9.0" - realpath-native "^1.1.0" + graceful-fs "^4.2.4" + jest-environment-jsdom "^25.5.0" + jest-environment-node "^25.5.0" + jest-get-type "^25.2.6" + jest-jasmine2 "^25.5.4" + jest-regex-util "^25.2.6" + jest-resolve "^25.5.1" + jest-util "^25.5.0" + jest-validate "^25.5.0" + micromatch "^4.0.2" + pretty-format "^25.5.0" + realpath-native "^2.0.0" -jest-diff@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" - integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== +jest-diff@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9" + integrity sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A== dependencies: - chalk "^2.0.1" - diff-sequences "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" + chalk "^3.0.0" + diff-sequences "^25.2.6" + jest-get-type "^25.2.6" + pretty-format "^25.5.0" -jest-docblock@^21.0.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" - integrity sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw== - -jest-docblock@^24.3.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2" - integrity sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA== +jest-docblock@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-25.3.0.tgz#8b777a27e3477cd77a168c05290c471a575623ef" + integrity sha512-aktF0kCar8+zxRHxQZwxMy70stc9R1mOmrLsT5VO3pIT0uzGRSDAXxSlz4NqQWpuLjPpuMhPRl7H+5FRsvIQAg== dependencies: - detect-newline "^2.1.0" + detect-newline "^3.0.0" -jest-each@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05" - integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog== +jest-each@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-25.5.0.tgz#0c3c2797e8225cb7bec7e4d249dcd96b934be516" + integrity sha512-QBogUxna3D8vtiItvn54xXde7+vuzqRrEeaw8r1s+1TG9eZLVJE5ZkKoSUlqFwRjnlaA4hyKGiu9OlkFIuKnjA== dependencies: - "@jest/types" "^24.9.0" - chalk "^2.0.1" - jest-get-type "^24.9.0" - jest-util "^24.9.0" - pretty-format "^24.9.0" + "@jest/types" "^25.5.0" + chalk "^3.0.0" + jest-get-type "^25.2.6" + jest-util "^25.5.0" + pretty-format "^25.5.0" -jest-environment-jsdom@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" - integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA== +jest-environment-jsdom@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-25.5.0.tgz#dcbe4da2ea997707997040ecf6e2560aec4e9834" + integrity sha512-7Jr02ydaq4jaWMZLY+Skn8wL5nVIYpWvmeatOHL3tOcV3Zw8sjnPpx+ZdeBfc457p8jCR9J6YCc+Lga0oIy62A== dependencies: - "@jest/environment" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - jest-util "^24.9.0" - jsdom "^11.5.1" + "@jest/environment" "^25.5.0" + "@jest/fake-timers" "^25.5.0" + "@jest/types" "^25.5.0" + jest-mock "^25.5.0" + jest-util "^25.5.0" + jsdom "^15.2.1" -jest-environment-node@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3" - integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA== +jest-environment-node@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-25.5.0.tgz#0f55270d94804902988e64adca37c6ce0f7d07a1" + integrity sha512-iuxK6rQR2En9EID+2k+IBs5fCFd919gVVK5BeND82fYeLWPqvRcFNPKu9+gxTwfB5XwBGBvZ0HFQa+cHtIoslA== dependencies: - "@jest/environment" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - jest-util "^24.9.0" + "@jest/environment" "^25.5.0" + "@jest/fake-timers" "^25.5.0" + "@jest/types" "^25.5.0" + jest-mock "^25.5.0" + jest-util "^25.5.0" + semver "^6.3.0" jest-get-type@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== -jest-haste-map@^24.7.1, jest-haste-map@^24.9.0: +jest-get-type@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" + integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== + +jest-haste-map@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== @@ -3693,45 +4117,66 @@ jest-haste-map@^24.7.1, jest-haste-map@^24.9.0: optionalDependencies: fsevents "^1.2.7" -jest-jasmine2@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0" - integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw== +jest-haste-map@^25.5.1: + version "25.5.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-25.5.1.tgz#1df10f716c1d94e60a1ebf7798c9fb3da2620943" + integrity sha512-dddgh9UZjV7SCDQUrQ+5t9yy8iEgKc1AKqZR9YDww8xsVOtzPQSMVLDChc21+g29oTRexb9/B0bIlZL+sWmvAQ== + dependencies: + "@jest/types" "^25.5.0" + "@types/graceful-fs" "^4.1.2" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-serializer "^25.5.0" + jest-util "^25.5.0" + jest-worker "^25.5.0" + micromatch "^4.0.2" + sane "^4.0.3" + walker "^1.0.7" + which "^2.0.2" + optionalDependencies: + fsevents "^2.1.2" + +jest-jasmine2@^25.5.4: + version "25.5.4" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-25.5.4.tgz#66ca8b328fb1a3c5364816f8958f6970a8526968" + integrity sha512-9acbWEfbmS8UpdcfqnDO+uBUgKa/9hcRh983IHdM+pKmJPL77G0sWAAK0V0kr5LK3a8cSBfkFSoncXwQlRZfkQ== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" + "@jest/environment" "^25.5.0" + "@jest/source-map" "^25.5.0" + "@jest/test-result" "^25.5.0" + "@jest/types" "^25.5.0" + chalk "^3.0.0" co "^4.6.0" - expect "^24.9.0" + expect "^25.5.0" is-generator-fn "^2.0.0" - jest-each "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-runtime "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - pretty-format "^24.9.0" - throat "^4.0.0" + jest-each "^25.5.0" + jest-matcher-utils "^25.5.0" + jest-message-util "^25.5.0" + jest-runtime "^25.5.4" + jest-snapshot "^25.5.1" + jest-util "^25.5.0" + pretty-format "^25.5.0" + throat "^5.0.0" -jest-leak-detector@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a" - integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA== +jest-leak-detector@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-25.5.0.tgz#2291c6294b0ce404241bb56fe60e2d0c3e34f0bb" + integrity sha512-rV7JdLsanS8OkdDpZtgBf61L5xZ4NnYLBq72r6ldxahJWWczZjXawRsoHyXzibM5ed7C2QRjpp6ypgwGdKyoVA== dependencies: - jest-get-type "^24.9.0" - pretty-format "^24.9.0" + jest-get-type "^25.2.6" + pretty-format "^25.5.0" -jest-matcher-utils@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" - integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== +jest-matcher-utils@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.5.0.tgz#fbc98a12d730e5d2453d7f1ed4a4d948e34b7867" + integrity sha512-VWI269+9JS5cpndnpCwm7dy7JtGQT30UHfrnM3mXl22gHGt/b7NkjBqXfbhZ8V4B7ANUsjK18PlSBmG0YH7gjw== dependencies: - chalk "^2.0.1" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" + chalk "^3.0.0" + jest-diff "^25.5.0" + jest-get-type "^25.2.6" + pretty-format "^25.5.0" jest-message-util@^24.9.0: version "24.9.0" @@ -3747,6 +4192,20 @@ jest-message-util@^24.9.0: slash "^2.0.0" stack-utils "^1.0.1" +jest-message-util@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.5.0.tgz#ea11d93204cc7ae97456e1d8716251185b8880ea" + integrity sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/types" "^25.5.0" + "@types/stack-utils" "^1.0.1" + chalk "^3.0.0" + graceful-fs "^4.2.4" + micromatch "^4.0.2" + slash "^3.0.0" + stack-utils "^1.0.1" + jest-mock@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" @@ -3754,113 +4213,136 @@ jest-mock@^24.9.0: dependencies: "@jest/types" "^24.9.0" +jest-mock@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.5.0.tgz#a91a54dabd14e37ecd61665d6b6e06360a55387a" + integrity sha512-eXWuTV8mKzp/ovHc5+3USJMYsTBhyQ+5A1Mak35dey/RG8GlM4YWVylZuGgVXinaW6tpvk/RSecmF37FKUlpXA== + dependencies: + "@jest/types" "^25.5.0" + jest-pnp-resolver@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" - integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^24.3.0, jest-regex-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" - integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== +jest-regex-util@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-25.2.6.tgz#d847d38ba15d2118d3b06390056028d0f2fd3964" + integrity sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw== -jest-resolve-dependencies@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab" - integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g== +jest-resolve-dependencies@^25.5.4: + version "25.5.4" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-25.5.4.tgz#85501f53957c8e3be446e863a74777b5a17397a7" + integrity sha512-yFmbPd+DAQjJQg88HveObcGBA32nqNZ02fjYmtL16t1xw9bAttSn5UGRRhzMHIQbsep7znWvAvnD4kDqOFM0Uw== dependencies: - "@jest/types" "^24.9.0" - jest-regex-util "^24.3.0" - jest-snapshot "^24.9.0" + "@jest/types" "^25.5.0" + jest-regex-util "^25.2.6" + jest-snapshot "^25.5.1" -jest-resolve@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" - integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== +jest-resolve@^25.5.1: + version "25.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-25.5.1.tgz#0e6fbcfa7c26d2a5fe8f456088dc332a79266829" + integrity sha512-Hc09hYch5aWdtejsUZhA+vSzcotf7fajSlPA6EZPE1RmPBAD39XtJhvHWFStid58iit4IPDLI/Da4cwdDmAHiQ== dependencies: - "@jest/types" "^24.9.0" + "@jest/types" "^25.5.0" browser-resolve "^1.11.3" - chalk "^2.0.1" + chalk "^3.0.0" + graceful-fs "^4.2.4" jest-pnp-resolver "^1.2.1" - realpath-native "^1.1.0" + read-pkg-up "^7.0.1" + realpath-native "^2.0.0" + resolve "^1.17.0" + slash "^3.0.0" -jest-runner@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42" - integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg== +jest-runner@^25.5.4: + version "25.5.4" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-25.5.4.tgz#ffec5df3875da5f5c878ae6d0a17b8e4ecd7c71d" + integrity sha512-V/2R7fKZo6blP8E9BL9vJ8aTU4TH2beuqGNxHbxi6t14XzTb+x90B3FRgdvuHm41GY8ch4xxvf0ATH4hdpjTqg== dependencies: - "@jest/console" "^24.7.1" - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.4.2" + "@jest/console" "^25.5.0" + "@jest/environment" "^25.5.0" + "@jest/test-result" "^25.5.0" + "@jest/types" "^25.5.0" + chalk "^3.0.0" exit "^0.1.2" - graceful-fs "^4.1.15" - jest-config "^24.9.0" - jest-docblock "^24.3.0" - jest-haste-map "^24.9.0" - jest-jasmine2 "^24.9.0" - jest-leak-detector "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - jest-runtime "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.6.0" + graceful-fs "^4.2.4" + jest-config "^25.5.4" + jest-docblock "^25.3.0" + jest-haste-map "^25.5.1" + jest-jasmine2 "^25.5.4" + jest-leak-detector "^25.5.0" + jest-message-util "^25.5.0" + jest-resolve "^25.5.1" + jest-runtime "^25.5.4" + jest-util "^25.5.0" + jest-worker "^25.5.0" source-map-support "^0.5.6" - throat "^4.0.0" + throat "^5.0.0" -jest-runtime@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac" - integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw== +jest-runtime@^25.5.4: + version "25.5.4" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-25.5.4.tgz#dc981fe2cb2137abcd319e74ccae7f7eeffbfaab" + integrity sha512-RWTt8LeWh3GvjYtASH2eezkc8AehVoWKK20udV6n3/gC87wlTbE1kIA+opCvNWyyPeBs6ptYsc6nyHUb1GlUVQ== dependencies: - "@jest/console" "^24.7.1" - "@jest/environment" "^24.9.0" - "@jest/source-map" "^24.3.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/yargs" "^13.0.0" - chalk "^2.0.1" + "@jest/console" "^25.5.0" + "@jest/environment" "^25.5.0" + "@jest/globals" "^25.5.2" + "@jest/source-map" "^25.5.0" + "@jest/test-result" "^25.5.0" + "@jest/transform" "^25.5.1" + "@jest/types" "^25.5.0" + "@types/yargs" "^15.0.0" + chalk "^3.0.0" + collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.3" - graceful-fs "^4.1.15" - jest-config "^24.9.0" - jest-haste-map "^24.9.0" - jest-message-util "^24.9.0" - jest-mock "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - realpath-native "^1.1.0" - slash "^2.0.0" - strip-bom "^3.0.0" - yargs "^13.3.0" + graceful-fs "^4.2.4" + jest-config "^25.5.4" + jest-haste-map "^25.5.1" + jest-message-util "^25.5.0" + jest-mock "^25.5.0" + jest-regex-util "^25.2.6" + jest-resolve "^25.5.1" + jest-snapshot "^25.5.1" + jest-util "^25.5.0" + jest-validate "^25.5.0" + realpath-native "^2.0.0" + slash "^3.0.0" + strip-bom "^4.0.0" + yargs "^15.3.1" -jest-serializer@^24.4.0, jest-serializer@^24.9.0: +jest-serializer@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== -jest-snapshot@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" - integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== +jest-serializer@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-25.5.0.tgz#a993f484e769b4ed54e70e0efdb74007f503072b" + integrity sha512-LxD8fY1lByomEPflwur9o4e2a5twSQ7TaVNLlFUuToIdoJuBt8tzHfCsZ42Ok6LkKXWzFWf3AGmheuLAA7LcCA== + dependencies: + graceful-fs "^4.2.4" + +jest-snapshot@^25.5.1: + version "25.5.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-25.5.1.tgz#1a2a576491f9961eb8d00c2e5fd479bc28e5ff7f" + integrity sha512-C02JE1TUe64p2v1auUJ2ze5vcuv32tkv9PyhEb318e8XOKF7MOyXdJ7kdjbvrp3ChPLU2usI7Rjxs97Dj5P0uQ== dependencies: "@babel/types" "^7.0.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" - expect "^24.9.0" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - mkdirp "^0.5.1" + "@jest/types" "^25.5.0" + "@types/prettier" "^1.19.0" + chalk "^3.0.0" + expect "^25.5.0" + graceful-fs "^4.2.4" + jest-diff "^25.5.0" + jest-get-type "^25.2.6" + jest-matcher-utils "^25.5.0" + jest-message-util "^25.5.0" + jest-resolve "^25.5.1" + make-dir "^3.0.0" natural-compare "^1.4.0" - pretty-format "^24.9.0" - semver "^6.2.0" + pretty-format "^25.5.0" + semver "^6.3.0" jest-util@^24.9.0: version "24.9.0" @@ -3880,7 +4362,18 @@ jest-util@^24.9.0: slash "^2.0.0" source-map "^0.6.0" -jest-validate@^24.7.0, jest-validate@^24.9.0: +jest-util@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.5.0.tgz#31c63b5d6e901274d264a4fec849230aa3fa35b0" + integrity sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA== + dependencies: + "@jest/types" "^25.5.0" + chalk "^3.0.0" + graceful-fs "^4.2.4" + is-ci "^2.0.0" + make-dir "^3.0.0" + +jest-validate@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== @@ -3892,20 +4385,31 @@ jest-validate@^24.7.0, jest-validate@^24.9.0: leven "^3.1.0" pretty-format "^24.9.0" -jest-watcher@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b" - integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw== +jest-validate@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-25.5.0.tgz#fb4c93f332c2e4cf70151a628e58a35e459a413a" + integrity sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ== dependencies: - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/yargs" "^13.0.0" - ansi-escapes "^3.0.0" - chalk "^2.0.1" - jest-util "^24.9.0" - string-length "^2.0.0" + "@jest/types" "^25.5.0" + camelcase "^5.3.1" + chalk "^3.0.0" + jest-get-type "^25.2.6" + leven "^3.1.0" + pretty-format "^25.5.0" -jest-worker@^24.6.0, jest-worker@^24.9.0: +jest-watcher@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-25.5.0.tgz#d6110d101df98badebe435003956fd4a465e8456" + integrity sha512-XrSfJnVASEl+5+bb51V0Q7WQx65dTSk7NL4yDdVjPnRNpM0hG+ncFmDYJo9O8jaSRcAitVbuVawyXCRoxGrT5Q== + dependencies: + "@jest/test-result" "^25.5.0" + "@jest/types" "^25.5.0" + ansi-escapes "^4.2.1" + chalk "^3.0.0" + jest-util "^25.5.0" + string-length "^3.1.0" + +jest-worker@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== @@ -3913,28 +4417,27 @@ jest-worker@^24.6.0, jest-worker@^24.9.0: merge-stream "^2.0.0" supports-color "^6.1.0" -jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171" - integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw== +jest-worker@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.5.0.tgz#2611d071b79cea0f43ee57a3d118593ac1547db1" + integrity sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw== dependencies: - import-local "^2.0.0" - jest-cli "^24.9.0" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest@^25.1.0: + version "25.5.4" + resolved "https://registry.yarnpkg.com/jest/-/jest-25.5.4.tgz#f21107b6489cfe32b076ce2adcadee3587acb9db" + integrity sha512-hHFJROBTqZahnO+X+PMtT6G2/ztqAZJveGqz//FnWWHurizkD05PQGzRZOhF3XP6z7SJmL+5tCfW8qV06JypwQ== + dependencies: + "@jest/core" "^25.5.4" + import-local "^3.0.2" + jest-cli "^25.5.4" jetifier@^1.6.2: - version "1.6.4" - resolved "https://registry.yarnpkg.com/jetifier/-/jetifier-1.6.4.tgz#6159db8e275d97980d26162897a0648b6d4a3222" - integrity sha512-+f/4OLeqY8RAmXnonI1ffeY1DR8kMNJPhv5WMFehchf7U71cjMQVKkOz1n6asz6kfVoAqKNWJz1A/18i18AcXA== - -jodid25519@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" - dependencies: - jsbn "~0.1.0" - -js-tokens@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" + version "1.6.8" + resolved "https://registry.yarnpkg.com/jetifier/-/jetifier-1.6.8.tgz#e88068697875cbda98c32472902c4d3756247798" + integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -3942,9 +4445,9 @@ js-tokens@^3.0.0: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -3952,42 +4455,43 @@ js-yaml@^3.13.1: jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= jsc-android@^245459.0.0: version "245459.0.0" resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-245459.0.0.tgz#e584258dd0b04c9159a27fb104cd5d491fd202c9" integrity sha512-wkjURqwaB1daNkDi2OYYbsLnIdC/lUM2nPXQKRs5pqEU9chDg435bjvo+LSaHotDENygHQDHe+ntUkkw2gwMtg== -jsdom@^11.5.1: - version "11.12.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" - integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== +jsdom@^15.2.1: + version "15.2.1" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" + integrity sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g== dependencies: abab "^2.0.0" - acorn "^5.5.3" - acorn-globals "^4.1.0" + acorn "^7.1.0" + acorn-globals "^4.3.2" array-equal "^1.0.0" - cssom ">= 0.3.2 < 0.4.0" - cssstyle "^1.0.0" - data-urls "^1.0.0" + cssom "^0.4.1" + cssstyle "^2.0.0" + data-urls "^1.1.0" domexception "^1.0.1" - escodegen "^1.9.1" + escodegen "^1.11.1" html-encoding-sniffer "^1.0.2" - left-pad "^1.3.0" - nwsapi "^2.0.7" - parse5 "4.0.0" + nwsapi "^2.2.0" + parse5 "5.1.0" pn "^1.1.0" - request "^2.87.0" - request-promise-native "^1.0.5" - sax "^1.2.4" + request "^2.88.0" + request-promise-native "^1.0.7" + saxes "^3.1.9" symbol-tree "^3.2.2" - tough-cookie "^2.3.4" + tough-cookie "^3.0.1" w3c-hr-time "^1.0.1" + w3c-xmlserializer "^1.1.2" webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.3" - whatwg-mimetype "^2.1.0" - whatwg-url "^6.4.1" - ws "^5.2.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^7.0.0" + ws "^7.0.0" xml-name-validator "^3.0.0" jsesc@^2.5.1: @@ -3998,20 +4502,27 @@ jsesc@^2.5.1: jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" @@ -4021,28 +4532,31 @@ json-stable-stringify-without-jsonify@^1.0.1: json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= dependencies: jsonify "~0.0.0" json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= -json5@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" - integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== +json5@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== dependencies: - minimist "^1.2.0" + minimist "^1.2.5" jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= optionalDependencies: graceful-fs "^4.1.6" @@ -4056,21 +4570,24 @@ jsonfile@^4.0.0: jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= jsprim@^1.2.2: - version "1.3.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.3.1.tgz#2a7256f70412a29ee3670aaca625994c4dcff252" + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== dependencies: - extsprintf "1.0.2" - json-schema "0.2.3" - verror "1.3.6" + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" -jsx-ast-utils@^2.0.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz#4d4973ebf8b9d2837ee91a8208cc66f3a2776cfb" - integrity sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ== +jsx-ast-utils@^2.2.3: + version "2.4.1" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz#1114a4c1209481db06c690c2b4f488cc665f657e" + integrity sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w== dependencies: - array-includes "^3.0.3" + array-includes "^3.1.1" object.assign "^4.1.0" keymirror@^0.1.1: @@ -4083,13 +4600,7 @@ kind-of@^1.1.0: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44" integrity sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ= -kind-of@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" - dependencies: - is-buffer "^1.0.2" - -kind-of@^3.0.3, kind-of@^3.2.0: +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= @@ -4109,13 +4620,14 @@ kind-of@^5.0.0: integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" - integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== klaw@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= optionalDependencies: graceful-fs "^4.1.9" @@ -4124,24 +4636,6 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - dependencies: - invert-kv "^1.0.0" - -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== - dependencies: - invert-kv "^2.0.0" - -left-pad@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" - integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== - leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -4155,33 +4649,10 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" - integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" - -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== locate-path@^3.0.0: version "3.0.0" @@ -4198,6 +4669,11 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -4208,24 +4684,10 @@ lodash.throttle@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= -lodash.unescape@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" - integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= - -lodash@^4.17.10, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - -lodash@^4.17.11, lodash@^4.17.5: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== - -lodash@^4.3.0: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" +lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.3.0: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^2.2.0: version "2.2.0" @@ -4234,22 +4696,23 @@ log-symbols@^2.2.0: dependencies: chalk "^2.0.1" -logkitty@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/logkitty/-/logkitty-0.6.1.tgz#fe29209669d261539cbd6bb998a136fc92a1a05c" - integrity sha512-cHuXN8qUZuzX/7kB6VyS7kB4xyD24e8gyHXIFNhIv+fjW3P+jEXNUhj0o/7qWJtv7UZpbnPgUqzu/AZQ8RAqxQ== +logkitty@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/logkitty/-/logkitty-0.7.1.tgz#8e8d62f4085a826e8d38987722570234e33c6aa7" + integrity sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ== dependencies: ansi-fragments "^0.2.1" dayjs "^1.8.15" - yargs "^12.0.5" + yargs "^15.1.0" -loose-envify@^1.0.0, loose-envify@^1.1.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" +lolex@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" + integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== dependencies: - js-tokens "^3.0.0" + "@sinonjs/commons" "^1.7.0" -loose-envify@^1.3.1, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -4257,11 +4720,19 @@ loose-envify@^1.3.1, loose-envify@^1.4.0: js-tokens "^3.0.0 || ^4.0.0" lru-cache@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== dependencies: - pseudomap "^1.0.1" - yallist "^2.0.0" + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" @@ -4271,11 +4742,19 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -makeerror@1.0.x: - version "1.0.11" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: - tmpl "1.0.x" + semver "^6.0.0" + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" map-age-cleaner@^0.1.1: version "0.1.3" @@ -4296,14 +4775,7 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -mem@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" - integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= - dependencies: - mimic-fn "^1.0.0" - -mem@^4.0.0, mem@^4.3.0: +mem@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== @@ -4324,86 +4796,80 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -metro-babel-register@^0.56.0, metro-babel-register@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.56.4.tgz#b0c627a1cfdd1bdd768f81af79481754e833a902" - integrity sha512-Phm6hMluOWYqfykftjJ1jsTpWvbgb49AC/1taxEctxUdRCZlFgZwBleJZAhQYxJD5J+ikFkEbHDzePEXb29KVA== +metro-babel-register@0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.59.0.tgz#2bcff65641b36794cf083ba732fbc46cf870fb43" + integrity sha512-JtWc29erdsXO/V3loenXKw+aHUXgj7lt0QPaZKPpctLLy8kcEpI/8pfXXgVK9weXICCpCnYtYncIosAyzh0xjg== dependencies: "@babel/core" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.0.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" "@babel/plugin-proposal-optional-chaining" "^7.0.0" - "@babel/plugin-transform-async-to-generator" "^7.0.0" "@babel/plugin-transform-flow-strip-types" "^7.0.0" "@babel/plugin-transform-modules-commonjs" "^7.0.0" "@babel/register" "^7.0.0" - core-js "^2.2.2" escape-string-regexp "^1.0.5" -metro-babel-transformer@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.56.4.tgz#fe1d0dc600fcf90201a5bea4d42caea10b801057" - integrity sha512-IOi4ILgZvaX7GCGHBJp79paNVOq5QxhhbyqAdEJgDP8bHfl/OVHoVKSypfrsMSKSiBrqxhIjyc4XjkXsQtkx5g== +metro-babel-transformer@0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.59.0.tgz#dda99c75d831b00142c42c020c51c103b29f199d" + integrity sha512-fdZJl8rs54GVFXokxRdD7ZrQ1TJjxWzOi/xSP25VR3E8tbm3nBZqS+/ylu643qSr/IueABR+jrlqAyACwGEf6w== dependencies: "@babel/core" "^7.0.0" - metro-source-map "^0.56.4" + metro-source-map "0.59.0" -metro-cache@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.56.4.tgz#542f9f8a35f8fb9d5576f46fd3ab4d4f42851a7e" - integrity sha512-d1hiUSKwtRsuMxUhHVJ3tjK2BbpUlJGvTyMWohK8Wxx+0GbnWRWWFcI4vlCzlZfoK0VtZK2MJEl5t7Du1mIniQ== +metro-cache@0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.59.0.tgz#ef3c055f276933979b731455dc8317d7a66f0f2d" + integrity sha512-ryWNkSnpyADfRpHGb8BRhQ3+k8bdT/bsxMH2O0ntlZYZ188d8nnYWmxbRvFmEzToJxe/ol4uDw0tJFAaQsN8KA== dependencies: - jest-serializer "^24.4.0" - metro-core "^0.56.4" + jest-serializer "^24.9.0" + metro-core "0.59.0" mkdirp "^0.5.1" rimraf "^2.5.4" -metro-config@^0.56.0, metro-config@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.56.4.tgz#338fd8165fba59424cec427c1a881757945e57e9" - integrity sha512-O85QDHwWdMn/8ERe13y4a6vbZL0AHyO8atTvL+9BCulLEO+FQBi1iJjr3+ViLa8cf0m5dRftDsa7P47m5euk4A== +metro-config@0.59.0, metro-config@^0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.59.0.tgz#9844e388069321dd7403e49f0d495a81f9aa0fef" + integrity sha512-MDsknFG9vZ4Nb5VR6OUDmGHaWz6oZg/FtE3up1zVBKPVRTXE1Z+k7zypnPtMXjMh3WHs/Sy4+wU1xnceE/zdnA== dependencies: cosmiconfig "^5.0.5" - jest-validate "^24.7.0" - metro "^0.56.4" - metro-cache "^0.56.4" - metro-core "^0.56.4" - pretty-format "^24.7.0" + jest-validate "^24.9.0" + metro "0.59.0" + metro-cache "0.59.0" + metro-core "0.59.0" -metro-core@^0.56.0, metro-core@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.56.4.tgz#67cc41b3c0bf66e9c2306f50239a1080b1e82312" - integrity sha512-hMzkBdgPt5Zm9nr/1KtIT+A6H7TNiLVCEGG5OiAXj8gTRsA2yy7wAdQpwy0xbE+zi88t/pLOzXpd3ClG/YxyWg== +metro-core@0.59.0, metro-core@^0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.59.0.tgz#958cde3fe5c8cd84a78e1899af801ad69e9c83b1" + integrity sha512-kb5LKvV5r2pqMEzGyTid8ai2mIjW13NMduQ8oBmfha7/EPTATcTQ//s+bkhAs1toQD8vqVvjAb0cPNjWQEmcmQ== dependencies: - jest-haste-map "^24.7.1" + jest-haste-map "^24.9.0" lodash.throttle "^4.1.1" - metro-resolver "^0.56.4" + metro-resolver "0.59.0" wordwrap "^1.0.0" -metro-inspector-proxy@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.56.4.tgz#7343ff3c5908af4fd99e96b6d646e24e99816be4" - integrity sha512-E1S3MO25mWKmcLn1UQuCDiS0hf9P2Fwq8sEAX5lBLoZbehepNH+4xJ3xXSY51JX4dozBrE8GGoKL4ll3II40LA== +metro-inspector-proxy@0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.59.0.tgz#39d1390772d13767fc595be9a1a7074e2425cf8e" + integrity sha512-hPeAuQcofTOH0F+2GEZqWkvkVY1/skezSSlMocDQDaqds+Kw6JgdA7FlZXxnKmQ/jYrWUzff/pl8SUCDwuYthQ== dependencies: connect "^3.6.5" debug "^2.2.0" - rxjs "^5.4.3" ws "^1.1.5" - yargs "^9.0.0" + yargs "^14.2.0" -metro-minify-uglify@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.56.4.tgz#13589dfb1d43343608aacb7f78ddfcc052daa63c" - integrity sha512-BHgj7+BKEK2pHvWHUR730bIrsZwl8DPtr49x9L0j2grPZ5/UROWXzEr8VZgIss7fl64t845uu1HXNNyuSj2EhA== +metro-minify-uglify@0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.59.0.tgz#6491876308d878742f7b894d7fca4af356886dd5" + integrity sha512-7IzVgCVWZMymgZ/quieg/9v5EQ8QmZWAgDc86Zp9j0Vy6tQTjUn6jlU+YAKW3mfMEjMr6iIUzCD8YklX78tFAw== dependencies: uglify-es "^3.1.9" -metro-react-native-babel-preset@^0.56.0: - version "0.56.0" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.56.0.tgz#fa47dfd5f7678e89cffd1249020b8add6938fc48" - integrity sha512-MAo1fm0dNn6MVZmylaz6k2HC1MINHLTLfE7O3a9Xz3fAtbGbApisp06rBUfK5uUqIJDmAaKgbiT34lHJSIiE6Q== +metro-react-native-babel-preset@0.59.0, metro-react-native-babel-preset@^0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.59.0.tgz#20e020bc6ac9849e1477de1333d303ed42aba225" + integrity sha512-BoO6ncPfceIDReIH8pQ5tQptcGo5yRWQXJGVXfANbiKLq4tfgdZB1C1e2rMUJ6iypmeJU9dzl+EhPmIFKtgREg== dependencies: "@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-export-default-from" "^7.0.0" @@ -4414,6 +4880,8 @@ metro-react-native-babel-preset@^0.56.0: "@babel/plugin-syntax-dynamic-import" "^7.0.0" "@babel/plugin-syntax-export-default-from" "^7.0.0" "@babel/plugin-syntax-flow" "^7.2.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" + "@babel/plugin-syntax-optional-chaining" "^7.0.0" "@babel/plugin-transform-arrow-functions" "^7.0.0" "@babel/plugin-transform-block-scoping" "^7.0.0" "@babel/plugin-transform-classes" "^7.0.0" @@ -4429,6 +4897,7 @@ metro-react-native-babel-preset@^0.56.0: "@babel/plugin-transform-parameters" "^7.0.0" "@babel/plugin-transform-react-display-name" "^7.0.0" "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" "@babel/plugin-transform-regenerator" "^7.0.0" "@babel/plugin-transform-runtime" "^7.0.0" @@ -4436,101 +4905,61 @@ metro-react-native-babel-preset@^0.56.0: "@babel/plugin-transform-spread" "^7.0.0" "@babel/plugin-transform-sticky-regex" "^7.0.0" "@babel/plugin-transform-template-literals" "^7.0.0" - "@babel/plugin-transform-typescript" "^7.0.0" + "@babel/plugin-transform-typescript" "^7.5.0" "@babel/plugin-transform-unicode-regex" "^7.0.0" "@babel/template" "^7.0.0" react-refresh "^0.4.0" -metro-react-native-babel-preset@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.56.4.tgz#dcedc64b7ff5c0734839458e70eb0ebef6d063a8" - integrity sha512-CzbBDM9Rh6w8s1fq+ZqihAh7DDqUAcfo9pPww25+N/eJ7UK436Q7JdfxwdIPpBwLFn6o6MyYn+uwL9OEWBJarA== - dependencies: - "@babel/plugin-proposal-class-properties" "^7.0.0" - "@babel/plugin-proposal-export-default-from" "^7.0.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.0.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" - "@babel/plugin-proposal-optional-chaining" "^7.0.0" - "@babel/plugin-syntax-dynamic-import" "^7.0.0" - "@babel/plugin-syntax-export-default-from" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.2.0" - "@babel/plugin-transform-arrow-functions" "^7.0.0" - "@babel/plugin-transform-block-scoping" "^7.0.0" - "@babel/plugin-transform-classes" "^7.0.0" - "@babel/plugin-transform-computed-properties" "^7.0.0" - "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-exponentiation-operator" "^7.0.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-for-of" "^7.0.0" - "@babel/plugin-transform-function-name" "^7.0.0" - "@babel/plugin-transform-literals" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/plugin-transform-object-assign" "^7.0.0" - "@babel/plugin-transform-parameters" "^7.0.0" - "@babel/plugin-transform-react-display-name" "^7.0.0" - "@babel/plugin-transform-react-jsx" "^7.0.0" - "@babel/plugin-transform-react-jsx-source" "^7.0.0" - "@babel/plugin-transform-regenerator" "^7.0.0" - "@babel/plugin-transform-runtime" "^7.0.0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0" - "@babel/plugin-transform-spread" "^7.0.0" - "@babel/plugin-transform-sticky-regex" "^7.0.0" - "@babel/plugin-transform-template-literals" "^7.0.0" - "@babel/plugin-transform-typescript" "^7.0.0" - "@babel/plugin-transform-unicode-regex" "^7.0.0" - "@babel/template" "^7.0.0" - react-refresh "^0.4.0" - -metro-react-native-babel-transformer@^0.56.0: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.56.4.tgz#3c6e48b605c305362ee624e45ff338656e35fc1d" - integrity sha512-ng74eutuy1nyGI9+TDzzVAVfEmNPDlapV4msTQMKPi4EFqo/fBn7Ct33ME9l5E51pQBBnxt/UwcpTvd13b29kQ== +metro-react-native-babel-transformer@0.59.0, metro-react-native-babel-transformer@^0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.59.0.tgz#9b3dfd6ad35c6ef37fc4ce4d20a2eb67fabbb4be" + integrity sha512-1O3wrnMq4NcPQ1asEcl9lRDn/t+F1Oef6S9WaYVIKEhg9m/EQRGVrrTVP+R6B5Eeaj3+zNKbzM8Dx/NWy1hUbQ== dependencies: "@babel/core" "^7.0.0" - babel-preset-fbjs "^3.1.2" - metro-babel-transformer "^0.56.4" - metro-react-native-babel-preset "^0.56.4" - metro-source-map "^0.56.4" + babel-preset-fbjs "^3.3.0" + metro-babel-transformer "0.59.0" + metro-react-native-babel-preset "0.59.0" + metro-source-map "0.59.0" -metro-resolver@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.56.4.tgz#9876f57bca37fd1bfcffd733541e2ee4a89fad7f" - integrity sha512-Ug4ulVfpkKZ1Wu7mdYj9XLGuOqZTuWCqEhyx3siKTc/2eBwKZQXmiNo5d/IxWNvmwL/87Abeb724I6CMzMfjiQ== +metro-resolver@0.59.0, metro-resolver@^0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.59.0.tgz#fbc9d7c95f094c52807877d0011feffb9e896fad" + integrity sha512-lbgiumnwoVosffEI96z0FGuq1ejTorHAj3QYUPmp5dFMfitRxLP7Wm/WP9l4ZZjIptxTExsJwuEff1SLRCPD9w== dependencies: absolute-path "^0.0.0" -metro-source-map@^0.56.0, metro-source-map@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.56.4.tgz#868ccac3f3519fe14eca358bc186f63651b2b9bc" - integrity sha512-f1P9/rpFmG3Z0Jatiw2zvLItx1TwR7mXTSDj4qLDCWeVMB3kEXAr3R0ucumTW8c6HfpJljeRBWzYFXF33fd81g== +metro-source-map@0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.59.0.tgz#e9beb9fc51bfb4e060f95820cf1508fc122d23f7" + integrity sha512-0w5CmCM+ybSqXIjqU4RiK40t4bvANL6lafabQ2GP2XD3vSwkLY+StWzCtsb4mPuyi9R/SgoLBel+ZOXHXAH0eQ== dependencies: "@babel/traverse" "^7.0.0" "@babel/types" "^7.0.0" invariant "^2.2.4" - metro-symbolicate "^0.56.4" - ob1 "^0.56.4" + metro-symbolicate "0.59.0" + ob1 "0.59.0" source-map "^0.5.6" vlq "^1.0.0" -metro-symbolicate@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.56.4.tgz#53e9d40beac9049fa75a3e620ddd47d4907ff015" - integrity sha512-8mCNNn6zV5FFKCIcRgI7736Xl+owgvYuy8qanPxZN36f7utiWRYeB+PirEBPcglBk4qQvoy2lT6oPULNXZQbbQ== +metro-symbolicate@0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.59.0.tgz#fc7f93957a42b02c2bfc57ed1e8f393f5f636a54" + integrity sha512-asLaF2A7rndrToGFIknL13aiohwPJ95RKHf0NM3hP/nipiLDoMzXT6ZnQvBqDxkUKyP+51AI75DMtb+Wcyw4Bw== dependencies: invariant "^2.2.4" - metro-source-map "^0.56.4" + metro-source-map "0.59.0" source-map "^0.5.6" through2 "^2.0.1" vlq "^1.0.0" -metro@^0.56.0, metro@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.56.4.tgz#be7e1380ee6ac3552c25ead8098eab261029e4d7" - integrity sha512-Kt3OQJQtQdts0JrKnyGdLpKHDjqYBgIfzvYrvfhmFCkKuZ8aqRlVnvpfjQ4/OBm0Fmm9NyyxbNRD9VIbj7WjnA== +metro@0.59.0, metro@^0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.59.0.tgz#64a87cd61357814a4f279518e0781b1eab5934b8" + integrity sha512-OpVgYXyuTvouusFZQJ/UYKEbwfLmialrSCUUTGTFaBor6UMUHZgXPYtK86LzesgMqRc8aiuTQVO78iKW2Iz3wg== dependencies: + "@babel/code-frame" "^7.0.0" "@babel/core" "^7.0.0" - "@babel/generator" "^7.0.0" + "@babel/generator" "^7.5.0" "@babel/parser" "^7.0.0" "@babel/plugin-external-helpers" "^7.0.0" "@babel/template" "^7.0.0" @@ -4538,50 +4967,52 @@ metro@^0.56.0, metro@^0.56.4: "@babel/types" "^7.0.0" absolute-path "^0.0.0" async "^2.4.0" - babel-preset-fbjs "^3.1.2" + babel-preset-fbjs "^3.3.0" buffer-crc32 "^0.2.13" chalk "^2.4.1" + ci-info "^2.0.0" concat-stream "^1.6.0" connect "^3.6.5" debug "^2.2.0" denodeify "^1.2.1" + error-stack-parser "^2.0.6" eventemitter3 "^3.0.0" fbjs "^1.0.0" fs-extra "^1.0.0" graceful-fs "^4.1.3" image-size "^0.6.0" invariant "^2.2.4" - jest-haste-map "^24.7.1" - jest-worker "^24.6.0" + jest-haste-map "^24.9.0" + jest-worker "^24.9.0" json-stable-stringify "^1.0.1" lodash.throttle "^4.1.1" merge-stream "^1.0.1" - metro-babel-register "^0.56.4" - metro-babel-transformer "^0.56.4" - metro-cache "^0.56.4" - metro-config "^0.56.4" - metro-core "^0.56.4" - metro-inspector-proxy "^0.56.4" - metro-minify-uglify "^0.56.4" - metro-react-native-babel-preset "^0.56.4" - metro-resolver "^0.56.4" - metro-source-map "^0.56.4" - metro-symbolicate "^0.56.4" + metro-babel-register "0.59.0" + metro-babel-transformer "0.59.0" + metro-cache "0.59.0" + metro-config "0.59.0" + metro-core "0.59.0" + metro-inspector-proxy "0.59.0" + metro-minify-uglify "0.59.0" + metro-react-native-babel-preset "0.59.0" + metro-resolver "0.59.0" + metro-source-map "0.59.0" + metro-symbolicate "0.59.0" mime-types "2.1.11" mkdirp "^0.5.1" node-fetch "^2.2.0" - nullthrows "^1.1.0" + nullthrows "^1.1.1" resolve "^1.5.0" rimraf "^2.5.4" serialize-error "^2.1.0" source-map "^0.5.6" + strip-ansi "^4.0.0" temp "0.8.3" throat "^4.1.0" wordwrap "^1.0.0" - write-file-atomic "^1.2.0" ws "^1.1.5" xpipe "^1.0.5" - yargs "^9.0.0" + yargs "^14.2.0" micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" @@ -4602,37 +5033,37 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -mime-db@1.40.0, "mime-db@>= 1.40.0 < 2": - version "1.40.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" - integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== +micromatch@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + +mime-db@1.51.0, "mime-db@>= 1.43.0 < 2": + version "1.51.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" + integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== mime-db@~1.23.0: version "1.23.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.23.0.tgz#a31b4070adaea27d732ea333740a64d0ec9a6659" - -mime-db@~1.26.0: - version "1.26.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" + integrity sha1-oxtAcK2uon1zLqMzdApk0OyaZlk= mime-types@2.1.11: version "2.1.11" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.11.tgz#c259c471bda808a85d6cd193b430a5fae4473b3c" + integrity sha1-wlnEcb2oCKhdbNGTtDCl+uRHOzw= dependencies: mime-db "~1.23.0" -mime-types@^2.1.12: - version "2.1.14" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee" +mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.34" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" + integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== dependencies: - mime-db "~1.26.0" - -mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.24" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" - integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== - dependencies: - mime-db "1.40.0" + mime-db "1.51.0" mime@1.6.0: version "1.6.0" @@ -4640,26 +5071,20 @@ mime@1.6.0: integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@^2.4.1: - version "2.4.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" - integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mimic-fn@^2.0.0: +mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" - dependencies: - brace-expansion "^1.0.0" - minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -4667,82 +5092,60 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@0.0.8, minimist@~0.0.1: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -minimist@^1.1.1, minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -minipass@^2.2.1, minipass@^2.3.5: - version "2.3.5" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" - integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minizlib@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" - integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== - dependencies: - minipass "^2.2.1" +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== mixin-deep@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" - integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ== + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.0, mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" +mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: - minimist "0.0.8" - -morgan@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.1.tgz#0a8d16734a1d9afbc824b99df87e738e58e2da59" - integrity sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA== - dependencies: - basic-auth "~2.0.0" - debug "2.6.9" - depd "~1.1.2" - on-finished "~2.3.0" - on-headers "~1.0.1" - -ms@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" + minimist "^1.2.5" ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -ms@^2.1.1: +ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mustache@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" + integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + nan@^2.12.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + version "2.15.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== nanomatch@^1.2.9: version "1.2.13" @@ -4766,103 +5169,69 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -needle@^2.2.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" - integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -neo-async@^2.6.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" - integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== - nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +nocache@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" + integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== + node-fetch@^1.0.1: - version "1.6.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== dependencies: encoding "^0.1.11" is-stream "^1.0.1" -node-fetch@^2.2.0, node-fetch@^2.5.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== +node-fetch@^2.2.0, node-fetch@^2.6.0: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-modules-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" - integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= - -node-notifier@^5.2.1: - version "5.4.0" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.0.tgz#7b455fdce9f7de0c63538297354f3db468426e6a" - integrity sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ== +node-notifier@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-6.0.0.tgz#cea319e06baa16deec8ce5cd7f133c4a46b68e12" + integrity sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw== dependencies: growly "^1.3.0" - is-wsl "^1.1.0" - semver "^5.5.0" + is-wsl "^2.1.1" + semver "^6.3.0" shellwords "^0.1.1" - which "^1.3.0" + which "^1.3.1" -node-notifier@^5.4.2: - version "5.4.3" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50" - integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q== - dependencies: - growly "^1.3.0" - is-wsl "^1.1.0" - semver "^5.5.0" - shellwords "^0.1.1" - which "^1.3.0" +node-releases@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" + integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== -node-pre-gyp@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" - integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4" +node-stream-zip@^1.9.1: + version "1.15.0" + resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" + integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= - dependencies: - abbrev "1" - osenv "^0.1.4" - -normalize-package-data@^2.3.2: - version "2.3.6" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.6.tgz#498fa420c96401f787402ba21e600def9f981fff" +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" + resolve "^1.10.0" semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" @@ -4873,18 +5242,10 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -npm-bundled@^1.0.1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" - integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== - -npm-packlist@^1.1.6: - version "1.4.1" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc" - integrity sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw== - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== npm-run-path@^2.0.0: version "2.0.2" @@ -4893,43 +5254,42 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" + path-key "^3.0.0" -nullthrows@^1.1.0: +nuget-exe@5.8.0: + version "5.8.0" + resolved "https://registry.yarnpkg.com/nuget-exe/-/nuget-exe-5.8.0.tgz#45287c4ad8308a5a7057913b32c08998fe7fdc82" + integrity sha512-m95SBWa+SJNOfR0GTwdQ3OwK2915Ts3XvajYx0iaOZYydwvz6reJyptiNGf+HQhvTe5viNiRAmGXxNww9obZjw== + +nullthrows@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - -nwsapi@^2.0.7: - version "2.1.4" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.4.tgz#e006a878db23636f8e8a67d33ca0e4edf61a842f" - integrity sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw== +nwsapi@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -ob1@^0.56.4: - version "0.56.4" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.56.4.tgz#c4acb3baa42f4993a44b35b2da7c8ef443dcccec" - integrity sha512-URgFof9z2wotiYFsqlydXtQfGV81gvBI2ODy64xfd3vPo+AYom5PVDX4t4zn23t/O+S2IxqApSQM8uJAybmz7w== +ob1@0.59.0: + version "0.59.0" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.59.0.tgz#ee103619ef5cb697f2866e3577da6f0ecd565a36" + integrity sha512-opXMTxyWJ9m68ZglCxwo0OPRESIC/iGmKFPXEXzMZqsVIrgoRXOHmoMDkQzz4y3irVjbyPJRAh5pI9fd0MJTFQ== object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-copy@^0.1.0: version "0.1.0" @@ -4940,12 +5300,12 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" - integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== +object-inspect@^1.11.0, object-inspect@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: +object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -4957,33 +5317,33 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== +object.assign@^4.1.0, object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" -object.fromentries@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab" - integrity sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA== +object.entries@^1.1.1: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" + integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== dependencies: - define-properties "^1.1.2" - es-abstract "^1.11.0" - function-bind "^1.1.1" - has "^1.0.1" + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" -object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= +object.fromentries@^2.0.2: + version "2.0.5" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" + integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" object.pick@^1.3.0: version "1.3.0" @@ -4992,16 +5352,22 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +object.values@^1.1.1: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" + integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" -on-headers@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" - on-headers@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" @@ -5010,6 +5376,7 @@ on-headers@~1.0.2: once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" @@ -5020,6 +5387,13 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + open@^6.2.0: version "6.4.0" resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" @@ -5027,28 +5401,22 @@ open@^6.2.0: dependencies: is-wsl "^1.1.0" -optimist@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" - dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" - -optionator@^0.8.1, optionator@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= +optionator@^0.8.1, optionator@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" - fast-levenshtein "~2.0.4" + fast-levenshtein "~2.0.6" levn "~0.3.0" prelude-ls "~1.1.2" type-check "~0.3.2" - wordwrap "~1.0.0" + word-wrap "~1.2.3" options@>=0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" + integrity sha1-7CLTEoBrtT5zF3Pnza788cZDEo8= ora@^3.4.0: version "3.4.0" @@ -5062,90 +5430,43 @@ ora@^3.4.0: strip-ansi "^5.2.0" wcwidth "^1.0.1" -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - -os-locale@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" - integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA== - dependencies: - execa "^0.7.0" - lcid "^1.0.0" - mem "^1.1.0" - -os-locale@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" - integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== - dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" - os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= -p-each-series@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" - integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E= - dependencies: - p-reduce "^1.0.0" +p-each-series@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" + integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-finally@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" + integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== + p-is-promise@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - -p-limit@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" - integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== - dependencies: - p-try "^2.0.0" - -p-limit@^2.2.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -5160,16 +5481,6 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-reduce@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" - integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -5182,12 +5493,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - dependencies: - error-ex "^1.2.0" - parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" @@ -5196,15 +5501,25 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + parse-node-version@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== -parse5@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" - integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== +parse5@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" + integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== parseurl@~1.3.3: version "1.3.3" @@ -5229,49 +5544,37 @@ path-exists@^4.0.0: path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= - dependencies: - pify "^2.0.0" - -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +picomatch@^2.0.4, picomatch@^2.2.3: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pify@^4.0.1: version "4.0.1" @@ -5279,11 +5582,9 @@ pify@^4.0.1: integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pirates@^4.0.0, pirates@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" - integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== - dependencies: - node-modules-regexp "^1.0.0" + version "4.0.5" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== pkg-dir@^3.0.0: version "3.0.0" @@ -5292,6 +5593,13 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + pkg-up@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" @@ -5299,14 +5607,13 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" -plist@^3.0.0, plist@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c" - integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ== +plist@^3.0.1, plist@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe" + integrity sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg== dependencies: - base64-js "^1.2.3" + base64-js "^1.5.1" xmlbuilder "^9.0.7" - xmldom "0.1.x" plugin-error@^0.1.2: version "0.1.2" @@ -5332,13 +5639,21 @@ posix-character-classes@^0.1.0: prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prettier@1.16.4: - version "1.16.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717" - integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g== +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" -pretty-format@^24.7.0, pretty-format@^24.9.0: +prettier@^2.0.2: + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== + +pretty-format@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== @@ -5348,7 +5663,7 @@ pretty-format@^24.7.0, pretty-format@^24.9.0: ansi-styles "^3.2.0" react-is "^16.8.4" -pretty-format@^25.1.0: +pretty-format@^25.1.0, pretty-format@^25.2.0, pretty-format@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a" integrity sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ== @@ -5358,14 +5673,6 @@ pretty-format@^25.1.0: ansi-styles "^4.0.0" react-is "^16.12.0" -private@^0.1.6: - version "0.1.7" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" - -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -5377,36 +5684,45 @@ progress@^2.0.0: integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== promise@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== dependencies: asap "~2.0.3" +promise@^8.0.3: + version "8.1.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" + integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q== + dependencies: + asap "~2.0.6" + prompts@^2.0.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.2.1.tgz#f901dd2a2dfee080359c0e20059b24188d75ad35" - integrity sha512-VObPvJiWPhpZI6C5m60XOzTfnYg/xc/an+r9VYymj9WJW3B/DIH+REzjpAACPf8brwPeP+7vz3bIim3S+AaMjw== + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== dependencies: kleur "^3.0.3" - sisteransi "^1.0.3" + sisteransi "^1.0.5" prop-types@^15.6.2, prop-types@^15.7.2: - version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" - integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" object-assign "^4.1.1" - react-is "^16.8.1" + react-is "^16.13.1" -pseudomap@^1.0.1: +pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.24, psl@^1.1.28: - version "1.1.32" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.32.tgz#3f132717cf2f9c169724b2b6caf373cf694198db" - integrity sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g== +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== pump@^3.0.0: version "3.0.0" @@ -5416,193 +5732,146 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -react-devtools-core@^3.6.3: - version "3.6.3" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-3.6.3.tgz#977d95b684c6ad28205f0c62e1e12c5f16675814" - integrity sha512-+P+eFy/yo8Z/UH9J0DqHZuUM5+RI2wl249TNvMx3J2jpUomLQa4Zxl56GEotGfw3PIP1eI+hVf1s53FlUONStQ== +react-devtools-core@^4.6.0: + version "4.23.0" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.23.0.tgz#dff9d12202a472ef62632203d6de3877dc6e58be" + integrity sha512-KkzneT1LczFtebbTJlvRphIRvzuHLhI9ghfrseVv9ktBs+l2cXy8Svw5U16lzQnwU9okVEcURmGPgH79WWrlaw== dependencies: shell-quote "^1.6.1" - ws "^3.3.1" + ws "^7" -react-is@^16.12.0: +react-is@^16.12.0, react-is@^16.13.1, react-is@^16.8.4, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^16.8.1: - version "16.8.6" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" - integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== - -react-is@^16.8.4, react-is@^16.8.6: - version "16.9.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" - integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== - -react-native-video@../../: - version "5.2.0" +react-native-video@../..: + version "6.0.0-alpha1" dependencies: keymirror "^0.1.1" prop-types "^15.7.2" shaka-player "^3.3.2" -react-native-windows@^0.61.0-0: - version "0.61.15" - resolved "https://registry.yarnpkg.com/react-native-windows/-/react-native-windows-0.61.15.tgz#523c904245ae764fa0e2c6b56b259e0ef0be6d3f" - integrity sha512-LSjUoNoLC1z76FsyRnKpEQQWupjsie918mryfHYddhWuZrdkKrqye043sgx1S/QayBbBH+1vMRNmwGtRqNxt0w== +react-native-windows@0.63.41: + version "0.63.41" + resolved "https://registry.yarnpkg.com/react-native-windows/-/react-native-windows-0.63.41.tgz#96f59bc24749b6c167cb4f35fd74b66f78f4a4bb" + integrity sha512-AhLQP0dzX4jhtv9AsR5KF+BIlccKKPzTSmivg3ZUirqQxXwDDMjtIGwgFnp1WyH8cFoUEvpuyNnHsU3f7LxEyg== dependencies: - "@babel/runtime" "^7.4.0" - cli-spinners "^2.2.0" - create-react-class "^15.6.3" - envinfo "^7.5.0" - fbjs "^1.0.0" - glob "^7.1.1" - ora "^3.4.0" - prop-types "^15.7.2" - regenerator-runtime "^0.13.2" - shelljs "^0.7.8" - username "^5.1.0" - uuid "^3.3.2" - xml-parser "^1.2.1" - -react-native@0.61.5: - version "0.61.5" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.61.5.tgz#6e21acb56cbd75a3baeb1f70201a66f42600bba8" - integrity sha512-MXqE3NoGO0T3dUKIKkIppijBhRRMpfN6ANbhMXHDuyfA+fSilRWgCwYgR/YNCC7ntECoJYikKaNTUBB0DeQy6Q== - dependencies: - "@babel/runtime" "^7.0.0" - "@react-native-community/cli" "^3.0.0" - "@react-native-community/cli-platform-android" "^3.0.0" - "@react-native-community/cli-platform-ios" "^3.0.0" + "@babel/runtime" "^7.8.4" + "@react-native-windows/cli" "0.63.14" abort-controller "^3.0.0" - art "^0.10.0" + anser "^1.4.9" base64-js "^1.1.2" - connect "^3.6.5" - create-react-class "^15.6.3" - escape-string-regexp "^1.0.5" event-target-shim "^5.0.1" fbjs "^1.0.0" fbjs-scripts "^1.1.0" - hermes-engine "^0.2.1" + invariant "^2.2.4" + nullthrows "^1.1.1" + pretty-format "^24.9.0" + promise "^8.0.3" + prop-types "^15.7.2" + react-devtools-core "^4.6.0" + regenerator-runtime "^0.13.2" + scheduler "0.19.1" + stacktrace-parser "^0.1.3" + use-subscription "^1.0.0" + whatwg-fetch "^3.0.0" + +react-native@0.63.4: + version "0.63.4" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.63.4.tgz#2210fdd404c94a5fa6b423c6de86f8e48810ec36" + integrity sha512-I4kM8kYO2mWEYUFITMcpRulcy4/jd+j9T6PbIzR0FuMcz/xwd+JwHoLPa1HmCesvR1RDOw9o4D+OFLwuXXfmGw== + dependencies: + "@babel/runtime" "^7.0.0" + "@react-native-community/cli" "^4.10.0" + "@react-native-community/cli-platform-android" "^4.10.0" + "@react-native-community/cli-platform-ios" "^4.10.0" + abort-controller "^3.0.0" + anser "^1.4.9" + base64-js "^1.1.2" + event-target-shim "^5.0.1" + fbjs "^1.0.0" + fbjs-scripts "^1.1.0" + hermes-engine "~0.5.0" invariant "^2.2.4" jsc-android "^245459.0.0" - metro-babel-register "^0.56.0" - metro-react-native-babel-transformer "^0.56.0" - metro-source-map "^0.56.0" - nullthrows "^1.1.0" - pretty-format "^24.7.0" - promise "^7.1.1" + metro-babel-register "0.59.0" + metro-react-native-babel-transformer "0.59.0" + metro-source-map "0.59.0" + nullthrows "^1.1.1" + pretty-format "^24.9.0" + promise "^8.0.3" prop-types "^15.7.2" - react-devtools-core "^3.6.3" + react-devtools-core "^4.6.0" react-refresh "^0.4.0" regenerator-runtime "^0.13.2" - scheduler "0.15.0" + scheduler "0.19.1" stacktrace-parser "^0.1.3" + use-subscription "^1.0.0" whatwg-fetch "^3.0.0" react-refresh@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.2.tgz#54a277a6caaac2803d88f1d6f13c1dcfbd81e334" - integrity sha512-kv5QlFFSZWo7OlJFNYbxRtY66JImuP2LcrFgyJfQaf85gSP+byzG21UbDQEYjU7f//ny8rwiEkO6py2Y+fEgAQ== + version "0.4.3" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53" + integrity sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA== -react-test-renderer@16.8.6: - version "16.8.6" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.6.tgz#188d8029b8c39c786f998aa3efd3ffe7642d5ba1" - integrity sha512-H2srzU5IWYT6cZXof6AhUcx/wEyJddQ8l7cLM/F7gDXYyPr4oq+vCIxJYXVGhId1J706sqziAjuOEjyNkfgoEw== +react-test-renderer@16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.13.1.tgz#de25ea358d9012606de51e012d9742e7f0deabc1" + integrity sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ== dependencies: object-assign "^4.1.1" prop-types "^15.6.2" react-is "^16.8.6" - scheduler "^0.13.6" + scheduler "^0.19.1" -react@^16.12.0: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" - integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== +react@16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" + integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" - integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" -read-pkg-up@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" - integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== dependencies: - find-up "^3.0.0" - read-pkg "^3.0.0" + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" - integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - -"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.1.5, readable-stream@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.3.tgz#9cf49463985df016c8ae8813097a9293a9b33729" - dependencies: - buffer-shims "^1.0.0" - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - -readable-stream@~2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== +readable-stream@^2.0.1, readable-stream@^2.2.2, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -5612,12 +5881,10 @@ readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -realpath-native@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" - integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== - dependencies: - util.promisify "^1.0.0" +realpath-native@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" + integrity sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q== rechoir@^0.6.2: version "0.6.2" @@ -5626,34 +5893,29 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -regenerate-unicode-properties@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" - integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== +regenerate-unicode-properties@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326" + integrity sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA== dependencies: - regenerate "^1.4.0" + regenerate "^1.4.2" -regenerate@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" - integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.2: - version "0.13.3" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" - integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== +regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== -regenerator-runtime@^0.13.4: - version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== - -regenerator-transform@^0.14.0: - version "0.14.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb" - integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ== +regenerator-transform@^0.14.2: + version "0.14.5" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" + integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== dependencies: - private "^0.1.6" + "@babel/runtime" "^7.8.4" regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" @@ -5663,32 +5925,45 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp.prototype.flags@^1.3.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" + integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== -regexpu-core@^4.5.4: - version "4.6.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" - integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg== +regexpp@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +regexpu-core@^4.7.1: + version "4.8.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0" + integrity sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg== dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.1.0" - regjsgen "^0.5.0" - regjsparser "^0.6.0" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.1.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^9.0.0" + regjsgen "^0.5.2" + regjsparser "^0.7.0" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.0.0" -regjsgen@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" - integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== +regjsgen@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" + integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== -regjsparser@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" - integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ== +regjsparser@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.7.0.tgz#a6b667b54c885e18b52554cb4960ef71187e9968" + integrity sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ== dependencies: jsesc "~0.5.0" @@ -5698,33 +5973,35 @@ remove-trailing-separator@^1.0.1: integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= repeat-element@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -request-promise-core@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346" - integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== +request-promise-core@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" + integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== dependencies: - lodash "^4.17.11" + lodash "^4.17.19" -request-promise-native@^1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59" - integrity sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== +request-promise-native@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" + integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== dependencies: - request-promise-core "1.1.2" + request-promise-core "1.1.4" stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.87.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== +request@^2.88.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -5733,7 +6010,7 @@ request@^2.87.0: extend "~3.0.2" forever-agent "~0.6.1" form-data "~2.3.2" - har-validator "~5.1.0" + har-validator "~5.1.3" http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" @@ -5743,17 +6020,14 @@ request@^2.87.0: performance-now "^2.1.0" qs "~6.5.2" safe-buffer "^5.1.2" - tough-cookie "~2.4.3" + tough-cookie "~2.5.0" tunnel-agent "^0.6.0" uuid "^3.3.2" require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= require-main-filename@^2.0.0: version "2.0.0" @@ -5765,12 +6039,12 @@ reselect@^4.0.0: resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.5.tgz#852c361247198da6756d07d9296c2b51eddb79f6" integrity sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ== -resolve-cwd@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" - integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: - resolve-from "^3.0.0" + resolve-from "^5.0.0" resolve-from@^3.0.0: version "3.0.0" @@ -5782,6 +6056,11 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -5792,15 +6071,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@^1.1.6: - version "1.19.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" - integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== - dependencies: - is-core-module "^2.1.0" - path-parse "^1.0.6" - -resolve@^1.13.1: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.15.1, resolve@^1.17.0, resolve@^1.5.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -5809,20 +6080,6 @@ resolve@^1.13.1: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.3.2, resolve@^1.5.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.0.tgz#4014870ba296176b86343d50b60f3b50609ce232" - integrity sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw== - dependencies: - path-parse "^1.0.6" - -resolve@^1.8.1, resolve@^1.9.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" - integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== - dependencies: - path-parse "^1.0.6" - restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -5831,12 +6088,20 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -rimraf@2.6.3, rimraf@^2.6.1: +rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== @@ -5844,32 +6109,33 @@ rimraf@2.6.3, rimraf@^2.6.1: glob "^7.1.3" rimraf@^2.5.4: - version "2.6.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" - dependencies: - glob "^7.0.5" - -rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" +rimraf@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" + integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI= rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - dependencies: - is-promise "^2.1.0" +run-async@^2.2.0, run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== rx-lite-aggregates@^4.0.8: version "4.0.8" @@ -5883,28 +6149,22 @@ rx-lite@*, rx-lite@^4.0.8: resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= -rxjs@^5.4.3: - version "5.5.12" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" - integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== - dependencies: - symbol-observable "1.0.1" - -rxjs@^6.4.0: - version "6.5.3" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" - integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA== +rxjs@^6.6.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" -safe-buffer@5.1.2, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" +safe-buffer@^5.0.1, safe-buffer@^5.1.2: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex@^1.1.0: version "1.1.0" @@ -5913,7 +6173,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -5933,55 +6193,52 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sax@^1.2.1, sax@^1.2.4: +sax@^1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -scheduler@0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.15.0.tgz#6bfcf80ff850b280fed4aeecc6513bc0b4f17f8e" - integrity sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg== +saxes@^3.1.9: + version "3.1.11" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" + integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== + dependencies: + xmlchars "^2.1.1" + +scheduler@0.19.1, scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" -scheduler@^0.13.6: - version "0.13.6" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889" - integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - -"semver@2 || 3 || 4 || 5", semver@^5.1.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - -semver@5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" - integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== - -semver@^5.3.0, semver@^5.4.1, semver@^5.5.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" - integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== - -semver@^5.5.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== +semver@^7.1.3, semver@^7.3.2: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +send@0.17.2: + version "0.17.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" + integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== dependencies: debug "2.6.9" depd "~1.1.2" @@ -5990,9 +6247,9 @@ send@0.17.1: escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.7.2" + http-errors "1.8.1" mime "1.6.0" - ms "2.1.1" + ms "2.1.3" on-finished "~2.3.0" range-parser "~1.2.1" statuses "~1.5.0" @@ -6003,33 +6260,24 @@ serialize-error@^2.1.0: integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go= serve-static@^1.13.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + version "1.14.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" + integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.17.1" + send "0.17.2" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-value@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" - integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE= - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.1" - to-object-path "^0.3.0" - -set-value@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" - integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg== +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" @@ -6039,19 +6287,27 @@ set-value@^2.0.0: setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== shaka-player@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-3.3.2.tgz#6c903413d64d0205d2c9672440f041233075e748" - integrity sha512-6N9J/Dy7xprT8xKcs9+YYiEJsXfKn3xtVzLndLRFP2hR13tn3YzVhP83hZmbnLzOEFBpuYlRq4kNrBNlOA+4/g== + version "3.3.4" + resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-3.3.4.tgz#7d137a18fc0f55c50852a4348c8319495b5fa546" + integrity sha512-8PrUBA8aOABGvhQVa59XMoPo5myAoQF4ptx6gvZWPOBtdsyVaamqQKELY77ikZJ1ejup7BmHf42MXGFmxQfcaA== dependencies: eme-encryption-scheme-polyfill "^2.0.3" +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -6059,24 +6315,42 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= -shell-quote@1.6.1, shell-quote@^1.6.1: +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c= dependencies: array-filter "~0.0.0" array-map "~0.0.0" array-reduce "~0.0.0" jsonify "~0.0.0" -shelljs@^0.7.8: - version "0.7.8" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" - integrity sha1-3svPh0sNHl+3LhSxZKloMEjprLM= +shell-quote@^1.6.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" + integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== + +shelljs@^0.8.4: + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== dependencies: glob "^7.0.0" interpret "^1.0.0" @@ -6087,23 +6361,38 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== +shimmer@^1.1.0, shimmer@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + version "3.0.6" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" + integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== simple-plist@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.0.0.tgz#bed3085633b22f371e111f45d159a1ccf94b81eb" - integrity sha512-043L2rO80LVF7zfZ+fqhsEkoJFvW8o59rt/l4ctx1TJWoTx7/jkiS1R5TatD15Z1oYnuLJytzE7gcnnBuIPL2g== + version "1.3.0" + resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.3.0.tgz#f451997663eafd8ea6bad353a01caf49ef186d43" + integrity sha512-uYWpeGFtZtVt2NhG4AHgpwx323zxD85x42heMJBan1qAiqqozIlaGrwrEt6kRjXWRWIXsuV1VLCvVmZan2B5dg== dependencies: - bplist-creator "0.0.7" - bplist-parser "0.1.1" - plist "^3.0.1" + bplist-creator "0.1.0" + bplist-parser "0.3.0" + plist "^3.0.4" -sisteransi@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.3.tgz#98168d62b79e3a5e758e27ae63c4a053d748f4eb" - integrity sha512-SbEG75TzH8G7eVXFSN5f9EExILKfly7SUvVY5DhhYLvfhKqhDFY0OzevWa/zwak0RLRfWS5AvfMWpd9gJvr5Yg== +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== slash@^2.0.0: version "2.0.0" @@ -6124,10 +6413,6 @@ slice-ansi@^2.0.0, slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" -slide@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" - snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -6159,51 +6444,69 @@ snapdragon@^0.8.1: use "^3.1.0" source-map-resolve@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" - integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== dependencies: - atob "^2.1.1" + atob "^2.1.2" decode-uri-component "^0.2.0" resolve-url "^0.2.1" source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.6, source-map-support@^0.5.9: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== +source-map-support@^0.5.16, source-map-support@^0.5.6: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" - integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== source-map@^0.5.0, source-map@^0.5.6: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -spdx-correct@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== dependencies: - spdx-license-ids "^1.0.2" + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" -spdx-expression-parse@~1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== -spdx-license-ids@^1.0.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.11" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" + integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" @@ -6218,28 +6521,43 @@ sprintf-js@~1.0.2: integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= sshpk@^1.7.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.11.0.tgz#2d8d5ebb4a6fab28ffba37fa62a90f4a3ea59d77" + version "1.17.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" + integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" ecc-jsbn "~0.1.1" - jodid25519 "^1.0.0" + getpass "^0.1.1" jsbn "~0.1.0" + safer-buffer "^2.0.2" tweetnacl "~0.14.0" +stack-chain@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" + integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU= + stack-utils@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" - integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== + version "1.0.5" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.5.tgz#a19b0b01947e0029c8e451d5d61a498f5bb1471b" + integrity sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ== + dependencies: + escape-string-regexp "^2.0.0" + +stackframe@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" + integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA== stacktrace-parser@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.4.tgz#01397922e5f62ecf30845522c95c4fe1d25e7d4e" + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" static-extend@^0.1.1: version "0.1.2" @@ -6259,28 +6577,20 @@ stealthy-require@^1.1.1: resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= -stream-buffers@~2.2.0: +stream-buffers@2.2.x: version "2.2.0" resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ= -string-length@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" - integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0= +string-length@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837" + integrity sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA== dependencies: astral-regex "^1.0.0" - strip-ansi "^4.0.0" + strip-ansi "^5.2.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: +string-width@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -6297,25 +6607,44 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string.prototype.trimleft@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" - integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" -string.prototype.trimright@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" - integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== +string.prototype.matchall@^4.0.2: + version "4.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa" + integrity sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - function-bind "^1.1.1" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.3.1" + side-channel "^1.0.4" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" string_decoder@~1.1.1: version "1.1.1" @@ -6324,12 +6653,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - dependencies: - ansi-regex "^2.0.0" - strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" @@ -6344,24 +6667,32 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= -strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" - integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" +strip-json-comments@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== sudo-prompt@^9.0.0: version "9.2.1" @@ -6382,23 +6713,26 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" +supports-hyperlinks@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -symbol-observable@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" - integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= - symbol-tree@^3.2.2: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -6414,54 +6748,47 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -tar@^4: - version "4.4.10" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" - integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.3.5" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" - temp@0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" + integrity sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k= dependencies: os-tmpdir "^1.0.0" rimraf "~2.2.6" -test-exclude@^5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" - integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== dependencies: - glob "^7.1.3" + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" minimatch "^3.0.4" - read-pkg-up "^4.0.0" - require-main-filename "^2.0.0" text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -throat@^4.0.0, throat@^4.1.0: +throat@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= -through2@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" - dependencies: - readable-stream "^2.1.5" - xtend "~4.0.1" +throat@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" + integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== -through2@^2.0.1: +through2@^2.0.0, through2@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== @@ -6472,10 +6799,12 @@ through2@^2.0.1: through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= time-stamp@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.0.1.tgz#9f4bd23559c9365966f3302dbba2b07c6b99b151" + version "1.1.0" + resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" + integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= tmp@^0.0.33: version "0.0.33" @@ -6484,9 +6813,10 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-fast-properties@^2.0.0: version "2.0.0" @@ -6508,6 +6838,13 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -6518,12 +6855,12 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -tough-cookie@^2.3.3, tough-cookie@^2.3.4: +tough-cookie@^2.3.3, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -6531,13 +6868,14 @@ tough-cookie@^2.3.3, tough-cookie@^2.3.4: psl "^1.1.28" punycode "^2.1.1" -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== +tough-cookie@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" + integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== dependencies: - psl "^1.1.24" - punycode "^1.4.1" + ip-regex "^2.1.0" + psl "^1.1.28" + punycode "^2.1.1" tr46@^1.0.1: version "1.0.1" @@ -6546,46 +6884,83 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= tslib@^1.8.1, tslib@^1.9.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tsutils@^3.7.0: - version "3.17.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" - integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== +tsutils@^3.17.1: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= ua-parser-js@^0.7.18: - version "0.7.20" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.20.tgz#7527178b82f6a62a0f243d1f94fd30e3e3c21098" - integrity sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw== + version "0.7.31" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" + integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== uglify-es@^3.1.9: version "3.3.9" @@ -6595,55 +6970,53 @@ uglify-es@^3.1.9: commander "~2.13.0" source-map "~0.6.1" -uglify-js@^3.1.4: - version "3.6.0" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5" - integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg== - dependencies: - commander "~2.20.0" - source-map "~0.6.1" - ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" + integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po= -ultron@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" - integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== - -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" - integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== - -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" - integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" -unicode-match-property-value-ecmascript@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" - integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== -unicode-property-aliases-ecmascript@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" - integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" + integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" + integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== union-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" - integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ= + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== dependencies: arr-union "^3.1.0" get-value "^2.0.6" is-extendable "^0.1.1" - set-value "^0.4.3" + set-value "^2.0.1" universalify@^0.1.0: version "0.1.2" @@ -6653,6 +7026,7 @@ universalify@^0.1.0: unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= unset-value@^1.0.0: version "1.0.0" @@ -6663,9 +7037,9 @@ unset-value@^1.0.0: isobject "^3.0.0" uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" @@ -6674,6 +7048,13 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= +use-subscription@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" + integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA== + dependencies: + object-assign "^4.1.1" + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -6690,14 +7071,7 @@ username@^5.1.0: util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - -util.promisify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" - integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== - dependencies: - define-properties "^1.1.2" - object.getownpropertydescriptors "^2.0.3" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= utils-merge@1.0.1: version "1.0.1" @@ -6705,32 +7079,45 @@ utils-merge@1.0.1: integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= uuid@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== v8-compile-cache@^2.0.3: - version "2.1.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" - integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== + +v8-to-istanbul@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz#b97936f21c0e2d9996d4985e5c5156e9d4e49cd6" + integrity sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" validate-npm-package-license@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: - spdx-correct "~1.0.0" - spdx-expression-parse "~1.0.0" + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -verror@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= dependencies: - extsprintf "1.0.2" + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" vlq@^1.0.0: version "1.0.1" @@ -6738,17 +7125,27 @@ vlq@^1.0.0: integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== w3c-hr-time@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" - integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== dependencies: - browser-process-hrtime "^0.1.2" + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" + integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== + dependencies: + domexception "^1.0.1" + webidl-conversions "^4.0.2" + xml-name-validator "^3.0.0" walker@^1.0.7, walker@~1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== dependencies: - makeerror "1.0.x" + makeerror "1.0.12" wcwidth@^1.0.1: version "1.0.1" @@ -6757,89 +7154,89 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" -whatwg-fetch@>=0.10.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-1.1.1.tgz#ac3c9d39f320c6dce5339969d054ef43dd333319" +whatwg-fetch@>=0.10.0, whatwg-fetch@^3.0.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" + integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== -whatwg-fetch@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" - integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== - -whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0: +whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^6.4.1: - version "6.5.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" - integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== dependencies: lodash.sortby "^4.7.0" tr46 "^1.0.1" webidl-conversions "^4.0.2" -whatwg-url@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd" - integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ== +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.9: - version "1.2.12" - resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192" - dependencies: - isexe "^1.1.1" - -which@^1.3.0: +which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== +which@^2.0.1, which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: - string-width "^1.0.2 || 2" + isexe "^2.0.0" -wordwrap@^1.0.0, wordwrap@~1.0.0: +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= wrap-ansi@^5.1.0: version "5.1.0" @@ -6850,26 +7247,29 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" - integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== dependencies: - graceful-fs "^4.1.11" imurmurhash "^0.1.4" + is-typedarray "^1.0.0" signal-exit "^3.0.2" - -write-file-atomic@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.1.tgz#7d45ba32316328dd1ec7d90f60ebc0d845bb759a" - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - slide "^1.1.5" + typedarray-to-buffer "^3.1.5" write@1.0.3: version "1.0.3" @@ -6878,14 +7278,7 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" -ws@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f" - dependencies: - options ">=0.0.5" - ultron "1.0.x" - -ws@^1.1.5: +ws@^1.1.0, ws@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" integrity sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w== @@ -6893,26 +7286,15 @@ ws@^1.1.5: options ">=0.0.5" ultron "1.0.x" -ws@^3.3.1: - version "3.3.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" - integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== - dependencies: - async-limiter "~1.0.0" - safe-buffer "~5.1.0" - ultron "~1.1.0" - -ws@^5.2.0: - version "5.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" - integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== - dependencies: - async-limiter "~1.0.0" +ws@^7, ws@^7.0.0: + version "7.5.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" + integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== xcode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xcode/-/xcode-2.0.0.tgz#134f1f94c26fbfe8a9aaa9724bfb2772419da1a2" - integrity sha512-5xF6RCjAdDEiEsbbZaS/gBRt3jZ/177otZcpoLCjGN/u1LrfgH7/Sgeeavpr/jELpyDqN2im3AKosl2G2W8hfw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/xcode/-/xcode-2.1.0.tgz#bab64a7e954bb50ca8d19da7e09531c65a43ecfe" + integrity sha512-uCrmPITrqTEzhn0TtT57fJaNaw8YJs1aCzs+P/QqxsDbvPZSv7XMPPwXrKvHtD6pLjBM/NaVwraWJm8q83Y4iQ== dependencies: simple-plist "^1.0.0" uuid "^3.3.2" @@ -6934,6 +7316,11 @@ xmlbuilder@^9.0.7: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= +xmlchars@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + xmldoc@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.1.2.tgz#6666e029fe25470d599cd30e23ff0d1ed50466d7" @@ -6941,84 +7328,66 @@ xmldoc@^1.1.2: dependencies: sax "^1.2.1" -xmldom@0.1.x: - version "0.1.27" - resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9" +xpath@^0.0.27: + version "0.0.27" + resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92" + integrity sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ== xpipe@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/xpipe/-/xpipe-1.0.5.tgz#8dd8bf45fc3f7f55f0e054b878f43a62614dafdf" integrity sha1-jdi/Rfw/f1Xw4FS4ePQ6YmFNr98= +xregexp@^4.3.0: + version "4.4.1" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.4.1.tgz#c84a88fa79e9ab18ca543959712094492185fe65" + integrity sha512-2u9HwfadaJaY9zHtRRnH6BY6CQVNQKkYm3oLtC9gJXXzfsbACg5X5e4EZZGVAH+YIfa+QA9lsFQTTe3HURF3ag== + dependencies: + "@babel/runtime-corejs3" "^7.12.1" + xtend@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -y18n@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== -"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yallist@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.0.0.tgz#306c543835f09ee1a4cb23b7bce9ab341c91cdd4" - -yallist@^3.0.0, yallist@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" - integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== - -yargs-parser@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" - integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== +yargs-parser@^15.0.1: + version "15.0.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.3.tgz#316e263d5febe8b38eef61ac092b33dfcc9b1115" + integrity sha512-/MVEVjTXy/cGAjdtQf8dW3V9b97bPN7rNn8ETj6BmAQL7ibC7O1Q9SPJbGjgh3SlwoBNXMzj/ZGIj8mBgl12YA== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^13.1.1: - version "13.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" - integrity sha1-jQrELxbqVd69MyyvTEA4s+P139k= - dependencies: - camelcase "^4.1.0" - -yargs@^12.0.5: - version "12.0.5" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" - integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== - dependencies: - cliui "^4.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^1.0.1" - os-locale "^3.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1 || ^4.0.0" - yargs-parser "^11.1.1" - -yargs@^13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== +yargs@^14.2.0: + version "14.2.3" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414" + integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg== dependencies: cliui "^5.0.0" + decamelize "^1.2.0" find-up "^3.0.0" get-caller-file "^2.0.1" require-directory "^2.1.1" @@ -7027,23 +7396,21 @@ yargs@^13.3.0: string-width "^3.0.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^13.1.1" + yargs-parser "^15.0.1" -yargs@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-9.0.1.tgz#52acc23feecac34042078ee78c0c007f5085db4c" - integrity sha1-UqzCP+7Kw0BCB47njAwAf1CF20w= +yargs@^15.1.0, yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== dependencies: - camelcase "^4.1.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - read-pkg-up "^2.0.0" + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" require-directory "^2.1.1" - require-main-filename "^1.0.1" + require-main-filename "^2.0.0" set-blocking "^2.0.0" - string-width "^2.0.0" + string-width "^4.2.0" which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^7.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" diff --git a/ios/RCTVideo.xcodeproj/project.pbxproj b/ios/RCTVideo.xcodeproj/project.pbxproj index 4e675ac7..e601ea80 100644 --- a/ios/RCTVideo.xcodeproj/project.pbxproj +++ b/ios/RCTVideo.xcodeproj/project.pbxproj @@ -7,14 +7,12 @@ objects = { /* Begin PBXBuildFile section */ - D1107C0A2110259000073188 /* UIView+FindUIViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C032110259000073188 /* UIView+FindUIViewController.m */; }; - D1107C0B2110259000073188 /* UIView+FindUIViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C032110259000073188 /* UIView+FindUIViewController.m */; }; - D1107C0C2110259000073188 /* RCTVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C052110259000073188 /* RCTVideo.m */; }; - D1107C0D2110259000073188 /* RCTVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C052110259000073188 /* RCTVideo.m */; }; - D1107C0E2110259000073188 /* RCTVideoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C062110259000073188 /* RCTVideoManager.m */; }; - D1107C0F2110259000073188 /* RCTVideoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C062110259000073188 /* RCTVideoManager.m */; }; - D1107C102110259000073188 /* RCTVideoPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C082110259000073188 /* RCTVideoPlayerViewController.m */; }; - D1107C112110259000073188 /* RCTVideoPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C082110259000073188 /* RCTVideoPlayerViewController.m */; }; + 0177D39A27170A7A00F5BE18 /* RCTVideoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0177D39227170A7A00F5BE18 /* RCTVideoManager.swift */; }; + 0177D39B27170A7A00F5BE18 /* UIView+FindUIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0177D39327170A7A00F5BE18 /* UIView+FindUIViewController.swift */; }; + 0177D39C27170A7A00F5BE18 /* RCTVideoPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0177D39427170A7A00F5BE18 /* RCTVideoPlayerViewController.swift */; }; + 0177D39D27170A7A00F5BE18 /* RCTVideoPlayerViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0177D39627170A7A00F5BE18 /* RCTVideoPlayerViewControllerDelegate.swift */; }; + 0177D39E27170A7A00F5BE18 /* RCTVideoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0177D39727170A7A00F5BE18 /* RCTVideoManager.m */; }; + 0177D39F27170A7A00F5BE18 /* RCTVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0177D39927170A7A00F5BE18 /* RCTVideo.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -39,17 +37,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 01450CB5271D5738005D8F6B /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTVideo.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 01489050272001A100E69940 /* DataStructures */ = {isa = PBXFileReference; lastKnownFileType = folder; name = DataStructures; path = Video/DataStructures; sourceTree = ""; }; + 01489051272001A100E69940 /* Features */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Features; path = Video/Features; sourceTree = ""; }; + 0177D39227170A7A00F5BE18 /* RCTVideoManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RCTVideoManager.swift; path = Video/RCTVideoManager.swift; sourceTree = ""; }; + 0177D39327170A7A00F5BE18 /* UIView+FindUIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+FindUIViewController.swift"; path = "Video/UIView+FindUIViewController.swift"; sourceTree = ""; }; + 0177D39427170A7A00F5BE18 /* RCTVideoPlayerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RCTVideoPlayerViewController.swift; path = Video/RCTVideoPlayerViewController.swift; sourceTree = ""; }; + 0177D39527170A7A00F5BE18 /* RCTSwiftLog */ = {isa = PBXFileReference; lastKnownFileType = folder; name = RCTSwiftLog; path = Video/RCTSwiftLog; sourceTree = ""; }; + 0177D39627170A7A00F5BE18 /* RCTVideoPlayerViewControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RCTVideoPlayerViewControllerDelegate.swift; path = Video/RCTVideoPlayerViewControllerDelegate.swift; sourceTree = ""; }; + 0177D39727170A7A00F5BE18 /* RCTVideoManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTVideoManager.m; path = Video/RCTVideoManager.m; sourceTree = ""; }; + 0177D39827170A7A00F5BE18 /* RCTVideo-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "RCTVideo-Bridging-Header.h"; path = "Video/RCTVideo-Bridging-Header.h"; sourceTree = ""; }; + 0177D39927170A7A00F5BE18 /* RCTVideo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RCTVideo.swift; path = Video/RCTVideo.swift; sourceTree = ""; }; 134814201AA4EA6300B7C361 /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTVideo.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 641E28441F0EEC8500443AF6 /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTVideo.a; sourceTree = BUILT_PRODUCTS_DIR; }; - D1107C012110259000073188 /* RCTVideoPlayerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideoPlayerViewController.h; path = Video/RCTVideoPlayerViewController.h; sourceTree = ""; }; - D1107C022110259000073188 /* RCTVideoPlayerViewControllerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideoPlayerViewControllerDelegate.h; path = Video/RCTVideoPlayerViewControllerDelegate.h; sourceTree = ""; }; - D1107C032110259000073188 /* UIView+FindUIViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+FindUIViewController.m"; path = "Video/UIView+FindUIViewController.m"; sourceTree = ""; }; - D1107C042110259000073188 /* UIView+FindUIViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+FindUIViewController.h"; path = "Video/UIView+FindUIViewController.h"; sourceTree = ""; }; - D1107C052110259000073188 /* RCTVideo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTVideo.m; path = Video/RCTVideo.m; sourceTree = ""; }; - D1107C062110259000073188 /* RCTVideoManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTVideoManager.m; path = Video/RCTVideoManager.m; sourceTree = ""; }; - D1107C072110259000073188 /* RCTVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideo.h; path = Video/RCTVideo.h; sourceTree = ""; }; - D1107C082110259000073188 /* RCTVideoPlayerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTVideoPlayerViewController.m; path = Video/RCTVideoPlayerViewController.m; sourceTree = ""; }; - D1107C092110259000073188 /* RCTVideoManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideoManager.h; path = Video/RCTVideoManager.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -88,18 +87,19 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( - D1107C072110259000073188 /* RCTVideo.h */, - D1107C052110259000073188 /* RCTVideo.m */, - D1107C092110259000073188 /* RCTVideoManager.h */, - D1107C062110259000073188 /* RCTVideoManager.m */, - D1107C012110259000073188 /* RCTVideoPlayerViewController.h */, - D1107C082110259000073188 /* RCTVideoPlayerViewController.m */, - D1107C022110259000073188 /* RCTVideoPlayerViewControllerDelegate.h */, - D1107C042110259000073188 /* UIView+FindUIViewController.h */, - D1107C032110259000073188 /* UIView+FindUIViewController.m */, + 01489050272001A100E69940 /* DataStructures */, + 01489051272001A100E69940 /* Features */, + 0177D39527170A7A00F5BE18 /* RCTSwiftLog */, + 0177D39927170A7A00F5BE18 /* RCTVideo.swift */, + 0177D39727170A7A00F5BE18 /* RCTVideoManager.m */, + 0177D39227170A7A00F5BE18 /* RCTVideoManager.swift */, + 0177D39427170A7A00F5BE18 /* RCTVideoPlayerViewController.swift */, + 0177D39627170A7A00F5BE18 /* RCTVideoPlayerViewControllerDelegate.swift */, + 0177D39327170A7A00F5BE18 /* UIView+FindUIViewController.swift */, + 0177D39827170A7A00F5BE18 /* RCTVideo-Bridging-Header.h */, 134814211AA4EA7D00B7C361 /* Products */, - 641E28441F0EEC8500443AF6 /* libRCTVideo.a */, 49E995712048B4CE00EA7890 /* Frameworks */, + 01450CB5271D5738005D8F6B /* libRCTVideo.a */, ); sourceTree = ""; }; @@ -137,7 +137,7 @@ ); name = "RCTVideo-tvOS"; productName = "RCTVideo-tvOS"; - productReference = 641E28441F0EEC8500443AF6 /* libRCTVideo.a */; + productReference = 01450CB5271D5738005D8F6B /* libRCTVideo.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ @@ -151,9 +151,11 @@ TargetAttributes = { 58B511DA1A9E6C8500147676 = { CreatedOnToolsVersion = 6.1.1; + LastSwiftMigration = 1300; }; 641E28431F0EEC8500443AF6 = { CreatedOnToolsVersion = 8.3.3; + LastSwiftMigration = 1300; ProvisioningStyle = Automatic; }; }; @@ -163,6 +165,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 58B511D21A9E6C8500147676; @@ -181,10 +184,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D1107C0A2110259000073188 /* UIView+FindUIViewController.m in Sources */, - D1107C102110259000073188 /* RCTVideoPlayerViewController.m in Sources */, - D1107C0E2110259000073188 /* RCTVideoManager.m in Sources */, - D1107C0C2110259000073188 /* RCTVideo.m in Sources */, + 0177D39D27170A7A00F5BE18 /* RCTVideoPlayerViewControllerDelegate.swift in Sources */, + 0177D39C27170A7A00F5BE18 /* RCTVideoPlayerViewController.swift in Sources */, + 0177D39B27170A7A00F5BE18 /* UIView+FindUIViewController.swift in Sources */, + 0177D39F27170A7A00F5BE18 /* RCTVideo.swift in Sources */, + 0177D39E27170A7A00F5BE18 /* RCTVideoManager.m in Sources */, + 0177D39A27170A7A00F5BE18 /* RCTVideoManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -192,10 +197,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D1107C0B2110259000073188 /* UIView+FindUIViewController.m in Sources */, - D1107C112110259000073188 /* RCTVideoPlayerViewController.m in Sources */, - D1107C0F2110259000073188 /* RCTVideoManager.m in Sources */, - D1107C0D2110259000073188 /* RCTVideo.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -280,30 +281,39 @@ 58B511F01A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/Vendor/SPTPersistentCache/include/**", ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTVideo; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "Video/RCTVideo-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; 58B511F11A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/Vendor/SPTPersistentCache/include/**", ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTVideo; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "Video/RCTVideo-Bridging-Header.h"; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -312,15 +322,19 @@ buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; DEBUG_INFORMATION_FORMAT = dwarf; GCC_NO_COMMON_BLOCKS = YES; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTVideo; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 10.2; }; name = Debug; @@ -330,16 +344,19 @@ buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_NO_COMMON_BLOCKS = YES; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTVideo; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 10.2; }; name = Release; diff --git a/ios/Video/DataStructures/DRMParams.swift b/ios/Video/DataStructures/DRMParams.swift new file mode 100644 index 00000000..64add7e0 --- /dev/null +++ b/ios/Video/DataStructures/DRMParams.swift @@ -0,0 +1,30 @@ +struct DRMParams { + let type: String? + let licenseServer: String? + let headers: Dictionary? + let contentId: String? + let certificateUrl: String? + let base64Certificate: Bool? + + let json: NSDictionary? + + init(_ json: NSDictionary!) { + guard json != nil else { + self.json = nil + self.type = nil + self.licenseServer = nil + self.contentId = nil + self.certificateUrl = nil + self.base64Certificate = nil + self.headers = nil + return + } + self.json = json + self.type = json["type"] as? String + self.licenseServer = json["licenseServer"] as? String + self.contentId = json["contentId"] as? String + self.certificateUrl = json["certificateUrl"] as? String + self.base64Certificate = json["base64Certificate"] as? Bool + self.headers = json["headers"] as? Dictionary + } +} diff --git a/ios/Video/DataStructures/SelectedTrackCriteria.swift b/ios/Video/DataStructures/SelectedTrackCriteria.swift new file mode 100644 index 00000000..7d97b8f2 --- /dev/null +++ b/ios/Video/DataStructures/SelectedTrackCriteria.swift @@ -0,0 +1,18 @@ +struct SelectedTrackCriteria { + let type: String + let value: Any? + + let json: NSDictionary? + + init(_ json: NSDictionary!) { + guard json != nil else { + self.json = nil + self.type = "" + self.value = nil + return + } + self.json = json + self.type = json["type"] as? String ?? "" + self.value = json["value"] + } +} diff --git a/ios/Video/DataStructures/TextTrack.swift b/ios/Video/DataStructures/TextTrack.swift new file mode 100644 index 00000000..b0bdad80 --- /dev/null +++ b/ios/Video/DataStructures/TextTrack.swift @@ -0,0 +1,25 @@ + +struct TextTrack { + let type: String + let language: String + let title: String + let uri: String + + let json: NSDictionary? + + init(_ json: NSDictionary!) { + guard json != nil else { + self.json = nil + self.type = "" + self.language = "" + self.title = "" + self.uri = "" + return + } + self.json = json + self.type = json["type"] as? String ?? "" + self.language = json["language"] as? String ?? "" + self.title = json["title"] as? String ?? "" + self.uri = json["uri"] as? String ?? "" + } +} diff --git a/ios/Video/DataStructures/VideoSource.swift b/ios/Video/DataStructures/VideoSource.swift new file mode 100644 index 00000000..20ab7158 --- /dev/null +++ b/ios/Video/DataStructures/VideoSource.swift @@ -0,0 +1,31 @@ + +struct VideoSource { + let type: String? + let uri: String? + let isNetwork: Bool + let isAsset: Bool + let shouldCache: Bool + let requestHeaders: Dictionary? + + let json: NSDictionary? + + init(_ json: NSDictionary!) { + guard json != nil else { + self.json = nil + self.type = nil + self.uri = nil + self.isNetwork = false + self.isAsset = false + self.shouldCache = false + self.requestHeaders = nil + return + } + self.json = json + self.type = json["type"] as? String + self.uri = json["uri"] as? String + self.isNetwork = json["isNetwork"] as? Bool ?? false + self.isAsset = json["isAsset"] as? Bool ?? false + self.shouldCache = json["shouldCache"] as? Bool ?? false + self.requestHeaders = json["requestHeaders"] as? Dictionary + } +} diff --git a/ios/Video/Features/RCTPictureInPicture.swift b/ios/Video/Features/RCTPictureInPicture.swift new file mode 100644 index 00000000..4525df0b --- /dev/null +++ b/ios/Video/Features/RCTPictureInPicture.swift @@ -0,0 +1,75 @@ +import AVFoundation +import AVKit +import MediaAccessibility +import React +import Foundation + +#if TARGET_OS_IOS +class RCTPictureInPicture: NSObject, AVPictureInPictureControllerDelegate { + private var _onPictureInPictureStatusChanged: RCTDirectEventBlock? + private var _onRestoreUserInterfaceForPictureInPictureStop: RCTDirectEventBlock? + private var _restoreUserInterfaceForPIPStopCompletionHandler:((Bool) -> Void)? = nil + private var _pipController:AVPictureInPictureController? + private var _isActive:Bool = false + + init(_ onPictureInPictureStatusChanged: @escaping RCTDirectEventBlock, _ onRestoreUserInterfaceForPictureInPictureStop: @escaping RCTDirectEventBlock) { + _onPictureInPictureStatusChanged = onPictureInPictureStatusChanged + _onRestoreUserInterfaceForPictureInPictureStop = onRestoreUserInterfaceForPictureInPictureStop + } + + func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + guard let _onPictureInPictureStatusChanged = _onPictureInPictureStatusChanged else { return } + + _onPictureInPictureStatusChanged([ "isActive": NSNumber(value: true)]) + } + + func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + guard let _onPictureInPictureStatusChanged = _onPictureInPictureStatusChanged else { return } + + _onPictureInPictureStatusChanged([ "isActive": NSNumber(value: false)]) + } + + func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) { + + assert(_restoreUserInterfaceForPIPStopCompletionHandler == nil, "restoreUserInterfaceForPIPStopCompletionHandler was not called after picture in picture was exited.") + + guard let _onRestoreUserInterfaceForPictureInPictureStop = _onRestoreUserInterfaceForPictureInPictureStop else { return } + + _onRestoreUserInterfaceForPictureInPictureStop([:]) + + _restoreUserInterfaceForPIPStopCompletionHandler = completionHandler + } + + func setRestoreUserInterfaceForPIPStopCompletionHandler(_ restore:Bool) { + guard let _restoreUserInterfaceForPIPStopCompletionHandler = _restoreUserInterfaceForPIPStopCompletionHandler else { return } + _restoreUserInterfaceForPIPStopCompletionHandler(restore) + self._restoreUserInterfaceForPIPStopCompletionHandler = nil + } + + func setupPipController(_ playerLayer: AVPlayerLayer?) { + guard playerLayer != nil && AVPictureInPictureController.isPictureInPictureSupported() && _isActive else { return } + // Create new controller passing reference to the AVPlayerLayer + _pipController = AVPictureInPictureController(playerLayer:playerLayer!) + _pipController?.delegate = self + } + + func setPictureInPicture(_ isActive:Bool) { + if _isActive == isActive { + return + } + _isActive = isActive + + guard let _pipController = _pipController else { return } + + if _isActive && !_pipController.isPictureInPictureActive { + DispatchQueue.main.async(execute: { + _pipController.startPictureInPicture() + }) + } else if !_isActive && _pipController.isPictureInPictureActive { + DispatchQueue.main.async(execute: { + _pipController.stopPictureInPicture() + }) + } + } +} +#endif diff --git a/ios/Video/Features/RCTPlayerObserver.swift b/ios/Video/Features/RCTPlayerObserver.swift new file mode 100644 index 00000000..73f3754f --- /dev/null +++ b/ios/Video/Features/RCTPlayerObserver.swift @@ -0,0 +1,207 @@ +import AVFoundation +import AVKit +import Foundation + +@objc +protocol RCTPlayerObserverHandlerObjc { + func handleDidFailToFinishPlaying(notification:NSNotification!) + func handlePlaybackStalled(notification:NSNotification!) + func handlePlayerItemDidReachEnd(notification:NSNotification!) + // unused +// func handleAVPlayerAccess(notification:NSNotification!) +} + +protocol RCTPlayerObserverHandler: RCTPlayerObserverHandlerObjc { + func handleTimeUpdate(time:CMTime) + func handleReadyForDisplay(changeObject: Any, change:NSKeyValueObservedChange) + func handleTimeMetadataChange(playerItem:AVPlayerItem, change:NSKeyValueObservedChange<[AVMetadataItem]?>) + func handlePlayerItemStatusChange(playerItem:AVPlayerItem, change:NSKeyValueObservedChange) + func handlePlaybackBufferKeyEmpty(playerItem:AVPlayerItem, change:NSKeyValueObservedChange) + func handlePlaybackLikelyToKeepUp(playerItem:AVPlayerItem, change:NSKeyValueObservedChange) + func handlePlaybackRateChange(player: AVPlayer, change: NSKeyValueObservedChange) + func handleExternalPlaybackActiveChange(player: AVPlayer, change: NSKeyValueObservedChange) + func handleViewControllerOverlayViewFrameChange(overlayView:UIView, change:NSKeyValueObservedChange) +} + +class RCTPlayerObserver: NSObject { + + var _handlers: RCTPlayerObserverHandler! + + var player:AVPlayer? { + willSet { + removePlayerObservers() + removePlayerTimeObserver() + } + didSet { + if player != nil { + addPlayerObservers() + addPlayerTimeObserver() + } + } + } + var playerItem:AVPlayerItem? { + willSet { + removePlayerItemObservers() + } + didSet { + if playerItem != nil { + addPlayerItemObservers() + } + } + } + var playerViewController:AVPlayerViewController? { + willSet { + removePlayerViewControllerObservers() + } + didSet { + if playerViewController != nil { + addPlayerViewControllerObservers() + } + } + } + var playerLayer:AVPlayerLayer? { + willSet { + removePlayerLayerObserver() + } + didSet { + if playerLayer == nil { + addPlayerLayerObserver() + } + } + } + + private var _progressUpdateInterval:TimeInterval = 250 + private var _timeObserver:Any? + + private var _playerRateChangeObserver:NSKeyValueObservation? + private var _playerExpernalPlaybackActiveObserver:NSKeyValueObservation? + private var _playerItemStatusObserver:NSKeyValueObservation? + private var _playerPlaybackBufferEmptyObserver:NSKeyValueObservation? + private var _playerPlaybackLikelyToKeepUpObserver:NSKeyValueObservation? + private var _playerTimedMetadataObserver:NSKeyValueObservation? + private var _playerViewControllerReadyForDisplayObserver:NSKeyValueObservation? + private var _playerLayerReadyForDisplayObserver:NSKeyValueObservation? + private var _playerViewControllerOverlayFrameObserver:NSKeyValueObservation? + + deinit { + NotificationCenter.default.removeObserver(_handlers) + } + + func addPlayerObservers() { + guard let player = player else { + return + } + + _playerRateChangeObserver = player.observe(\.rate, changeHandler: _handlers.handlePlaybackRateChange) + _playerExpernalPlaybackActiveObserver = player.observe(\.isExternalPlaybackActive, changeHandler: _handlers.handleExternalPlaybackActiveChange) + } + + func removePlayerObservers() { + _playerRateChangeObserver?.invalidate() + _playerExpernalPlaybackActiveObserver?.invalidate() + } + + func addPlayerItemObservers() { + guard let playerItem = playerItem else { return } + + _playerItemStatusObserver = playerItem.observe(\.status, options: [.new, .old], changeHandler: _handlers.handlePlayerItemStatusChange) + _playerPlaybackBufferEmptyObserver = playerItem.observe(\.isPlaybackBufferEmpty, options: [.new, .old], changeHandler: _handlers.handlePlaybackBufferKeyEmpty) + _playerPlaybackLikelyToKeepUpObserver = playerItem.observe(\.isPlaybackLikelyToKeepUp, options: [.new, .old], changeHandler: _handlers.handlePlaybackLikelyToKeepUp) + _playerTimedMetadataObserver = playerItem.observe(\.timedMetadata, options: [.new], changeHandler: _handlers.handleTimeMetadataChange) + } + + func removePlayerItemObservers() { + _playerItemStatusObserver?.invalidate() + _playerPlaybackBufferEmptyObserver?.invalidate() + _playerPlaybackLikelyToKeepUpObserver?.invalidate() + _playerTimedMetadataObserver?.invalidate() + } + + func addPlayerViewControllerObservers() { + guard let playerViewController = playerViewController else { return } + + _playerViewControllerReadyForDisplayObserver = playerViewController.observe(\.isReadyForDisplay, options: [.new], changeHandler: _handlers.handleReadyForDisplay) + + _playerViewControllerOverlayFrameObserver = playerViewController.contentOverlayView?.observe(\.frame, options: [.new, .old], changeHandler: _handlers.handleViewControllerOverlayViewFrameChange) + } + + func removePlayerViewControllerObservers() { + _playerViewControllerReadyForDisplayObserver?.invalidate() + _playerViewControllerOverlayFrameObserver?.invalidate() + } + + func addPlayerLayerObserver() { + _playerLayerReadyForDisplayObserver = playerLayer?.observe(\.isReadyForDisplay, options: [.new], changeHandler: _handlers.handleReadyForDisplay) + } + + func removePlayerLayerObserver() { + _playerLayerReadyForDisplayObserver?.invalidate() + } + + func addPlayerTimeObserver() { + removePlayerTimeObserver() + let progressUpdateIntervalMS:Float64 = _progressUpdateInterval / 1000 + // @see endScrubbing in AVPlayerDemoPlaybackViewController.m + // of https://developer.apple.com/library/ios/samplecode/AVPlayerDemo/Introduction/Intro.html + _timeObserver = player?.addPeriodicTimeObserver( + forInterval: CMTimeMakeWithSeconds(progressUpdateIntervalMS, preferredTimescale: Int32(NSEC_PER_SEC)), + queue:nil, + using:_handlers.handleTimeUpdate + ) + } + + /* Cancels the previously registered time observer. */ + func removePlayerTimeObserver() { + if _timeObserver != nil { + player?.removeTimeObserver(_timeObserver) + _timeObserver = nil + } + } + + func addTimeObserverIfNotSet() { + if (_timeObserver == nil) { + addPlayerTimeObserver() + } + } + + func replaceTimeObserverIfSet(_ newUpdateInterval:Float64? = nil) { + if let newUpdateInterval = newUpdateInterval { + _progressUpdateInterval = newUpdateInterval + } + if (_timeObserver != nil) { + addPlayerTimeObserver() + } + } + + func attachPlayerEventListeners() { + + NotificationCenter.default.removeObserver(_handlers, + name:NSNotification.Name.AVPlayerItemDidPlayToEndTime, + object:player?.currentItem) + NotificationCenter.default.addObserver(_handlers, + selector:#selector(RCTPlayerObserverHandler.handlePlayerItemDidReachEnd(notification:)), + name:NSNotification.Name.AVPlayerItemDidPlayToEndTime, + object:player?.currentItem) + + NotificationCenter.default.removeObserver(_handlers, + name:NSNotification.Name.AVPlayerItemPlaybackStalled, + object:nil) + NotificationCenter.default.addObserver(_handlers, + selector:#selector(RCTPlayerObserverHandler.handlePlaybackStalled(notification:)), + name:NSNotification.Name.AVPlayerItemPlaybackStalled, + object:nil) + NotificationCenter.default.removeObserver(_handlers, + name: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime, + object:nil) + NotificationCenter.default.addObserver(_handlers, + selector:#selector(RCTPlayerObserverHandler.handleDidFailToFinishPlaying(notification:)), + name: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime, + object:nil) + } + + func clearPlayer() { + player = nil + playerItem = nil + NotificationCenter.default.removeObserver(_handlers) + } +} diff --git a/ios/Video/Features/RCTPlayerOperations.swift b/ios/Video/Features/RCTPlayerOperations.swift new file mode 100644 index 00000000..320e3223 --- /dev/null +++ b/ios/Video/Features/RCTPlayerOperations.swift @@ -0,0 +1,223 @@ +import AVFoundation +import MediaAccessibility +import Promises + +let RCTVideoUnset = -1 + +/*! + * Collection of mutating functions + */ +enum RCTPlayerOperations { + + static func setSideloadedText(player:AVPlayer?, textTracks:[TextTrack]?, criteria:SelectedTrackCriteria?) { + let type = criteria?.type + let textTracks:[TextTrack]! = textTracks ?? RCTVideoUtils.getTextTrackInfo(player) + + // The first few tracks will be audio & video track + let firstTextIndex:Int = 0 + for firstTextIndex in 0..<(player?.currentItem?.tracks.count ?? 0) { + if player?.currentItem?.tracks[firstTextIndex].assetTrack?.hasMediaCharacteristic(.legible) ?? false { + break + } + } + + var selectedTrackIndex:Int = RCTVideoUnset + + if (type == "disabled") { + // Do nothing. We want to ensure option is nil + } else if (type == "language") { + let selectedValue = criteria?.value as? String + for i in 0.. index { + selectedTrackIndex = index + } + } + } + + // in the situation that a selected text track is not available (eg. specifies a textTrack not available) + if (type != "disabled") && selectedTrackIndex == RCTVideoUnset { + let captioningMediaCharacteristics = MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics(.user) as! CFArray + let captionSettings = captioningMediaCharacteristics as? [AnyHashable] + if ((captionSettings?.contains(AVMediaCharacteristic.transcribesSpokenDialogForAccessibility)) != nil) { + selectedTrackIndex = 0 // If we can't find a match, use the first available track + let systemLanguage = NSLocale.preferredLanguages.first + for i in 0.. index { + mediaOption = group.options[index] + } + } + } else { // default. invalid type or "system" + #if TARGET_OS_TV + // Do noting. Fix for tvOS native audio menu language selector + #else + player?.currentItem?.selectMediaOptionAutomatically(in: group) + return + #endif + } + + #if TARGET_OS_TV + // Do noting. Fix for tvOS native audio menu language selector + #else + // If a match isn't found, option will be nil and text tracks will be disabled + player?.currentItem?.select(mediaOption, in:group) + #endif + } + + static func setMediaSelectionTrackForCharacteristic(player:AVPlayer?, characteristic:AVMediaCharacteristic, criteria:SelectedTrackCriteria?) { + let type = criteria?.type + let group:AVMediaSelectionGroup! = player?.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: characteristic) + var mediaOption:AVMediaSelectionOption! + + if (type == "disabled") { + // Do nothing. We want to ensure option is nil + } else if (type == "language") || (type == "title") { + let value = criteria?.value as? String + for i in 0.. index { + mediaOption = group.options[index] + } + } + } else if let group = group { // default. invalid type or "system" + player?.currentItem?.selectMediaOptionAutomatically(in: group) + return + } + + if let group = group { + // If a match isn't found, option will be nil and text tracks will be disabled + player?.currentItem?.select(mediaOption, in:group) + } + + } + + static func seek(player: AVPlayer, playerItem:AVPlayerItem, paused:Bool, seekTime:Float, seekTolerance:Float) -> Promise { + let timeScale:Int = 1000 + let cmSeekTime:CMTime = CMTimeMakeWithSeconds(Float64(seekTime), preferredTimescale: Int32(timeScale)) + let current:CMTime = playerItem.currentTime() + let tolerance:CMTime = CMTimeMake(value: Int64(seekTolerance), timescale: Int32(timeScale)) + + return Promise(on: .global()) { fulfill, reject in + guard CMTimeCompare(current, cmSeekTime) != 0 else { + reject(NSError()) + return + } + if !paused { player.pause() } + + player.seek(to: cmSeekTime, toleranceBefore:tolerance, toleranceAfter:tolerance, completionHandler:{ (finished:Bool) in + fulfill(finished) + }) + } + } + + static func configureAudio(ignoreSilentSwitch:String, mixWithOthers:String) { + let session:AVAudioSession! = AVAudioSession.sharedInstance() + var category:AVAudioSession.Category? = nil + var options:AVAudioSession.CategoryOptions? = nil + + if (ignoreSilentSwitch == "ignore") { + category = AVAudioSession.Category.playback + } else if (ignoreSilentSwitch == "obey") { + category = AVAudioSession.Category.ambient + } + + if (mixWithOthers == "mix") { + options = .mixWithOthers + } else if (mixWithOthers == "duck") { + options = .duckOthers + } + + if let category = category, let options = options { + do { + try session.setCategory(category, options: options) + } catch { + } + } else if let category = category, options == nil { + do { + try session.setCategory(category) + } catch { + } + } else if category == nil, let options = options { + do { + try session.setCategory(session.category, options: options) + } catch { + } + } + } +} diff --git a/ios/Video/Features/RCTResourceLoaderDelegate.swift b/ios/Video/Features/RCTResourceLoaderDelegate.swift new file mode 100644 index 00000000..e1f11528 --- /dev/null +++ b/ios/Video/Features/RCTResourceLoaderDelegate.swift @@ -0,0 +1,167 @@ +import AVFoundation +import Promises + +class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSessionDelegate { + + private var _loadingRequest:AVAssetResourceLoadingRequest? + private var _requestingCertificate:Bool = false + private var _requestingCertificateErrored:Bool = false + private var _drm: DRMParams? + private var _localSourceEncryptionKeyScheme: String? + private var _reactTag: NSNumber? + private var _onVideoError: RCTDirectEventBlock? + private var _onGetLicense: RCTDirectEventBlock? + + + init( + asset: AVURLAsset, + drm: DRMParams?, + localSourceEncryptionKeyScheme: String?, + onVideoError: RCTDirectEventBlock?, + onGetLicense: RCTDirectEventBlock?, + reactTag: NSNumber + ) { + super.init() + let queue = DispatchQueue(label: "assetQueue") + asset.resourceLoader.setDelegate(self, queue: queue) + _reactTag = reactTag + _onVideoError = onVideoError + _onGetLicense = onGetLicense + _drm = drm + _localSourceEncryptionKeyScheme = localSourceEncryptionKeyScheme + } + + deinit { + _loadingRequest?.finishLoading() + } + + func resourceLoader(_ resourceLoader:AVAssetResourceLoader, shouldWaitForRenewalOfRequestedResource renewalRequest:AVAssetResourceRenewalRequest) -> Bool { + return loadingRequestHandling(renewalRequest) + } + + func resourceLoader(_ resourceLoader:AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest:AVAssetResourceLoadingRequest) -> Bool { + return loadingRequestHandling(loadingRequest) + } + + func resourceLoader(_ resourceLoader:AVAssetResourceLoader, didCancel loadingRequest:AVAssetResourceLoadingRequest) { + NSLog("didCancelLoadingRequest") + } + + func setLicenseResult(_ license:String!) { + guard let respondData = RCTVideoUtils.base64DataFromBase64String(base64String: license), + let _loadingRequest = _loadingRequest else { + setLicenseResultError("No data from JS license response") + return + } + let dataRequest:AVAssetResourceLoadingDataRequest! = _loadingRequest.dataRequest + dataRequest.respond(with: respondData) + _loadingRequest.finishLoading() + } + + func setLicenseResultError(_ error:String!) { + if _loadingRequest != nil { + self.finishLoadingWithError(error: RCTVideoErrorHandler.fromJSPart(error)) + } + } + + func finishLoadingWithError(error:Error!) -> Bool { + if let _loadingRequest = _loadingRequest, let error = error { + _loadingRequest.finishLoading(with: error as! NSError) + + _onVideoError?([ + "error": [ + "code": NSNumber(value: (error as NSError).code), + "localizedDescription": error.localizedDescription == nil ? "" : error.localizedDescription, + "localizedFailureReason": ((error as NSError).localizedFailureReason == nil ? "" : (error as NSError).localizedFailureReason) ?? "", + "localizedRecoverySuggestion": ((error as NSError).localizedRecoverySuggestion == nil ? "" : (error as NSError).localizedRecoverySuggestion) ?? "", + "domain": (error as NSError).domain + ], + "target": _reactTag + ]) + + } + return false + } + + func loadingRequestHandling(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool { + if handleEmbeddedKey(loadingRequest) { + return true + } + + if _drm != nil { + return handleDrm(loadingRequest) + } + + return false + } + + func handleEmbeddedKey(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool { + guard let url = loadingRequest.request.url, + let _localSourceEncryptionKeyScheme = _localSourceEncryptionKeyScheme, + let persistentKeyData = RCTVideoUtils.extractDataFromCustomSchemeUrl(from: url, scheme: _localSourceEncryptionKeyScheme) + else { + return false + } + + loadingRequest.contentInformationRequest?.contentType = AVStreamingKeyDeliveryPersistentContentKeyType + loadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true + loadingRequest.contentInformationRequest?.contentLength = Int64(persistentKeyData.count) + loadingRequest.dataRequest?.respond(with: persistentKeyData) + loadingRequest.finishLoading() + + return true + } + + func handleDrm(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool { + if _requestingCertificate { + return true + } else if _requestingCertificateErrored { + return false + } + _loadingRequest = loadingRequest + + guard let _drm = _drm, let drmType = _drm.type, drmType == "fairplay" else { + return finishLoadingWithError(error: RCTVideoErrorHandler.noDRMData) + } + + var promise: Promise + if _onGetLicense != nil { + let contentId = _drm.contentId ?? loadingRequest.request.url?.host + promise = RCTVideoDRM.handleWithOnGetLicense( + loadingRequest:loadingRequest, + contentId:contentId, + certificateUrl:_drm.certificateUrl, + base64Certificate:_drm.base64Certificate + ) .then{ spcData -> Void in + self._requestingCertificate = true + self._onGetLicense?(["licenseUrl": self._drm?.licenseServer ?? "", + "contentId": contentId, + "spcBase64": spcData.base64EncodedString(options: []), + "target": self._reactTag]) + } + } else { + promise = RCTVideoDRM.handleInternalGetLicense( + loadingRequest:loadingRequest, + contentId:_drm.contentId, + licenseServer:_drm.licenseServer, + certificateUrl:_drm.certificateUrl, + base64Certificate:_drm.base64Certificate, + headers:_drm.headers + ) .then{ data -> Void in + guard let dataRequest = loadingRequest.dataRequest else { + throw RCTVideoErrorHandler.noCertificateData + } + dataRequest.respond(with:data) + loadingRequest.finishLoading() + } + } + + + promise.catch{ error in + self.finishLoadingWithError(error:error) + self._requestingCertificateErrored = true + } + + return true + } +} diff --git a/ios/Video/Features/RCTVideoDRM.swift b/ios/Video/Features/RCTVideoDRM.swift new file mode 100644 index 00000000..d059bbc4 --- /dev/null +++ b/ios/Video/Features/RCTVideoDRM.swift @@ -0,0 +1,168 @@ +import AVFoundation +import Promises + +struct RCTVideoDRM { + @available(*, unavailable) private init() {} + + static func fetchLicense( + licenseServer: String, + spcData: Data?, + contentId: String, + headers: [String:Any]? + ) -> Promise { + let request = createLicenseRequest(licenseServer:licenseServer, spcData:spcData, contentId:contentId, headers:headers) + + return Promise(on: .global()) { fulfill, reject in + let postDataTask = URLSession.shared.dataTask(with: request as URLRequest, completionHandler:{ (data:Data!,response:URLResponse!,error:Error!) in + + let httpResponse:HTTPURLResponse! = (response as! HTTPURLResponse) + + guard error == nil else { + print("Error getting license from \(licenseServer), HTTP status code \(httpResponse.statusCode)") + reject(error) + return + } + guard httpResponse.statusCode == 200 else { + print("Error getting license from \(licenseServer), HTTP status code \(httpResponse.statusCode)") + reject(RCTVideoErrorHandler.licenseRequestNotOk(httpResponse.statusCode)) + return + } + + guard data != nil, let decodedData = Data(base64Encoded: data, options: []) else { + reject(RCTVideoErrorHandler.noDataFromLicenseRequest) + return + } + + fulfill(decodedData) + }) + postDataTask.resume() + } + } + + static func createLicenseRequest( + licenseServer: String, + spcData: Data?, + contentId: String, + headers: [String:Any]? + ) -> URLRequest { + var request = URLRequest(url: URL(string: licenseServer)!) + request.httpMethod = "POST" + + if let headers = headers { + for item in headers { + guard let key = item.key as? String, let value = item.value as? String else { + continue + } + request.setValue(value, forHTTPHeaderField: key) + } + } + + let spcEncoded = spcData?.base64EncodedString(options: []) + let spcUrlEncoded = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, spcEncoded as? CFString? as! CFString, nil, "?=&+" as CFString, CFStringBuiltInEncodings.UTF8.rawValue) as? String + let post = String(format:"spc=%@&%@", spcUrlEncoded as! CVarArg, contentId) + let postData = post.data(using: String.Encoding.utf8, allowLossyConversion:true) + request.httpBody = postData + + return request + } + + static func fetchSpcData( + loadingRequest: AVAssetResourceLoadingRequest, + certificateData: Data, + contentIdData: Data + ) -> Promise { + return Promise(on: .global()) { fulfill, reject in + var spcError:NSError! + var spcData: Data? + do { + spcData = try loadingRequest.streamingContentKeyRequestData(forApp: certificateData, contentIdentifier: contentIdData as Data, options: nil) + } catch _ { + print("SPC error") + } + + if spcError != nil { + reject(spcError) + } + + guard let spcData = spcData else { + reject(RCTVideoErrorHandler.noSPC) + return + } + + fulfill(spcData) + } + } + + static func createCertificateData(certificateStringUrl:String?, base64Certificate:Bool?) -> Promise { + return Promise(on: .global()) { fulfill, reject in + + guard let certificateStringUrl = certificateStringUrl, + let certificateURL = URL(string: certificateStringUrl.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) ?? "") else { + reject(RCTVideoErrorHandler.noCertificateURL) + return + } + + var certificateData:Data? + do { + certificateData = try Data(contentsOf: certificateURL) + if (base64Certificate != nil) { + certificateData = Data(base64Encoded: certificateData! as Data, options: .ignoreUnknownCharacters) + } + } catch {} + + guard let certificateData = certificateData else { + reject(RCTVideoErrorHandler.noCertificateData) + return + } + + fulfill(certificateData) + } + } + + static func handleWithOnGetLicense(loadingRequest: AVAssetResourceLoadingRequest, contentId:String?, certificateUrl:String?, base64Certificate:Bool?) -> Promise { + let contentIdData = contentId?.data(using: .utf8) + + return RCTVideoDRM.createCertificateData(certificateStringUrl:certificateUrl, base64Certificate:base64Certificate) + .then{ certificateData -> Promise in + guard let contentIdData = contentIdData else { + throw RCTVideoError.invalidContentId as! Error + } + + return RCTVideoDRM.fetchSpcData( + loadingRequest:loadingRequest, + certificateData:certificateData, + contentIdData:contentIdData + ) + } + } + + static func handleInternalGetLicense(loadingRequest: AVAssetResourceLoadingRequest, contentId:String?, licenseServer:String?, certificateUrl:String?, base64Certificate:Bool?, headers: [String:Any]?) -> Promise { + let url = loadingRequest.request.url + + guard let contentId = contentId ?? url?.absoluteString.replacingOccurrences(of: "skd://", with:"") else { + return Promise(RCTVideoError.invalidContentId as! Error) + } + + let contentIdData = NSData(bytes: contentId.cString(using: String.Encoding.utf8), length:contentId.lengthOfBytes(using: String.Encoding.utf8)) as Data + + return RCTVideoDRM.createCertificateData(certificateStringUrl:certificateUrl, base64Certificate:base64Certificate) + .then{ certificateData in + return RCTVideoDRM.fetchSpcData( + loadingRequest:loadingRequest, + certificateData:certificateData, + contentIdData:contentIdData + ) + } + .then{ spcData -> Promise in + guard let licenseServer = licenseServer else { + throw RCTVideoError.noLicenseServerURL as! Error + } + return RCTVideoDRM.fetchLicense( + licenseServer: licenseServer, + spcData: spcData, + contentId: contentId, + headers: headers + ) + } + } +} diff --git a/ios/Video/Features/RCTVideoErrorHandling.swift b/ios/Video/Features/RCTVideoErrorHandling.swift new file mode 100644 index 00000000..e795aa28 --- /dev/null +++ b/ios/Video/Features/RCTVideoErrorHandling.swift @@ -0,0 +1,103 @@ +enum RCTVideoError : Int { + case fromJSPart + case noLicenseServerURL + case licenseRequestNotOk + case noDataFromLicenseRequest + case noSPC + case noDataRequest + case noCertificateData + case noCertificateURL + case noFairplayDRM + case noDRMData + case invalidContentId +} + +enum RCTVideoErrorHandler { + + static let noDRMData = NSError( + domain: "RCTVideo", + code: RCTVideoError.noDRMData.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining DRM license.", + NSLocalizedFailureReasonErrorKey: "No drm object found.", + NSLocalizedRecoverySuggestionErrorKey: "Have you specified the 'drm' prop?" + ]) + + static let noCertificateURL = NSError( + domain: "RCTVideo", + code: RCTVideoError.noCertificateURL.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining DRM License.", + NSLocalizedFailureReasonErrorKey: "No certificate URL has been found.", + NSLocalizedRecoverySuggestionErrorKey: "Did you specified the prop certificateUrl?" + ]) + + static let noCertificateData = NSError( + domain: "RCTVideo", + code: RCTVideoError.noCertificateData.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining DRM license.", + NSLocalizedFailureReasonErrorKey: "No certificate data obtained from the specificied url.", + NSLocalizedRecoverySuggestionErrorKey: "Have you specified a valid 'certificateUrl'?" + ]) + + static let noSPC = NSError( + domain: "RCTVideo", + code: RCTVideoError.noSPC.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining license.", + NSLocalizedFailureReasonErrorKey: "No spc received.", + NSLocalizedRecoverySuggestionErrorKey: "Check your DRM config." + ]) + + static let noLicenseServerURL = NSError( + domain: "RCTVideo", + code: RCTVideoError.noLicenseServerURL.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining DRM License.", + NSLocalizedFailureReasonErrorKey: "No license server URL has been found.", + NSLocalizedRecoverySuggestionErrorKey: "Did you specified the prop licenseServer?" + ]) + + static let noDataFromLicenseRequest = NSError( + domain: "RCTVideo", + code: RCTVideoError.noDataFromLicenseRequest.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining DRM license.", + NSLocalizedFailureReasonErrorKey: "No data received from the license server.", + NSLocalizedRecoverySuggestionErrorKey: "Is the licenseServer ok?" + ]) + + static func licenseRequestNotOk(_ statusCode: Int) -> NSError { + return NSError( + domain: "RCTVideo", + code: RCTVideoError.licenseRequestNotOk.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining license.", + NSLocalizedFailureReasonErrorKey: String( + format:"License server responded with status code %li", + (statusCode) + ), + NSLocalizedRecoverySuggestionErrorKey: "Did you send the correct data to the license Server? Is the server ok?" + ]) + } + + static func fromJSPart(_ error: String) -> NSError { + return NSError(domain: "RCTVideo", + code: RCTVideoError.fromJSPart.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: error, + NSLocalizedFailureReasonErrorKey: error, + NSLocalizedRecoverySuggestionErrorKey: error + ]) + } + + static let invalidContentId = NSError( + domain: "RCTVideo", + code: RCTVideoError.invalidContentId.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Error obtaining DRM license.", + NSLocalizedFailureReasonErrorKey: "No valide content Id received", + NSLocalizedRecoverySuggestionErrorKey: "Is the contentId and url ok?" + ]) +} diff --git a/ios/Video/Features/RCTVideoSave.swift b/ios/Video/Features/RCTVideoSave.swift new file mode 100644 index 00000000..ff8155ec --- /dev/null +++ b/ios/Video/Features/RCTVideoSave.swift @@ -0,0 +1,75 @@ +import AVFoundation + +enum RCTVideoSave { + + static func save( + options:NSDictionary!, + resolve: @escaping RCTPromiseResolveBlock, + reject:@escaping RCTPromiseRejectBlock, + + playerItem: AVPlayerItem? + ) { + let asset:AVAsset! = playerItem?.asset + + guard asset != nil else { + reject("ERROR_ASSET_NIL", "Asset is nil", nil) + return + } + + guard let exportSession = AVAssetExportSession(asset: asset, presetName:AVAssetExportPresetHighestQuality) else { + reject("ERROR_COULD_NOT_CREATE_EXPORT_SESSION", "Could not create export session", nil) + return + } + var path:String! = nil + path = RCTVideoSave.generatePathInDirectory( + directory: URL(fileURLWithPath: RCTVideoSave.cacheDirectoryPath() ?? "").appendingPathComponent("Videos").path, + withExtension: ".mp4") + let url:NSURL! = NSURL.fileURL(withPath: path) as NSURL + exportSession.outputFileType = AVFileType.mp4 + exportSession.outputURL = url as URL? + exportSession.videoComposition = playerItem?.videoComposition + exportSession.shouldOptimizeForNetworkUse = true + exportSession.exportAsynchronously(completionHandler: { + + switch (exportSession.status) { + case .failed: + reject("ERROR_COULD_NOT_EXPORT_VIDEO", "Could not export video", exportSession.error) + break + case .cancelled: + reject("ERROR_EXPORT_SESSION_CANCELLED", "Export session was cancelled", exportSession.error) + break + default: + resolve(["uri": url.absoluteString]) + break + } + + }) + } + + static func generatePathInDirectory(directory: String?, withExtension `extension`: String?) -> String? { + let fileName = UUID().uuidString + (`extension` ?? "") + RCTVideoSave.ensureDirExists(withPath: directory) + return URL(fileURLWithPath: directory ?? "").appendingPathComponent(fileName).path + } + + static func cacheDirectoryPath() -> String? { + let array = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).map(\.path) + return array[0] + } + + static func ensureDirExists(withPath path: String?) -> Bool { + var isDir: ObjCBool = false + var error: Error? + let exists = FileManager.default.fileExists(atPath: path ?? "", isDirectory: &isDir) + if !(exists && isDir.boolValue) { + do { + try FileManager.default.createDirectory(atPath: path ?? "", withIntermediateDirectories: true, attributes: nil) + } catch { + } + if error != nil { + return false + } + } + return true + } +} diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift new file mode 100644 index 00000000..69c127af --- /dev/null +++ b/ios/Video/Features/RCTVideoUtils.swift @@ -0,0 +1,250 @@ +import AVFoundation +import Promises + +/*! + * Collection of pure functions + */ +enum RCTVideoUtils { + + /*! + * Calculates and returns the playable duration of the current player item using its loaded time ranges. + * + * \returns The playable duration of the current player item in seconds. + */ + static func calculatePlayableDuration(_ player:AVPlayer?) -> NSNumber { + guard let player = player, + let video:AVPlayerItem = player.currentItem, + video.status == AVPlayerItem.Status.readyToPlay else { + return 0 + } + + var effectiveTimeRange:CMTimeRange? + for (_, value) in video.loadedTimeRanges.enumerated() { + let timeRange:CMTimeRange = value.timeRangeValue + if CMTimeRangeContainsTime(timeRange, time: video.currentTime()) { + effectiveTimeRange = timeRange + break + } + } + + if let effectiveTimeRange = effectiveTimeRange { + let playableDuration:Float64 = CMTimeGetSeconds(CMTimeRangeGetEnd(effectiveTimeRange)) + if playableDuration > 0 { + return playableDuration as NSNumber + } + } + + return 0 + } + + static func urlFilePath(filepath:NSString!) -> NSURL! { + if filepath.contains("file://") { + return NSURL(string: filepath as String) + } + + // if no file found, check if the file exists in the Document directory + let paths:[String]! = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) + var relativeFilePath:String! = filepath.lastPathComponent + // the file may be multiple levels below the documents directory + let fileComponents:[String]! = filepath.components(separatedBy: "Documents/") + if fileComponents.count > 1 { + relativeFilePath = fileComponents[1] + } + + let path:String! = (paths.first! as NSString).appendingPathComponent(relativeFilePath) + if FileManager.default.fileExists(atPath: path) { + return NSURL.fileURL(withPath: path) as NSURL + } + return nil + } + + static func playerItemSeekableTimeRange(_ player:AVPlayer?) -> CMTimeRange { + if let playerItem = player?.currentItem, + playerItem.status == .readyToPlay, + let firstItem = playerItem.seekableTimeRanges.first { + return firstItem.timeRangeValue + } + + return (CMTimeRange.zero) + } + + static func playerItemDuration(_ player:AVPlayer?) -> CMTime { + if let playerItem = player?.currentItem, + playerItem.status == .readyToPlay { + return(playerItem.duration) + } + + return(CMTime.invalid) + } + + static func calculateSeekableDuration(_ player:AVPlayer?) -> NSNumber { + let timeRange:CMTimeRange = RCTVideoUtils.playerItemSeekableTimeRange(player) + if CMTIME_IS_NUMERIC(timeRange.duration) + { + return NSNumber(value: CMTimeGetSeconds(timeRange.duration)) + } + return 0 + } + + static func getAudioTrackInfo(_ player:AVPlayer?) -> [AnyObject]! { + guard let player = player else { + return [] + } + + let audioTracks:NSMutableArray! = NSMutableArray() + let group = player.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: .audible) + for i in 0..<(group?.options.count ?? 0) { + let currentOption = group?.options[i] + var title = "" + let values = currentOption?.commonMetadata.map(\.value) + if (values?.count ?? 0) > 0, let value = values?[0] { + title = value as! String + } + let language:String! = currentOption?.extendedLanguageTag ?? "" + let audioTrack = [ + "index": NSNumber(value: i), + "title": title, + "language": language + ] as [String : Any] + audioTracks.add(audioTrack) + } + return audioTracks as [AnyObject]? + } + + static func getTextTrackInfo(_ player:AVPlayer?) -> [TextTrack]! { + guard let player = player else { + return [] + } + + // if streaming video, we extract the text tracks + var textTracks:[TextTrack] = [] + let group = player.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: .legible) + for i in 0..<(group?.options.count ?? 0) { + let currentOption = group?.options[i] + var title = "" + let values = currentOption?.commonMetadata.map(\.value) + if (values?.count ?? 0) > 0, let value = values?[0] { + title = value as! String + } + let language:String! = currentOption?.extendedLanguageTag ?? "" + let textTrack = TextTrack([ + "index": NSNumber(value: i), + "title": title, + "language": language + ]) + textTracks.append(textTrack) + } + return textTracks + } + + // UNUSED + static func getCurrentTime(playerItem:AVPlayerItem?) -> Float { + return Float(CMTimeGetSeconds(playerItem?.currentTime() ?? .zero)) + } + + static func base64DataFromBase64String(base64String:String?) -> Data? { + if let base64String = base64String { + return Data(base64Encoded:base64String) + } + return nil + } + + static func replaceURLScheme(url: URL, scheme: String?) -> URL? { + var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) + urlComponents?.scheme = scheme + + return urlComponents?.url + } + + static func extractDataFromCustomSchemeUrl(from url: URL, scheme: String) -> Data? { + guard url.scheme == scheme, + let adoptURL = RCTVideoUtils.replaceURLScheme(url:url, scheme: nil) else { return nil } + + return Data(base64Encoded: adoptURL.absoluteString) + } + + static func generateMixComposition(_ asset:AVAsset) -> AVMutableComposition { + let mixComposition:AVMutableComposition = AVMutableComposition() + + let videoAsset:AVAssetTrack! = asset.tracks(withMediaType: AVMediaType.video).first + let videoCompTrack:AVMutableCompositionTrack! = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID:kCMPersistentTrackID_Invalid) + do { + try videoCompTrack.insertTimeRange( + CMTimeRangeMake(start: .zero, duration: videoAsset.timeRange.duration), + of: videoAsset, + at: .zero) + } catch { + } + + let audioAsset:AVAssetTrack! = asset.tracks(withMediaType: AVMediaType.audio).first + let audioCompTrack:AVMutableCompositionTrack! = mixComposition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID:kCMPersistentTrackID_Invalid) + do { + try audioCompTrack.insertTimeRange( + CMTimeRangeMake(start: .zero, duration: videoAsset.timeRange.duration), + of: audioAsset, + at: .zero) + } catch { + } + + return mixComposition + } + + static func getValidTextTracks(asset:AVAsset, assetOptions:NSDictionary?, mixComposition:AVMutableComposition, textTracks:[TextTrack]?) -> [TextTrack] { + let videoAsset:AVAssetTrack! = asset.tracks(withMediaType: AVMediaType.video).first + var validTextTracks:[TextTrack] = [] + if let textTracks = textTracks, textTracks.count > 0 { + for i in 0.. Promise { + return Promise(on: .global()) { fulfill, reject in + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(seconds)) / Double(NSEC_PER_SEC), execute: { + fulfill(()) + }) + } + } + + static func prepareAsset(source:VideoSource) -> (asset:AVURLAsset?, assetOptions:NSMutableDictionary?)? { + guard source.uri != nil && source.uri != "" else { return nil } + var asset:AVURLAsset! + let bundlePath = Bundle.main.path(forResource: source.uri, ofType: source.type) ?? "" + let url = source.isNetwork || source.isAsset + ? URL(string: source.uri ?? "") + : URL(fileURLWithPath: bundlePath) + let assetOptions:NSMutableDictionary! = NSMutableDictionary() + + if source.isNetwork { + if let headers = source.requestHeaders, headers.count > 0 { + assetOptions.setObject(headers, forKey:"AVURLAssetHTTPHeaderFieldsKey" as NSCopying) + } + let cookies:[AnyObject]! = HTTPCookieStorage.shared.cookies + assetOptions.setObject(cookies, forKey:AVURLAssetHTTPCookiesKey as NSCopying) + asset = AVURLAsset(url: url!, options:assetOptions as! [String : Any]) + } else { + asset = AVURLAsset(url: url!) + } + return (asset, assetOptions) + } +} diff --git a/ios/Video/RCTSwiftLog/RCTSwiftLog.h b/ios/Video/RCTSwiftLog/RCTSwiftLog.h new file mode 100644 index 00000000..8f9deab7 --- /dev/null +++ b/ios/Video/RCTSwiftLog/RCTSwiftLog.h @@ -0,0 +1,11 @@ +#import + +@interface RCTSwiftLog : NSObject + ++ (void)error:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; ++ (void)warn:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; ++ (void)info:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; ++ (void)log:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; ++ (void)trace:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; + +@end diff --git a/ios/Video/RCTSwiftLog/RCTSwiftLog.m b/ios/Video/RCTSwiftLog/RCTSwiftLog.m new file mode 100644 index 00000000..4e153846 --- /dev/null +++ b/ios/Video/RCTSwiftLog/RCTSwiftLog.m @@ -0,0 +1,32 @@ +#import + +#import "RCTSwiftLog.h" + +@implementation RCTSwiftLog + ++ (void)info:(NSString *)message file:(NSString *)file line:(NSUInteger)line +{ + _RCTLogNativeInternal(RCTLogLevelInfo, file.UTF8String, (int)line, @"%@", message); +} + ++ (void)warn:(NSString *)message file:(NSString *)file line:(NSUInteger)line +{ + _RCTLogNativeInternal(RCTLogLevelWarning, file.UTF8String, (int)line, @"%@", message); +} + ++ (void)error:(NSString *)message file:(NSString *)file line:(NSUInteger)line +{ + _RCTLogNativeInternal(RCTLogLevelError, file.UTF8String, (int)line, @"%@", message); +} + ++ (void)log:(NSString *)message file:(NSString *)file line:(NSUInteger)line +{ + _RCTLogNativeInternal(RCTLogLevelInfo, file.UTF8String, (int)line, @"%@", message); +} + ++ (void)trace:(NSString *)message file:(NSString *)file line:(NSUInteger)line +{ + _RCTLogNativeInternal(RCTLogLevelTrace, file.UTF8String, (int)line, @"%@", message); +} + +@end diff --git a/ios/Video/RCTSwiftLog/RCTSwiftLog.swift b/ios/Video/RCTSwiftLog/RCTSwiftLog.swift new file mode 100644 index 00000000..ec39d9b1 --- /dev/null +++ b/ios/Video/RCTSwiftLog/RCTSwiftLog.swift @@ -0,0 +1,53 @@ +// +// RCTLog.swift +// WebViewExample +// +// Created by Jimmy Dee on 4/5/17. +// Copyright © 2017 Branch Metrics. All rights reserved. +// + +/* + * Under at least some conditions, output from NSLog has been unavailable in the RNBranch module. + * Hence that module uses the RCTLog macros from . The React logger is nicer than + * NSLog anyway, since it provides log levels with runtime filtering, file and line context and + * an identifier for the thread that logged the message. + * + * This wrapper lets you use functions with the same name in Swift. For example: + * + * RCTLogInfo("application launched") + * + * generates + * + * 2017-04-06 12:31:09.611 [info][tid:main][AppDelegate.swift:18] application launched + * + * This is currently part of this sample app. There may be some issues integrating it into an + * Objective-C library, either react-native-branch or react-native itself, but it may find its + * way into one or the other eventually. Feel free to reuse it as desired. + */ + +func RCTLogError(_ message: String, _ file: String=#file, _ line: UInt=#line) { + RCTSwiftLog.error(message, file: file, line: line) +} + +func RCTLogWarn(_ message: String, _ file: String=#file, _ line: UInt=#line) { + RCTSwiftLog.warn(message, file: file, line: line) +} + +func RCTLogInfo(_ message: String, _ file: String=#file, _ line: UInt=#line) { + RCTSwiftLog.info(message, file: file, line: line) +} + +func RCTLog(_ message: String, _ file: String=#file, _ line: UInt=#line) { + RCTSwiftLog.log(message, file: file, line: line) +} + +func RCTLogTrace(_ message: String, _ file: String=#file, _ line: UInt=#line) { + RCTSwiftLog.trace(message, file: file, line: line) +} + +func DebugLog(_ message: String) { +#if DEBUG + print(message) +#endif +} + diff --git a/ios/Video/RCTVideo-Bridging-Header.h b/ios/Video/RCTVideo-Bridging-Header.h new file mode 100644 index 00000000..ad6089be --- /dev/null +++ b/ios/Video/RCTVideo-Bridging-Header.h @@ -0,0 +1,7 @@ +#import +#import "RCTSwiftLog.h" + +#if __has_include() +#import "RCTVideoCache.h" +#endif + diff --git a/ios/Video/RCTVideo.h b/ios/Video/RCTVideo.h deleted file mode 100644 index 6fee2996..00000000 --- a/ios/Video/RCTVideo.h +++ /dev/null @@ -1,67 +0,0 @@ -#import -#import "AVKit/AVKit.h" -#import "UIView+FindUIViewController.h" -#import "RCTVideoPlayerViewController.h" -#import "RCTVideoPlayerViewControllerDelegate.h" -#import -#import - -#if __has_include() -#import -#import -#import -#endif - -@class RCTEventDispatcher; -#if __has_include() -@interface RCTVideo : UIView -#elif TARGET_OS_TV -@interface RCTVideo : UIView -#else -@interface RCTVideo : UIView -#endif - -@property (nonatomic, copy) RCTDirectEventBlock onVideoLoadStart; -@property (nonatomic, copy) RCTDirectEventBlock onVideoLoad; -@property (nonatomic, copy) RCTDirectEventBlock onVideoBuffer; -@property (nonatomic, copy) RCTDirectEventBlock onVideoError; -@property (nonatomic, copy) RCTDirectEventBlock onVideoProgress; -@property (nonatomic, copy) RCTDirectEventBlock onBandwidthUpdate; -@property (nonatomic, copy) RCTDirectEventBlock onVideoSeek; -@property (nonatomic, copy) RCTDirectEventBlock onVideoEnd; -@property (nonatomic, copy) RCTDirectEventBlock onTimedMetadata; -@property (nonatomic, copy) RCTDirectEventBlock onVideoAudioBecomingNoisy; -@property (nonatomic, copy) RCTDirectEventBlock onVideoFullscreenPlayerWillPresent; -@property (nonatomic, copy) RCTDirectEventBlock onVideoFullscreenPlayerDidPresent; -@property (nonatomic, copy) RCTDirectEventBlock onVideoFullscreenPlayerWillDismiss; -@property (nonatomic, copy) RCTDirectEventBlock onVideoFullscreenPlayerDidDismiss; -@property (nonatomic, copy) RCTDirectEventBlock onReadyForDisplay; -@property (nonatomic, copy) RCTDirectEventBlock onPlaybackStalled; -@property (nonatomic, copy) RCTDirectEventBlock onPlaybackResume; -@property (nonatomic, copy) RCTDirectEventBlock onPlaybackRateChange; -@property (nonatomic, copy) RCTDirectEventBlock onVideoExternalPlaybackChange; -@property (nonatomic, copy) RCTDirectEventBlock onPictureInPictureStatusChanged; -@property (nonatomic, copy) RCTDirectEventBlock onRestoreUserInterfaceForPictureInPictureStop; -@property (nonatomic, copy) RCTDirectEventBlock onGetLicense; - -typedef NS_ENUM(NSInteger, RCTVideoError) { - RCTVideoErrorFromJSPart, - RCTVideoErrorLicenseRequestNotOk, - RCTVideoErrorNoDataFromLicenseRequest, - RCTVideoErrorNoSPC, - RCTVideoErrorNoDataRequest, - RCTVideoErrorNoCertificateData, - RCTVideoErrorNoCertificateURL, - RCTVideoErrorNoFairplayDRM, - RCTVideoErrorNoDRMData -}; - -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - -- (AVPlayerViewController*)createPlayerViewController:(AVPlayer*)player withPlayerItem:(AVPlayerItem*)playerItem; - -- (void)save:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject; -- (void)setLicenseResult:(NSString * )license; -- (BOOL)setLicenseResultError:(NSString * )error; - -@end diff --git a/ios/Video/RCTVideo.m b/ios/Video/RCTVideo.m deleted file mode 100644 index cd01cc4e..00000000 --- a/ios/Video/RCTVideo.m +++ /dev/null @@ -1,2032 +0,0 @@ -#import -#import "RCTVideo.h" -#import -#import -#import -#include -#include - -static NSString *const statusKeyPath = @"status"; -static NSString *const playbackLikelyToKeepUpKeyPath = @"playbackLikelyToKeepUp"; -static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; -static NSString *const readyForDisplayKeyPath = @"readyForDisplay"; -static NSString *const playbackRate = @"rate"; -static NSString *const timedMetadata = @"timedMetadata"; -static NSString *const externalPlaybackActive = @"externalPlaybackActive"; - -static int const RCTVideoUnset = -1; - -#ifdef DEBUG - #define DebugLog(...) NSLog(__VA_ARGS__) -#else - #define DebugLog(...) (void)0 -#endif - -@implementation RCTVideo -{ - AVPlayer *_player; - AVPlayerItem *_playerItem; - NSDictionary *_source; - BOOL _playerItemObserversSet; - BOOL _playerBufferEmpty; - AVPlayerLayer *_playerLayer; - BOOL _playerLayerObserverSet; - RCTVideoPlayerViewController *_playerViewController; - NSURL *_videoURL; - BOOL _requestingCertificate; - BOOL _requestingCertificateErrored; - - /* DRM */ - NSDictionary *_drm; - AVAssetResourceLoadingRequest *_loadingRequest; - - /* Required to publish events */ - RCTEventDispatcher *_eventDispatcher; - BOOL _playbackRateObserverRegistered; - BOOL _isExternalPlaybackActiveObserverRegistered; - BOOL _videoLoadStarted; - - bool _pendingSeek; - float _pendingSeekTime; - float _lastSeekTime; - - /* For sending videoProgress events */ - Float64 _progressUpdateInterval; - BOOL _controls; - id _timeObserver; - - /* Keep track of any modifiers, need to be applied after each play */ - float _volume; - float _rate; - float _maxBitRate; - - BOOL _automaticallyWaitsToMinimizeStalling; - BOOL _muted; - BOOL _paused; - BOOL _repeat; - BOOL _allowsExternalPlayback; - NSArray * _textTracks; - NSDictionary * _selectedTextTrack; - NSDictionary * _selectedAudioTrack; - BOOL _playbackStalled; - BOOL _playInBackground; - BOOL _preventsDisplaySleepDuringVideoPlayback; - float _preferredForwardBufferDuration; - BOOL _playWhenInactive; - BOOL _pictureInPicture; - NSString * _ignoreSilentSwitch; - NSString * _mixWithOthers; - NSString * _resizeMode; - BOOL _fullscreen; - BOOL _fullscreenAutorotate; - NSString * _fullscreenOrientation; - BOOL _fullscreenPlayerPresented; - NSString *_filterName; - BOOL _filterEnabled; - UIViewController * _presentingViewController; -#if __has_include() - RCTVideoCache * _videoCache; -#endif -#if TARGET_OS_IOS - void (^__strong _Nonnull _restoreUserInterfaceForPIPStopCompletionHandler)(BOOL); - AVPictureInPictureController *_pipController; -#endif -} - -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher -{ - if ((self = [super init])) { - _eventDispatcher = eventDispatcher; - _automaticallyWaitsToMinimizeStalling = YES; - _playbackRateObserverRegistered = NO; - _isExternalPlaybackActiveObserverRegistered = NO; - _playbackStalled = NO; - _rate = 1.0; - _volume = 1.0; - _resizeMode = @"AVLayerVideoGravityResizeAspectFill"; - _fullscreenAutorotate = YES; - _fullscreenOrientation = @"all"; - _pendingSeek = false; - _pendingSeekTime = 0.0f; - _lastSeekTime = 0.0f; - _progressUpdateInterval = 250; - _controls = NO; - _playerBufferEmpty = YES; - _playInBackground = false; - _preventsDisplaySleepDuringVideoPlayback = true; - _preferredForwardBufferDuration = 0.0f; - _allowsExternalPlayback = YES; - _playWhenInactive = false; - _pictureInPicture = false; - _ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey - _mixWithOthers = @"inherit"; // inherit, mix, duck -#if TARGET_OS_IOS - _restoreUserInterfaceForPIPStopCompletionHandler = NULL; -#endif -#if __has_include() - _videoCache = [RCTVideoCache sharedInstance]; -#endif - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationWillResignActive:) - name:UIApplicationWillResignActiveNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationDidEnterBackground:) - name:UIApplicationDidEnterBackgroundNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationWillEnterForeground:) - name:UIApplicationWillEnterForegroundNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(audioRouteChanged:) - name:AVAudioSessionRouteChangeNotification - object:nil]; - } - - return self; -} - -- (RCTVideoPlayerViewController*)createPlayerViewController:(AVPlayer*)player - withPlayerItem:(AVPlayerItem*)playerItem { - RCTVideoPlayerViewController* viewController = [[RCTVideoPlayerViewController alloc] init]; - viewController.showsPlaybackControls = YES; - viewController.rctDelegate = self; - viewController.preferredOrientation = _fullscreenOrientation; - - viewController.view.frame = self.bounds; - viewController.player = player; - return viewController; -} - -/* --------------------------------------------------------- - ** Get the duration for a AVPlayerItem. - ** ------------------------------------------------------- */ - -- (CMTime)playerItemDuration -{ - AVPlayerItem *playerItem = [_player currentItem]; - if (playerItem.status == AVPlayerItemStatusReadyToPlay) - { - return([playerItem duration]); - } - - return(kCMTimeInvalid); -} - -- (CMTimeRange)playerItemSeekableTimeRange -{ - AVPlayerItem *playerItem = [_player currentItem]; - if (playerItem.status == AVPlayerItemStatusReadyToPlay) - { - return [playerItem seekableTimeRanges].firstObject.CMTimeRangeValue; - } - - return (kCMTimeRangeZero); -} - --(void)addPlayerTimeObserver -{ - const Float64 progressUpdateIntervalMS = _progressUpdateInterval / 1000; - // @see endScrubbing in AVPlayerDemoPlaybackViewController.m - // of https://developer.apple.com/library/ios/samplecode/AVPlayerDemo/Introduction/Intro.html - __weak RCTVideo *weakSelf = self; - _timeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(progressUpdateIntervalMS, NSEC_PER_SEC) - queue:NULL - usingBlock:^(CMTime time) { [weakSelf sendProgressUpdate]; } - ]; -} - -/* Cancels the previously registered time observer. */ --(void)removePlayerTimeObserver -{ - if (_timeObserver) - { - [_player removeTimeObserver:_timeObserver]; - _timeObserver = nil; - } -} - -#pragma mark - Progress - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [self removePlayerLayer]; - [self removePlayerItemObservers]; - [_player removeObserver:self forKeyPath:playbackRate context:nil]; - [_player removeObserver:self forKeyPath:externalPlaybackActive context: nil]; -} - -#pragma mark - App lifecycle handlers - -- (void)applicationWillResignActive:(NSNotification *)notification -{ - if (_playInBackground || _playWhenInactive || _paused) return; - - [_player pause]; - [_player setRate:0.0]; -} - -- (void)applicationDidEnterBackground:(NSNotification *)notification -{ - if (_playInBackground) { - // Needed to play sound in background. See https://developer.apple.com/library/ios/qa/qa1668/_index.html - [_playerLayer setPlayer:nil]; - [_playerViewController setPlayer:nil]; - } -} - -- (void)applicationWillEnterForeground:(NSNotification *)notification -{ - [self applyModifiers]; - if (_playInBackground) { - [_playerLayer setPlayer:_player]; - [_playerViewController setPlayer:_player]; - } -} - -#pragma mark - Audio events - -- (void)audioRouteChanged:(NSNotification *)notification -{ - NSNumber *reason = [[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey]; - NSNumber *previousRoute = [[notification userInfo] objectForKey:AVAudioSessionRouteChangePreviousRouteKey]; - if (reason.unsignedIntValue == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) { - self.onVideoAudioBecomingNoisy(@{@"target": self.reactTag}); - } -} - -#pragma mark - Progress - -- (void)sendProgressUpdate -{ - AVPlayerItem *video = [_player currentItem]; - if (video == nil || video.status != AVPlayerItemStatusReadyToPlay) { - return; - } - - CMTime playerDuration = [self playerItemDuration]; - if (CMTIME_IS_INVALID(playerDuration)) { - return; - } - - CMTime currentTime = _player.currentTime; - NSDate *currentPlaybackTime = _player.currentItem.currentDate; - const Float64 duration = CMTimeGetSeconds(playerDuration); - const Float64 currentTimeSecs = CMTimeGetSeconds(currentTime); - - [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTVideo_progress" object:nil userInfo:@{@"progress": [NSNumber numberWithDouble: currentTimeSecs / duration]}]; - - if( currentTimeSecs >= 0 && self.onVideoProgress) { - self.onVideoProgress(@{ - @"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(currentTime)], - @"playableDuration": [self calculatePlayableDuration], - @"atValue": [NSNumber numberWithLongLong:currentTime.value], - @"atTimescale": [NSNumber numberWithInt:currentTime.timescale], - @"currentPlaybackTime": [NSNumber numberWithLongLong:[@(floor([currentPlaybackTime timeIntervalSince1970] * 1000)) longLongValue]], - @"target": self.reactTag, - @"seekableDuration": [self calculateSeekableDuration], - }); - } -} - -/*! - * Calculates and returns the playable duration of the current player item using its loaded time ranges. - * - * \returns The playable duration of the current player item in seconds. - */ -- (NSNumber *)calculatePlayableDuration -{ - AVPlayerItem *video = _player.currentItem; - if (video.status == AVPlayerItemStatusReadyToPlay) { - __block CMTimeRange effectiveTimeRange; - [video.loadedTimeRanges enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - CMTimeRange timeRange = [obj CMTimeRangeValue]; - if (CMTimeRangeContainsTime(timeRange, video.currentTime)) { - effectiveTimeRange = timeRange; - *stop = YES; - } - }]; - Float64 playableDuration = CMTimeGetSeconds(CMTimeRangeGetEnd(effectiveTimeRange)); - if (playableDuration > 0) { - return [NSNumber numberWithFloat:playableDuration]; - } - } - return [NSNumber numberWithInteger:0]; -} - -- (NSNumber *)calculateSeekableDuration -{ - CMTimeRange timeRange = [self playerItemSeekableTimeRange]; - if (CMTIME_IS_NUMERIC(timeRange.duration)) - { - return [NSNumber numberWithFloat:CMTimeGetSeconds(timeRange.duration)]; - } - return [NSNumber numberWithInteger:0]; -} - -- (void)addPlayerItemObservers -{ - [_playerItem addObserver:self forKeyPath:statusKeyPath options:0 context:nil]; - [_playerItem addObserver:self forKeyPath:playbackBufferEmptyKeyPath options:0 context:nil]; - [_playerItem addObserver:self forKeyPath:playbackLikelyToKeepUpKeyPath options:0 context:nil]; - [_playerItem addObserver:self forKeyPath:timedMetadata options:NSKeyValueObservingOptionNew context:nil]; - _playerItemObserversSet = YES; -} - -/* Fixes https://github.com/brentvatne/react-native-video/issues/43 - * Crashes caused when trying to remove the observer when there is no - * observer set */ -- (void)removePlayerItemObservers -{ - if (_playerItemObserversSet) { - [_playerItem removeObserver:self forKeyPath:statusKeyPath]; - [_playerItem removeObserver:self forKeyPath:playbackBufferEmptyKeyPath]; - [_playerItem removeObserver:self forKeyPath:playbackLikelyToKeepUpKeyPath]; - [_playerItem removeObserver:self forKeyPath:timedMetadata]; - _playerItemObserversSet = NO; - } -} - -#pragma mark - Player and source - -- (void)setSrc:(NSDictionary *)source -{ - _source = source; - [self removePlayerLayer]; - [self removePlayerTimeObserver]; - [self removePlayerItemObservers]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) 0), dispatch_get_main_queue(), ^{ - - // perform on next run loop, otherwise other passed react-props may not be set - [self playerItemForSource:self->_source withCallback:^(AVPlayerItem * playerItem) { - self->_playerItem = playerItem; - _playerItem = playerItem; - [self setPreferredForwardBufferDuration:_preferredForwardBufferDuration]; - [self addPlayerItemObservers]; - [self setFilter:self->_filterName]; - [self setMaxBitRate:self->_maxBitRate]; - - [_player pause]; - - if (_playbackRateObserverRegistered) { - [_player removeObserver:self forKeyPath:playbackRate context:nil]; - _playbackRateObserverRegistered = NO; - } - if (self->_isExternalPlaybackActiveObserverRegistered) { - [self->_player removeObserver:self forKeyPath:externalPlaybackActive context:nil]; - self->_isExternalPlaybackActiveObserverRegistered = NO; - } - - self->_player = [AVPlayer playerWithPlayerItem:self->_playerItem]; - [self applyModifiers]; - self->_player.actionAtItemEnd = AVPlayerActionAtItemEndNone; - - [self->_player addObserver:self forKeyPath:playbackRate options:0 context:nil]; - self->_playbackRateObserverRegistered = YES; - - [self->_player addObserver:self forKeyPath:externalPlaybackActive options:0 context:nil]; - self->_isExternalPlaybackActiveObserverRegistered = YES; - - [self addPlayerTimeObserver]; - if (@available(iOS 10.0, *)) { - [self setAutomaticallyWaitsToMinimizeStalling:_automaticallyWaitsToMinimizeStalling]; - } - - //Perform on next run loop, otherwise onVideoLoadStart is nil - if (self.onVideoLoadStart) { - id uri = [self->_source objectForKey:@"uri"]; - id type = [self->_source objectForKey:@"type"]; - self.onVideoLoadStart(@{@"src": @{ - @"uri": uri ? uri : [NSNull null], - @"type": type ? type : [NSNull null], - @"isNetwork": [NSNumber numberWithBool:(bool)[self->_source objectForKey:@"isNetwork"]]}, - @"drm": self->_drm ? self->_drm : [NSNull null], - @"target": self.reactTag - }); - } - }]; - }); - _videoLoadStarted = YES; -} - -- (void)setDrm:(NSDictionary *)drm { - _drm = drm; -} - -- (NSURL*) urlFilePath:(NSString*) filepath { - if ([filepath containsString:@"file://"]) { - return [NSURL URLWithString:filepath]; - } - - // if no file found, check if the file exists in the Document directory - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString* relativeFilePath = [filepath lastPathComponent]; - // the file may be multiple levels below the documents directory - NSArray* fileComponents = [filepath componentsSeparatedByString:@"Documents/"]; - if (fileComponents.count > 1) { - relativeFilePath = [fileComponents objectAtIndex:1]; - } - - NSString *path = [paths.firstObject stringByAppendingPathComponent:relativeFilePath]; - if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { - return [NSURL fileURLWithPath:path]; - } - return nil; -} - -- (void)playerItemPrepareText:(AVAsset *)asset assetOptions:(NSDictionary * __nullable)assetOptions withCallback:(void(^)(AVPlayerItem *))handler -{ - if (!_textTracks || _textTracks.count==0) { - handler([AVPlayerItem playerItemWithAsset:asset]); - return; - } - - // AVPlayer can't airplay AVMutableCompositions - _allowsExternalPlayback = NO; - - // sideload text tracks - AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init]; - - AVAssetTrack *videoAsset = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject; - AVMutableCompositionTrack *videoCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; - [videoCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) - ofTrack:videoAsset - atTime:kCMTimeZero - error:nil]; - - AVAssetTrack *audioAsset = [asset tracksWithMediaType:AVMediaTypeAudio].firstObject; - AVMutableCompositionTrack *audioCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; - [audioCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) - ofTrack:audioAsset - atTime:kCMTimeZero - error:nil]; - - NSMutableArray* validTextTracks = [NSMutableArray array]; - for (int i = 0; i < _textTracks.count; ++i) { - AVURLAsset *textURLAsset; - NSString *textUri = [_textTracks objectAtIndex:i][@"uri"]; - if ([[textUri lowercaseString] hasPrefix:@"http"]) { - textURLAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:textUri] options:assetOptions]; - } else { - textURLAsset = [AVURLAsset URLAssetWithURL:[self urlFilePath:textUri] options:nil]; - } - AVAssetTrack *textTrackAsset = [textURLAsset tracksWithMediaType:AVMediaTypeText].firstObject; - if (!textTrackAsset) continue; // fix when there's no textTrackAsset - [validTextTracks addObject:[_textTracks objectAtIndex:i]]; - AVMutableCompositionTrack *textCompTrack = [mixComposition - addMutableTrackWithMediaType:AVMediaTypeText - preferredTrackID:kCMPersistentTrackID_Invalid]; - [textCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) - ofTrack:textTrackAsset - atTime:kCMTimeZero - error:nil]; - } - if (validTextTracks.count != _textTracks.count) { - [self setTextTracks:validTextTracks]; - } - - handler([AVPlayerItem playerItemWithAsset:mixComposition]); -} - -- (void)playerItemForSource:(NSDictionary *)source withCallback:(void(^)(AVPlayerItem *))handler -{ - bool isNetwork = [RCTConvert BOOL:[source objectForKey:@"isNetwork"]]; - bool isAsset = [RCTConvert BOOL:[source objectForKey:@"isAsset"]]; - bool shouldCache = [RCTConvert BOOL:[source objectForKey:@"shouldCache"]]; - NSString *uri = [source objectForKey:@"uri"]; - NSString *type = [source objectForKey:@"type"]; - AVURLAsset *asset; - if (!uri || [uri isEqualToString:@""]) { - DebugLog(@"Could not find video URL in source '%@'", source); - return; - } - - NSURL *url = isNetwork || isAsset - ? [NSURL URLWithString:uri] - : [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]]; - NSMutableDictionary *assetOptions = [[NSMutableDictionary alloc] init]; - - if (isNetwork) { - NSDictionary *headers = [source objectForKey:@"requestHeaders"]; - if ([headers count] > 0) { - [assetOptions setObject:headers forKey:@"AVURLAssetHTTPHeaderFieldsKey"]; - } - NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; - [assetOptions setObject:cookies forKey:AVURLAssetHTTPCookiesKey]; - -#if __has_include() - if (shouldCache && (!_textTracks || !_textTracks.count)) { - /* The DVURLAsset created by cache doesn't have a tracksWithMediaType property, so trying - * to bring in the text track code will crash. I suspect this is because the asset hasn't fully loaded. - * Until this is fixed, we need to bypass caching when text tracks are specified. - */ - DebugLog(@"Caching is not supported for uri '%@' because text tracks are not compatible with the cache. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md", uri); - [self playerItemForSourceUsingCache:uri assetOptions:assetOptions withCallback:handler]; - return; - } -#endif - - asset = [AVURLAsset URLAssetWithURL:url options:assetOptions]; - } else if (isAsset) { - asset = [AVURLAsset URLAssetWithURL:url options:nil]; - } else { - asset = [AVURLAsset URLAssetWithURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]] options:nil]; - } - // Reset _loadingRequest - if (_loadingRequest != nil) { - [_loadingRequest finishLoading]; - } - _requestingCertificate = NO; - _requestingCertificateErrored = NO; - // End Reset _loadingRequest - if (self->_drm != nil) { - dispatch_queue_t queue = dispatch_queue_create("assetQueue", nil); - [asset.resourceLoader setDelegate:self queue:queue]; - } - - [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler]; -} - -#if __has_include() - -- (void)playerItemForSourceUsingCache:(NSString *)uri assetOptions:(NSDictionary *)options withCallback:(void(^)(AVPlayerItem *))handler { - NSURL *url = [NSURL URLWithString:uri]; - [_videoCache getItemForUri:uri withCallback:^(RCTVideoCacheStatus videoCacheStatus, AVAsset * _Nullable cachedAsset) { - switch (videoCacheStatus) { - case RCTVideoCacheStatusMissingFileExtension: { - DebugLog(@"Could not generate cache key for uri '%@'. It is currently not supported to cache urls that do not include a file extension. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md", uri); - AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:options]; - [self playerItemPrepareText:asset assetOptions:options withCallback:handler]; - return; - } - case RCTVideoCacheStatusUnsupportedFileExtension: { - DebugLog(@"Could not generate cache key for uri '%@'. The file extension of that uri is currently not supported. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md", uri); - AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:options]; - [self playerItemPrepareText:asset assetOptions:options withCallback:handler]; - return; - } - default: - if (cachedAsset) { - DebugLog(@"Playing back uri '%@' from cache", uri); - // See note in playerItemForSource about not being able to support text tracks & caching - handler([AVPlayerItem playerItemWithAsset:cachedAsset]); - return; - } - } - - DVURLAsset *asset = [[DVURLAsset alloc] initWithURL:url options:options networkTimeout:10000]; - asset.loaderDelegate = self; - - /* More granular code to have control over the DVURLAsset - DVAssetLoaderDelegate *resourceLoaderDelegate = [[DVAssetLoaderDelegate alloc] initWithURL:url]; - resourceLoaderDelegate.delegate = self; - NSURLComponents *components = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO]; - components.scheme = [DVAssetLoaderDelegate scheme]; - AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[components URL] options:options]; - [asset.resourceLoader setDelegate:resourceLoaderDelegate queue:dispatch_get_main_queue()]; - */ - - handler([AVPlayerItem playerItemWithAsset:asset]); - }]; -} - -#pragma mark - DVAssetLoaderDelegate - -- (void)dvAssetLoaderDelegate:(DVAssetLoaderDelegate *)loaderDelegate - didLoadData:(NSData *)data - forURL:(NSURL *)url { - [_videoCache storeItem:data forUri:[url absoluteString] withCallback:^(BOOL success) { - DebugLog(@"Cache data stored successfully 🎉"); - }]; -} - -#endif - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ - - if([keyPath isEqualToString:readyForDisplayKeyPath] && [change objectForKey:NSKeyValueChangeNewKey] && self.onReadyForDisplay) { - self.onReadyForDisplay(@{@"target": self.reactTag}); - return; - } - if (object == _playerItem) { - // When timeMetadata is read the event onTimedMetadata is triggered - if ([keyPath isEqualToString:timedMetadata]) { - NSArray *items = [change objectForKey:@"new"]; - if (items && ![items isEqual:[NSNull null]] && items.count > 0) { - NSMutableArray *array = [NSMutableArray new]; - for (AVMetadataItem *item in items) { - NSString *value = (NSString *)item.value; - NSString *identifier = item.identifier; - - if (![value isEqual: [NSNull null]]) { - NSDictionary *dictionary = [[NSDictionary alloc] initWithObjects:@[value, identifier] forKeys:@[@"value", @"identifier"]]; - - [array addObject:dictionary]; - } - } - - self.onTimedMetadata(@{ - @"target": self.reactTag, - @"metadata": array - }); - } - } - - if ([keyPath isEqualToString:statusKeyPath]) { - // Handle player item status change. - if (_playerItem.status == AVPlayerItemStatusReadyToPlay) { - float duration = CMTimeGetSeconds(_playerItem.asset.duration); - - if (isnan(duration)) { - duration = 0.0; - } - - NSObject *width = @"undefined"; - NSObject *height = @"undefined"; - NSString *orientation = @"undefined"; - - if ([_playerItem.asset tracksWithMediaType:AVMediaTypeVideo].count > 0) { - AVAssetTrack *videoTrack = [[_playerItem.asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; - width = [NSNumber numberWithFloat:videoTrack.naturalSize.width]; - height = [NSNumber numberWithFloat:videoTrack.naturalSize.height]; - CGAffineTransform preferredTransform = [videoTrack preferredTransform]; - - if ((videoTrack.naturalSize.width == preferredTransform.tx - && videoTrack.naturalSize.height == preferredTransform.ty) - || (preferredTransform.tx == 0 && preferredTransform.ty == 0)) - { - orientation = @"landscape"; - } else { - orientation = @"portrait"; - } - } else if (_playerItem.presentationSize.height) { - width = [NSNumber numberWithFloat:_playerItem.presentationSize.width]; - height = [NSNumber numberWithFloat:_playerItem.presentationSize.height]; - orientation = _playerItem.presentationSize.width > _playerItem.presentationSize.height ? @"landscape" : @"portrait"; - } - - if (_pendingSeek) { - [self setCurrentTime:_pendingSeekTime]; - _pendingSeek = false; - } - - if (self.onVideoLoad && _videoLoadStarted) { - self.onVideoLoad(@{@"duration": [NSNumber numberWithFloat:duration], - @"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(_playerItem.currentTime)], - @"canPlayReverse": [NSNumber numberWithBool:_playerItem.canPlayReverse], - @"canPlayFastForward": [NSNumber numberWithBool:_playerItem.canPlayFastForward], - @"canPlaySlowForward": [NSNumber numberWithBool:_playerItem.canPlaySlowForward], - @"canPlaySlowReverse": [NSNumber numberWithBool:_playerItem.canPlaySlowReverse], - @"canStepBackward": [NSNumber numberWithBool:_playerItem.canStepBackward], - @"canStepForward": [NSNumber numberWithBool:_playerItem.canStepForward], - @"naturalSize": @{ - @"width": width, - @"height": height, - @"orientation": orientation - }, - @"audioTracks": [self getAudioTrackInfo], - @"textTracks": [self getTextTrackInfo], - @"target": self.reactTag}); - } - _videoLoadStarted = NO; - - [self attachListeners]; - [self applyModifiers]; - } else if (_playerItem.status == AVPlayerItemStatusFailed && self.onVideoError) { - self.onVideoError(@{@"error": @{@"code": [NSNumber numberWithInteger: _playerItem.error.code], - @"localizedDescription": [_playerItem.error localizedDescription] == nil ? @"" : [_playerItem.error localizedDescription], - @"localizedFailureReason": [_playerItem.error localizedFailureReason] == nil ? @"" : [_playerItem.error localizedFailureReason], - @"localizedRecoverySuggestion": [_playerItem.error localizedRecoverySuggestion] == nil ? @"" : [_playerItem.error localizedRecoverySuggestion], - @"domain": _playerItem != nil && _playerItem.error != nil ? _playerItem.error.domain : @"RTCVideo"}, - @"target": self.reactTag}); - } - } else if ([keyPath isEqualToString:playbackBufferEmptyKeyPath]) { - _playerBufferEmpty = YES; - self.onVideoBuffer(@{@"isBuffering": @(YES), @"target": self.reactTag}); - } else if ([keyPath isEqualToString:playbackLikelyToKeepUpKeyPath]) { - // Continue playing (or not if paused) after being paused due to hitting an unbuffered zone. - if ((!(_controls || _fullscreenPlayerPresented) || _playerBufferEmpty) && _playerItem.playbackLikelyToKeepUp) { - [self setPaused:_paused]; - } - _playerBufferEmpty = NO; - self.onVideoBuffer(@{@"isBuffering": @(NO), @"target": self.reactTag}); - } - } else if (object == _player) { - if([keyPath isEqualToString:playbackRate]) { - if (_player.rate > 0 && _rate > 0 && _player.rate != _rate) { - // Playback is resuming, apply rate modifer. - [_player setRate:_rate]; - } else if(self.onPlaybackRateChange) { - self.onPlaybackRateChange(@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], - @"target": self.reactTag}); - } - if(_playbackStalled && _player.rate > 0) { - if(self.onPlaybackResume) { - self.onPlaybackResume(@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], - @"target": self.reactTag}); - } - _playbackStalled = NO; - } - } - else if([keyPath isEqualToString:externalPlaybackActive]) { - if(self.onVideoExternalPlaybackChange) { - self.onVideoExternalPlaybackChange(@{@"isExternalPlaybackActive": [NSNumber numberWithBool:_player.isExternalPlaybackActive], - @"target": self.reactTag}); - } - } - } else if (object == _playerViewController.contentOverlayView) { - // when controls==true, this is a hack to reset the rootview when rotation happens in fullscreen - if ([keyPath isEqualToString:@"frame"]) { - - CGRect oldRect = [change[NSKeyValueChangeOldKey] CGRectValue]; - CGRect newRect = [change[NSKeyValueChangeNewKey] CGRectValue]; - - if (!CGRectEqualToRect(oldRect, newRect)) { - if (CGRectEqualToRect(newRect, [UIScreen mainScreen].bounds)) { - NSLog(@"in fullscreen"); - - [self.reactViewController.view setFrame:[UIScreen mainScreen].bounds]; - [self.reactViewController.view setNeedsLayout]; - } else NSLog(@"not fullscreen"); - } - - return; - } - } -} - -- (void)attachListeners -{ - // listen for end of file - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVPlayerItemDidPlayToEndTimeNotification - object:[_player currentItem]]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(playerItemDidReachEnd:) - name:AVPlayerItemDidPlayToEndTimeNotification - object:[_player currentItem]]; - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVPlayerItemPlaybackStalledNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(playbackStalled:) - name:AVPlayerItemPlaybackStalledNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVPlayerItemNewAccessLogEntryNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleAVPlayerAccess:) - name:AVPlayerItemNewAccessLogEntryNotification - object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self - name: AVPlayerItemFailedToPlayToEndTimeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(didFailToFinishPlaying:) - name: AVPlayerItemFailedToPlayToEndTimeNotification - object:nil]; - -} - -- (void)handleAVPlayerAccess:(NSNotification *)notification { - AVPlayerItemAccessLog *accessLog = [((AVPlayerItem *)notification.object) accessLog]; - AVPlayerItemAccessLogEvent *lastEvent = accessLog.events.lastObject; - - /* TODO: get this working - if (self.onBandwidthUpdate) { - self.onBandwidthUpdate(@{@"bitrate": [NSNumber numberWithFloat:lastEvent.observedBitrate]}); - } - */ -} - -- (void)didFailToFinishPlaying:(NSNotification *)notification { - NSError *error = notification.userInfo[AVPlayerItemFailedToPlayToEndTimeErrorKey]; - self.onVideoError(@{@"error": @{@"code": [NSNumber numberWithInteger: error.code], - @"localizedDescription": [error localizedDescription] == nil ? @"" : [error localizedDescription], - @"localizedFailureReason": [error localizedFailureReason] == nil ? @"" : [error localizedFailureReason], - @"localizedRecoverySuggestion": [error localizedRecoverySuggestion] == nil ? @"" : [error localizedRecoverySuggestion], - @"domain": error.domain}, - @"target": self.reactTag}); -} - -- (void)playbackStalled:(NSNotification *)notification -{ - if(self.onPlaybackStalled) { - self.onPlaybackStalled(@{@"target": self.reactTag}); - } - _playbackStalled = YES; -} - -- (void)playerItemDidReachEnd:(NSNotification *)notification -{ - if(self.onVideoEnd) { - self.onVideoEnd(@{@"target": self.reactTag}); - } - - if (_repeat) { - AVPlayerItem *item = [notification object]; - [item seekToTime:kCMTimeZero]; - [self applyModifiers]; - } else { - [self removePlayerTimeObserver]; - } -} - -#pragma mark - Prop setters - -- (void)setResizeMode:(NSString*)mode -{ - if( _controls ) - { - _playerViewController.videoGravity = mode; - } - else - { - _playerLayer.videoGravity = mode; - } - _resizeMode = mode; -} - -- (void)setPlayInBackground:(BOOL)playInBackground -{ - _playInBackground = playInBackground; -} - -- (void)setPreventsDisplaySleepDuringVideoPlayback:(BOOL)preventsDisplaySleepDuringVideoPlayback -{ - _preventsDisplaySleepDuringVideoPlayback = preventsDisplaySleepDuringVideoPlayback; - [self applyModifiers]; -} - -- (void)setAllowsExternalPlayback:(BOOL)allowsExternalPlayback -{ - _allowsExternalPlayback = allowsExternalPlayback; - _player.allowsExternalPlayback = _allowsExternalPlayback; -} - -- (void)setPlayWhenInactive:(BOOL)playWhenInactive -{ - _playWhenInactive = playWhenInactive; -} - -- (void)setPictureInPicture:(BOOL)pictureInPicture -{ - #if TARGET_OS_IOS - if (_pictureInPicture == pictureInPicture) { - return; - } - - _pictureInPicture = pictureInPicture; - if (_pipController && _pictureInPicture && ![_pipController isPictureInPictureActive]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [_pipController startPictureInPicture]; - }); - } else if (_pipController && !_pictureInPicture && [_pipController isPictureInPictureActive]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [_pipController stopPictureInPicture]; - }); - } - #endif -} - -#if TARGET_OS_IOS -- (void)setRestoreUserInterfaceForPIPStopCompletionHandler:(BOOL)restore -{ - if (_restoreUserInterfaceForPIPStopCompletionHandler != NULL) { - _restoreUserInterfaceForPIPStopCompletionHandler(restore); - _restoreUserInterfaceForPIPStopCompletionHandler = NULL; - } -} - -- (void)setupPipController { - if (!_pipController && _playerLayer && [AVPictureInPictureController isPictureInPictureSupported] && _pictureInPicture) { - // Create new controller passing reference to the AVPlayerLayer - _pipController = [[AVPictureInPictureController alloc] initWithPlayerLayer:_playerLayer]; - _pipController.delegate = self; - } -} -#endif - -- (void)setIgnoreSilentSwitch:(NSString *)ignoreSilentSwitch -{ - _ignoreSilentSwitch = ignoreSilentSwitch; - [self configureAudio]; - [self applyModifiers]; -} - -- (void)setMixWithOthers:(NSString *)mixWithOthers -{ - _mixWithOthers = mixWithOthers; - [self applyModifiers]; -} - -- (void)setPaused:(BOOL)paused -{ - if (paused) { - [_player pause]; - [_player setRate:0.0]; - } else { - - [self configureAudio]; - - if (@available(iOS 10.0, *) && !_automaticallyWaitsToMinimizeStalling) { - [_player playImmediatelyAtRate:_rate]; - } else { - [_player play]; - [_player setRate:_rate]; - } - [_player setRate:_rate]; - } - - _paused = paused; -} - -- (float)getCurrentTime -{ - return _playerItem != NULL ? CMTimeGetSeconds(_playerItem.currentTime) : 0; -} - -- (void)setCurrentTime:(float)currentTime -{ - NSDictionary *info = @{ - @"time": [NSNumber numberWithFloat:currentTime], - @"tolerance": [NSNumber numberWithInt:100] - }; - [self setSeek:info]; -} - -- (void)setSeek:(NSDictionary *)info -{ - NSNumber *seekTime = info[@"time"]; - NSNumber *seekTolerance = info[@"tolerance"]; - - int timeScale = 1000; - - AVPlayerItem *item = _player.currentItem; - if (item && item.status == AVPlayerItemStatusReadyToPlay) { - // TODO check loadedTimeRanges - - CMTime cmSeekTime = CMTimeMakeWithSeconds([seekTime floatValue], timeScale); - CMTime current = item.currentTime; - // TODO figure out a good tolerance level - CMTime tolerance = CMTimeMake([seekTolerance floatValue], timeScale); - BOOL wasPaused = _paused; - - if (CMTimeCompare(current, cmSeekTime) != 0) { - if (!wasPaused) [_player pause]; - [_player seekToTime:cmSeekTime toleranceBefore:tolerance toleranceAfter:tolerance completionHandler:^(BOOL finished) { - if (!_timeObserver) { - [self addPlayerTimeObserver]; - } - if (!wasPaused) { - [self setPaused:false]; - } - if(self.onVideoSeek) { - self.onVideoSeek(@{@"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(item.currentTime)], - @"seekTime": seekTime, - @"target": self.reactTag}); - } - }]; - - _pendingSeek = false; - } - - } else { - _pendingSeek = true; - _pendingSeekTime = [seekTime floatValue]; - } -} - -- (void)setRate:(float)rate -{ - _rate = rate; - [self applyModifiers]; -} - -- (void)setMuted:(BOOL)muted -{ - _muted = muted; - [self applyModifiers]; -} - -- (void)setVolume:(float)volume -{ - _volume = volume; - [self applyModifiers]; -} - -- (void)setMaxBitRate:(float) maxBitRate { - _maxBitRate = maxBitRate; - _playerItem.preferredPeakBitRate = maxBitRate; -} - -- (void)setPreferredForwardBufferDuration:(float) preferredForwardBufferDuration -{ - _preferredForwardBufferDuration = preferredForwardBufferDuration; - _playerItem.preferredForwardBufferDuration = preferredForwardBufferDuration; -} - -- (void)setAutomaticallyWaitsToMinimizeStalling:(BOOL)waits -{ - _automaticallyWaitsToMinimizeStalling = waits; - _player.automaticallyWaitsToMinimizeStalling = waits; -} - - -- (void)applyModifiers -{ - if (_muted) { - if (!_controls) { - [_player setVolume:0]; - } - [_player setMuted:YES]; - } else { - [_player setVolume:_volume]; - [_player setMuted:NO]; - } - - if (@available(iOS 12.0, *)) { - self->_player.preventsDisplaySleepDuringVideoPlayback = _preventsDisplaySleepDuringVideoPlayback; - } else { - // Fallback on earlier versions - } - - [self setMaxBitRate:_maxBitRate]; - [self setSelectedAudioTrack:_selectedAudioTrack]; - [self setSelectedTextTrack:_selectedTextTrack]; - [self setResizeMode:_resizeMode]; - [self setRepeat:_repeat]; - [self setControls:_controls]; - [self setPaused:_paused]; - [self setAllowsExternalPlayback:_allowsExternalPlayback]; -} - -- (void)configureAudio -{ - AVAudioSession *session = [AVAudioSession sharedInstance]; - AVAudioSessionCategory category = nil; - AVAudioSessionCategoryOptions options = nil; - - if([_ignoreSilentSwitch isEqualToString:@"ignore"]) { - category = AVAudioSessionCategoryPlayback; - } else if([_ignoreSilentSwitch isEqualToString:@"obey"]) { - category = AVAudioSessionCategoryAmbient; - } - - if([_mixWithOthers isEqualToString:@"mix"]) { - options = AVAudioSessionCategoryOptionMixWithOthers; - } else if([_mixWithOthers isEqualToString:@"duck"]) { - options = AVAudioSessionCategoryOptionDuckOthers; - } - - if (category != nil && options != nil) { - [session setCategory:category withOptions:options error:nil]; - } else if (category != nil && options == nil) { - [session setCategory:category error:nil]; - } else if (category == nil && options != nil) { - [session setCategory:session.category withOptions:options error:nil]; - } -} - -- (void)setRepeat:(BOOL)repeat { - _repeat = repeat; -} - -- (void)setMediaSelectionTrackForCharacteristic:(AVMediaCharacteristic)characteristic - withCriteria:(NSDictionary *)criteria -{ - NSString *type = criteria[@"type"]; - AVMediaSelectionGroup *group = [_player.currentItem.asset - mediaSelectionGroupForMediaCharacteristic:characteristic]; - AVMediaSelectionOption *mediaOption; - - if ([type isEqualToString:@"disabled"]) { - // Do nothing. We want to ensure option is nil - } else if ([type isEqualToString:@"language"] || [type isEqualToString:@"title"]) { - NSString *value = criteria[@"value"]; - for (int i = 0; i < group.options.count; ++i) { - AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i]; - NSString *optionValue; - if ([type isEqualToString:@"language"]) { - optionValue = [currentOption extendedLanguageTag]; - } else { - optionValue = [[[currentOption commonMetadata] - valueForKey:@"value"] - objectAtIndex:0]; - } - if ([value isEqualToString:optionValue]) { - mediaOption = currentOption; - break; - } - } - //} else if ([type isEqualToString:@"default"]) { - // option = group.defaultOption; */ - } else if ([type isEqualToString:@"index"]) { - if ([criteria[@"value"] isKindOfClass:[NSNumber class]]) { - int index = [criteria[@"value"] intValue]; - if (group.options.count > index) { - mediaOption = [group.options objectAtIndex:index]; - } - } - } else { // default. invalid type or "system" - #if TARGET_OS_TV - // Do noting. Fix for tvOS native audio menu language selector - #else - [_player.currentItem selectMediaOptionAutomaticallyInMediaSelectionGroup:group]; - return; - #endif - } - - #if TARGET_OS_TV - // Do noting. Fix for tvOS native audio menu language selector - #else - // If a match isn't found, option will be nil and text tracks will be disabled - [_player.currentItem selectMediaOption:mediaOption inMediaSelectionGroup:group]; - #endif -} - -- (void)setSelectedAudioTrack:(NSDictionary *)selectedAudioTrack { - _selectedAudioTrack = selectedAudioTrack; - [self setMediaSelectionTrackForCharacteristic:AVMediaCharacteristicAudible - withCriteria:_selectedAudioTrack]; -} - -- (void)setSelectedTextTrack:(NSDictionary *)selectedTextTrack { - _selectedTextTrack = selectedTextTrack; - if (_textTracks) { // sideloaded text tracks - [self setSideloadedText]; - } else { // text tracks included in the HLS playlist - [self setMediaSelectionTrackForCharacteristic:AVMediaCharacteristicLegible - withCriteria:_selectedTextTrack]; - } -} - -- (void) setSideloadedText { - NSString *type = _selectedTextTrack[@"type"]; - NSArray *textTracks = [self getTextTrackInfo]; - - // The first few tracks will be audio & video track - int firstTextIndex = 0; - for (firstTextIndex = 0; firstTextIndex < _player.currentItem.tracks.count; ++firstTextIndex) { - if ([_player.currentItem.tracks[firstTextIndex].assetTrack hasMediaCharacteristic:AVMediaCharacteristicLegible]) { - break; - } - } - - int selectedTrackIndex = RCTVideoUnset; - - if ([type isEqualToString:@"disabled"]) { - // Do nothing. We want to ensure option is nil - } else if ([type isEqualToString:@"language"]) { - NSString *selectedValue = _selectedTextTrack[@"value"]; - for (int i = 0; i < textTracks.count; ++i) { - NSDictionary *currentTextTrack = [textTracks objectAtIndex:i]; - if ([selectedValue isEqualToString:currentTextTrack[@"language"]]) { - selectedTrackIndex = i; - break; - } - } - } else if ([type isEqualToString:@"title"]) { - NSString *selectedValue = _selectedTextTrack[@"value"]; - for (int i = 0; i < textTracks.count; ++i) { - NSDictionary *currentTextTrack = [textTracks objectAtIndex:i]; - if ([selectedValue isEqualToString:currentTextTrack[@"title"]]) { - selectedTrackIndex = i; - break; - } - } - } else if ([type isEqualToString:@"index"]) { - if ([_selectedTextTrack[@"value"] isKindOfClass:[NSNumber class]]) { - int index = [_selectedTextTrack[@"value"] intValue]; - if (textTracks.count > index) { - selectedTrackIndex = index; - } - } - } - - // in the situation that a selected text track is not available (eg. specifies a textTrack not available) - if (![type isEqualToString:@"disabled"] && selectedTrackIndex == RCTVideoUnset) { - CFArrayRef captioningMediaCharacteristics = MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics(kMACaptionAppearanceDomainUser); - NSArray *captionSettings = (__bridge NSArray*)captioningMediaCharacteristics; - if ([captionSettings containsObject:AVMediaCharacteristicTranscribesSpokenDialogForAccessibility]) { - selectedTrackIndex = 0; // If we can't find a match, use the first available track - NSString *systemLanguage = [[NSLocale preferredLanguages] firstObject]; - for (int i = 0; i < textTracks.count; ++i) { - NSDictionary *currentTextTrack = [textTracks objectAtIndex:i]; - if ([systemLanguage isEqualToString:currentTextTrack[@"language"]]) { - selectedTrackIndex = i; - break; - } - } - } - } - - for (int i = firstTextIndex; i < _player.currentItem.tracks.count; ++i) { - BOOL isEnabled = NO; - if (selectedTrackIndex != RCTVideoUnset) { - isEnabled = i == selectedTrackIndex + firstTextIndex; - } - [_player.currentItem.tracks[i] setEnabled:isEnabled]; - } -} - --(void) setStreamingText { - NSString *type = _selectedTextTrack[@"type"]; - AVMediaSelectionGroup *group = [_player.currentItem.asset - mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible]; - AVMediaSelectionOption *mediaOption; - - if ([type isEqualToString:@"disabled"]) { - // Do nothing. We want to ensure option is nil - } else if ([type isEqualToString:@"language"] || [type isEqualToString:@"title"]) { - NSString *value = _selectedTextTrack[@"value"]; - for (int i = 0; i < group.options.count; ++i) { - AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i]; - NSString *optionValue; - if ([type isEqualToString:@"language"]) { - optionValue = [currentOption extendedLanguageTag]; - } else { - optionValue = [[[currentOption commonMetadata] - valueForKey:@"value"] - objectAtIndex:0]; - } - if ([value isEqualToString:optionValue]) { - mediaOption = currentOption; - break; - } - } - //} else if ([type isEqualToString:@"default"]) { - // option = group.defaultOption; */ - } else if ([type isEqualToString:@"index"]) { - if ([_selectedTextTrack[@"value"] isKindOfClass:[NSNumber class]]) { - int index = [_selectedTextTrack[@"value"] intValue]; - if (group.options.count > index) { - mediaOption = [group.options objectAtIndex:index]; - } - } - } else { // default. invalid type or "system" - [_player.currentItem selectMediaOptionAutomaticallyInMediaSelectionGroup:group]; - return; - } - - // If a match isn't found, option will be nil and text tracks will be disabled - [_player.currentItem selectMediaOption:mediaOption inMediaSelectionGroup:group]; -} - -- (void)setTextTracks:(NSArray*) textTracks; -{ - _textTracks = textTracks; - - // in case textTracks was set after selectedTextTrack - if (_selectedTextTrack) [self setSelectedTextTrack:_selectedTextTrack]; -} - -- (NSArray *)getAudioTrackInfo -{ - NSMutableArray *audioTracks = [[NSMutableArray alloc] init]; - AVMediaSelectionGroup *group = [_player.currentItem.asset - mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible]; - for (int i = 0; i < group.options.count; ++i) { - AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i]; - NSString *title = @""; - NSArray *values = [[currentOption commonMetadata] valueForKey:@"value"]; - if (values.count > 0) { - title = [values objectAtIndex:0]; - } - NSString *language = [currentOption extendedLanguageTag] ? [currentOption extendedLanguageTag] : @""; - NSDictionary *audioTrack = @{ - @"index": [NSNumber numberWithInt:i], - @"title": title, - @"language": language - }; - [audioTracks addObject:audioTrack]; - } - return audioTracks; -} - -- (NSArray *)getTextTrackInfo -{ - // if sideloaded, textTracks will already be set - if (_textTracks) return _textTracks; - - // if streaming video, we extract the text tracks - NSMutableArray *textTracks = [[NSMutableArray alloc] init]; - AVMediaSelectionGroup *group = [_player.currentItem.asset - mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible]; - for (int i = 0; i < group.options.count; ++i) { - AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i]; - NSString *title = @""; - NSArray *values = [[currentOption commonMetadata] valueForKey:@"value"]; - if (values.count > 0) { - title = [values objectAtIndex:0]; - } - NSString *language = [currentOption extendedLanguageTag] ? [currentOption extendedLanguageTag] : @""; - NSDictionary *textTrack = @{ - @"index": [NSNumber numberWithInt:i], - @"title": title, - @"language": language - }; - [textTracks addObject:textTrack]; - } - return textTracks; -} - -- (BOOL)getFullscreen -{ - return _fullscreenPlayerPresented; -} - -- (void)setFullscreen:(BOOL) fullscreen { - if( fullscreen && !_fullscreenPlayerPresented && _player ) - { - // Ensure player view controller is not null - if( !_playerViewController ) - { - [self usePlayerViewController]; - } - // Set presentation style to fullscreen - [_playerViewController setModalPresentationStyle:UIModalPresentationFullScreen]; - - // Find the nearest view controller - UIViewController *viewController = [self firstAvailableUIViewController]; - if( !viewController ) - { - UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; - viewController = keyWindow.rootViewController; - if( viewController.childViewControllers.count > 0 ) - { - viewController = viewController.childViewControllers.lastObject; - } - } - if( viewController ) - { - _presentingViewController = viewController; - if(self.onVideoFullscreenPlayerWillPresent) { - self.onVideoFullscreenPlayerWillPresent(@{@"target": self.reactTag}); - } - [viewController presentViewController:_playerViewController animated:true completion:^{ - _playerViewController.showsPlaybackControls = YES; - _fullscreenPlayerPresented = fullscreen; - _playerViewController.autorotate = _fullscreenAutorotate; - if(self.onVideoFullscreenPlayerDidPresent) { - self.onVideoFullscreenPlayerDidPresent(@{@"target": self.reactTag}); - } - }]; - } - } - else if ( !fullscreen && _fullscreenPlayerPresented ) - { - [self videoPlayerViewControllerWillDismiss:_playerViewController]; - [_presentingViewController dismissViewControllerAnimated:true completion:^{ - [self videoPlayerViewControllerDidDismiss:_playerViewController]; - }]; - } -} - -- (void)setFullscreenAutorotate:(BOOL)autorotate { - _fullscreenAutorotate = autorotate; - if (_fullscreenPlayerPresented) { - _playerViewController.autorotate = autorotate; - } -} - -- (void)setFullscreenOrientation:(NSString *)orientation { - _fullscreenOrientation = orientation; - if (_fullscreenPlayerPresented) { - _playerViewController.preferredOrientation = orientation; - } -} - -- (void)usePlayerViewController -{ - if( _player ) - { - if (!_playerViewController) { - _playerViewController = [self createPlayerViewController:_player withPlayerItem:_playerItem]; - } - // to prevent video from being animated when resizeMode is 'cover' - // resize mode must be set before subview is added - [self setResizeMode:_resizeMode]; - - if (_controls) { - UIViewController *viewController = [self reactViewController]; - [viewController addChildViewController:_playerViewController]; - [self addSubview:_playerViewController.view]; - } - - [_playerViewController addObserver:self forKeyPath:readyForDisplayKeyPath options:NSKeyValueObservingOptionNew context:nil]; - - [_playerViewController.contentOverlayView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL]; - } -} - -- (void)usePlayerLayer -{ - if( _player ) - { - _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player]; - _playerLayer.frame = self.bounds; - _playerLayer.needsDisplayOnBoundsChange = YES; - - // to prevent video from being animated when resizeMode is 'cover' - // resize mode must be set before layer is added - [self setResizeMode:_resizeMode]; - [_playerLayer addObserver:self forKeyPath:readyForDisplayKeyPath options:NSKeyValueObservingOptionNew context:nil]; - _playerLayerObserverSet = YES; - - [self.layer addSublayer:_playerLayer]; - self.layer.needsDisplayOnBoundsChange = YES; - #if TARGET_OS_IOS - [self setupPipController]; - #endif - } -} - -- (void)setControls:(BOOL)controls -{ - if( _controls != controls || (!_playerLayer && !_playerViewController) ) - { - _controls = controls; - if( _controls ) - { - [self removePlayerLayer]; - [self usePlayerViewController]; - } - else - { - [_playerViewController.view removeFromSuperview]; - _playerViewController = nil; - [self usePlayerLayer]; - } - } -} - -- (void)setProgressUpdateInterval:(float)progressUpdateInterval -{ - _progressUpdateInterval = progressUpdateInterval; - - if (_timeObserver) { - [self removePlayerTimeObserver]; - [self addPlayerTimeObserver]; - } -} - -- (void)removePlayerLayer -{ - if (_loadingRequest != nil) { - [_loadingRequest finishLoading]; - } - _requestingCertificate = NO; - _requestingCertificateErrored = NO; - [_playerLayer removeFromSuperlayer]; - if (_playerLayerObserverSet) { - [_playerLayer removeObserver:self forKeyPath:readyForDisplayKeyPath]; - _playerLayerObserverSet = NO; - } - _playerLayer = nil; -} - -#pragma mark - RCTVideoPlayerViewControllerDelegate - -- (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController -{ - if (_playerViewController == playerViewController && _fullscreenPlayerPresented && self.onVideoFullscreenPlayerWillDismiss) - { - @try{ - [_playerViewController.contentOverlayView removeObserver:self forKeyPath:@"frame"]; - [_playerViewController removeObserver:self forKeyPath:readyForDisplayKeyPath]; - }@catch(id anException){ - } - self.onVideoFullscreenPlayerWillDismiss(@{@"target": self.reactTag}); - } -} - -- (void)videoPlayerViewControllerDidDismiss:(AVPlayerViewController *)playerViewController -{ - if (_playerViewController == playerViewController && _fullscreenPlayerPresented) - { - _fullscreenPlayerPresented = false; - _presentingViewController = nil; - _playerViewController = nil; - [self applyModifiers]; - if(self.onVideoFullscreenPlayerDidDismiss) { - self.onVideoFullscreenPlayerDidDismiss(@{@"target": self.reactTag}); - } - } -} - -- (void)setFilter:(NSString *)filterName { - _filterName = filterName; - - if (!_filterEnabled) { - return; - } else if ([[_source objectForKey:@"uri"] rangeOfString:@"m3u8"].location != NSNotFound) { - return; // filters don't work for HLS... return - } else if (!_playerItem.asset) { - return; - } - - CIFilter *filter = [CIFilter filterWithName:filterName]; - _playerItem.videoComposition = [AVVideoComposition - videoCompositionWithAsset:_playerItem.asset - applyingCIFiltersWithHandler:^(AVAsynchronousCIImageFilteringRequest *_Nonnull request) { - if (filter == nil) { - [request finishWithImage:request.sourceImage context:nil]; - } else { - CIImage *image = request.sourceImage.imageByClampingToExtent; - [filter setValue:image forKey:kCIInputImageKey]; - CIImage *output = [filter.outputImage imageByCroppingToRect:request.sourceImage.extent]; - [request finishWithImage:output context:nil]; - } - }]; -} - -- (void)setFilterEnabled:(BOOL)filterEnabled { - _filterEnabled = filterEnabled; -} - -#pragma mark - React View Management - -- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex -{ - // We are early in the game and somebody wants to set a subview. - // That can only be in the context of playerViewController. - if( !_controls && !_playerLayer && !_playerViewController ) - { - [self setControls:true]; - } - - if( _controls ) - { - view.frame = self.bounds; - [_playerViewController.contentOverlayView insertSubview:view atIndex:atIndex]; - } - else - { - RCTLogError(@"video cannot have any subviews"); - } - return; -} - -- (void)removeReactSubview:(UIView *)subview -{ - if( _controls ) - { - [subview removeFromSuperview]; - } - else - { - RCTLogError(@"video cannot have any subviews"); - } - return; -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - if( _controls ) - { - _playerViewController.view.frame = self.bounds; - - // also adjust all subviews of contentOverlayView - for (UIView* subview in _playerViewController.contentOverlayView.subviews) { - subview.frame = self.bounds; - } - } - else - { - [CATransaction begin]; - [CATransaction setAnimationDuration:0]; - _playerLayer.frame = self.bounds; - [CATransaction commit]; - } -} - -#pragma mark - Lifecycle - -- (void)removeFromSuperview -{ - [_player pause]; - if (_playbackRateObserverRegistered) { - [_player removeObserver:self forKeyPath:playbackRate context:nil]; - _playbackRateObserverRegistered = NO; - } - if (_isExternalPlaybackActiveObserverRegistered) { - [_player removeObserver:self forKeyPath:externalPlaybackActive context:nil]; - _isExternalPlaybackActiveObserverRegistered = NO; - } - _player = nil; - - [self removePlayerLayer]; - - [_playerViewController.contentOverlayView removeObserver:self forKeyPath:@"frame"]; - [_playerViewController removeObserver:self forKeyPath:readyForDisplayKeyPath]; - [_playerViewController.view removeFromSuperview]; - _playerViewController.rctDelegate = nil; - _playerViewController.player = nil; - _playerViewController = nil; - - [self removePlayerTimeObserver]; - [self removePlayerItemObservers]; - - _eventDispatcher = nil; - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - [super removeFromSuperview]; -} - -#pragma mark - Export - -- (void)save:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { - - AVAsset *asset = _playerItem.asset; - - if (asset != nil) { - - AVAssetExportSession *exportSession = [AVAssetExportSession - exportSessionWithAsset:asset presetName:AVAssetExportPresetHighestQuality]; - - if (exportSession != nil) { - NSString *path = nil; - NSArray *array = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - path = [self generatePathInDirectory:[[self cacheDirectoryPath] stringByAppendingPathComponent:@"Videos"] - withExtension:@".mp4"]; - NSURL *url = [NSURL fileURLWithPath:path]; - exportSession.outputFileType = AVFileTypeMPEG4; - exportSession.outputURL = url; - exportSession.videoComposition = _playerItem.videoComposition; - exportSession.shouldOptimizeForNetworkUse = true; - [exportSession exportAsynchronouslyWithCompletionHandler:^{ - - switch ([exportSession status]) { - case AVAssetExportSessionStatusFailed: - reject(@"ERROR_COULD_NOT_EXPORT_VIDEO", @"Could not export video", exportSession.error); - break; - case AVAssetExportSessionStatusCancelled: - reject(@"ERROR_EXPORT_SESSION_CANCELLED", @"Export session was cancelled", exportSession.error); - break; - default: - resolve(@{@"uri": url.absoluteString}); - break; - } - - }]; - - } else { - - reject(@"ERROR_COULD_NOT_CREATE_EXPORT_SESSION", @"Could not create export session", nil); - - } - - } else { - - reject(@"ERROR_ASSET_NIL", @"Asset is nil", nil); - - } -} - -- (void)setLicenseResult:(NSString *)license { - NSData *respondData = [self base64DataFromBase64String:license]; - if (_loadingRequest != nil && respondData != nil) { - AVAssetResourceLoadingDataRequest *dataRequest = [_loadingRequest dataRequest]; - [dataRequest respondWithData:respondData]; - [_loadingRequest finishLoading]; - } else { - [self setLicenseResultError:@"No data from JS license response"]; - } -} - -- (BOOL)setLicenseResultError:(NSString *)error { - if (_loadingRequest != nil) { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorFromJSPart - userInfo: @{ - NSLocalizedDescriptionKey: error, - NSLocalizedFailureReasonErrorKey: error, - NSLocalizedRecoverySuggestionErrorKey: error - } - ]; - [self finishLoadingWithError:licenseError]; - } - return NO; -} - -- (BOOL)finishLoadingWithError:(NSError *)error { - if (_loadingRequest && error != nil) { - NSError *licenseError = error; - [_loadingRequest finishLoadingWithError:licenseError]; - if (self.onVideoError) { - self.onVideoError(@{@"error": @{@"code": [NSNumber numberWithInteger: error.code], - @"localizedDescription": [error localizedDescription] == nil ? @"" : [error localizedDescription], - @"localizedFailureReason": [error localizedFailureReason] == nil ? @"" : [error localizedFailureReason], - @"localizedRecoverySuggestion": [error localizedRecoverySuggestion] == nil ? @"" : [error localizedRecoverySuggestion], - @"domain": _playerItem.error == nil ? @"RCTVideo" : _playerItem.error.domain}, - @"target": self.reactTag}); - } - } - return NO; -} - -- (BOOL)ensureDirExistsWithPath:(NSString *)path { - BOOL isDir = NO; - NSError *error; - BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]; - if (!(exists && isDir)) { - [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; - if (error) { - return NO; - } - } - return YES; -} - -- (NSString *)generatePathInDirectory:(NSString *)directory withExtension:(NSString *)extension { - NSString *fileName = [[[NSUUID UUID] UUIDString] stringByAppendingString:extension]; - [self ensureDirExistsWithPath:directory]; - return [directory stringByAppendingPathComponent:fileName]; -} - -- (NSString *)cacheDirectoryPath { - NSArray *array = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - return array[0]; -} - -#pragma mark - AVAssetResourceLoaderDelegate - -- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForRenewalOfRequestedResource:(AVAssetResourceRenewalRequest *)renewalRequest { - return [self loadingRequestHandling:renewalRequest]; -} - -- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest { - return [self loadingRequestHandling:loadingRequest]; -} - -- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader -didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest { - NSLog(@"didCancelLoadingRequest"); -} - -- (BOOL)loadingRequestHandling:(AVAssetResourceLoadingRequest *)loadingRequest { - if (self->_requestingCertificate) { - return YES; - } else if (self->_requestingCertificateErrored) { - return NO; - } - _loadingRequest = loadingRequest; - NSURL *url = loadingRequest.request.URL; - if (self->_drm != nil) { - NSString *contentId; - NSString *contentIdOverride = (NSString *)[self->_drm objectForKey:@"contentId"]; - if (contentIdOverride != nil) { - contentId = contentIdOverride; - } else if (self.onGetLicense) { - contentId = url.host; - } else { - contentId = [url.absoluteString stringByReplacingOccurrencesOfString:@"skd://" withString:@""]; - } - NSString *drmType = (NSString *)[self->_drm objectForKey:@"type"]; - if ([drmType isEqualToString:@"fairplay"]) { - NSString *certificateStringUrl = (NSString *)[self->_drm objectForKey:@"certificateUrl"]; - if (certificateStringUrl != nil) { - NSURL *certificateURL = [NSURL URLWithString:[certificateStringUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSData *certificateData = [NSData dataWithContentsOfURL:certificateURL]; - if ([self->_drm objectForKey:@"base64Certificate"]) { - certificateData = [[NSData alloc] initWithBase64EncodedData:certificateData options:NSDataBase64DecodingIgnoreUnknownCharacters]; - } - - if (certificateData != nil) { - NSData *contentIdData; - if(self.onGetLicense) { - contentIdData = [contentId dataUsingEncoding:NSUTF8StringEncoding]; - } else { - contentIdData = [NSData dataWithBytes: [contentId cStringUsingEncoding:NSUTF8StringEncoding] length:[contentId lengthOfBytesUsingEncoding:NSUTF8StringEncoding]]; - } - AVAssetResourceLoadingDataRequest *dataRequest = [loadingRequest dataRequest]; - if (dataRequest != nil) { - NSError *spcError = nil; - NSData *spcData = [loadingRequest streamingContentKeyRequestDataForApp:certificateData contentIdentifier:contentIdData options:nil error:&spcError]; - // Request CKC to the server - NSString *licenseServer = (NSString *)[self->_drm objectForKey:@"licenseServer"]; - if (spcError != nil) { - [self finishLoadingWithError:spcError]; - self->_requestingCertificateErrored = YES; - } - if (spcData != nil) { - if(self.onGetLicense) { - NSString *base64Encoded = [spcData base64EncodedStringWithOptions:0]; - self->_requestingCertificate = YES; - if (licenseServer == nil) { - licenseServer = @""; - } - self.onGetLicense(@{@"licenseUrl": licenseServer, - @"contentId": contentId, - @"spcBase64": base64Encoded, - @"target": self.reactTag} - ); - } else if(licenseServer != nil) { - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; - [request setHTTPMethod:@"POST"]; - [request setURL:[NSURL URLWithString:licenseServer]]; - // HEADERS - NSDictionary *headers = (NSDictionary *)[self->_drm objectForKey:@"headers"]; - if (headers != nil) { - for (NSString *key in headers) { - NSString *value = headers[key]; - [request setValue:value forHTTPHeaderField:key]; - } - } - - if(self.onGetLicense) { - [request setHTTPBody: spcData]; - } else { - NSString *spcEncoded = [spcData base64EncodedStringWithOptions:0]; - NSString *spcUrlEncoded = (NSString *) CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)spcEncoded, NULL, CFSTR("?=&+"), kCFStringEncodingUTF8)); - NSString *post = [NSString stringWithFormat:@"spc=%@&%@", spcUrlEncoded, contentId]; - NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES]; - [request setHTTPBody: postData]; - } - - NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; - NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; - NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; - if (error != nil) { - NSLog(@"Error getting license from %@, HTTP status code %li", url, (long)[httpResponse statusCode]); - [self finishLoadingWithError:error]; - self->_requestingCertificateErrored = YES; - } else { - if([httpResponse statusCode] != 200){ - NSLog(@"Error getting license from %@, HTTP status code %li", url, (long)[httpResponse statusCode]); - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorLicenseRequestNotOk - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining license.", - NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"License server responded with status code %li", (long)[httpResponse statusCode]], - NSLocalizedRecoverySuggestionErrorKey: @"Did you send the correct data to the license Server? Is the server ok?" - } - ]; - [self finishLoadingWithError:licenseError]; - self->_requestingCertificateErrored = YES; - } else if (data != nil) { - if(self.onGetLicense) { - [dataRequest respondWithData:data]; - } else { - NSData *decodedData = [[NSData alloc] initWithBase64EncodedData:data options:0]; - [dataRequest respondWithData:decodedData]; - } - [loadingRequest finishLoading]; - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoDataFromLicenseRequest - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining DRM license.", - NSLocalizedFailureReasonErrorKey: @"No data received from the license server.", - NSLocalizedRecoverySuggestionErrorKey: @"Is the licenseServer ok?." - } - ]; - [self finishLoadingWithError:licenseError]; - self->_requestingCertificateErrored = YES; - } - - } - }]; - [postDataTask resume]; - } - - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoSPC - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining license.", - NSLocalizedFailureReasonErrorKey: @"No spc received.", - NSLocalizedRecoverySuggestionErrorKey: @"Check your DRM config." - } - ]; - [self finishLoadingWithError:licenseError]; - self->_requestingCertificateErrored = YES; - } - - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoDataRequest - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining DRM license.", - NSLocalizedFailureReasonErrorKey: @"No dataRequest found.", - NSLocalizedRecoverySuggestionErrorKey: @"Check your DRM configuration." - } - ]; - [self finishLoadingWithError:licenseError]; - self->_requestingCertificateErrored = YES; - } - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoCertificateData - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining DRM license.", - NSLocalizedFailureReasonErrorKey: @"No certificate data obtained from the specificied url.", - NSLocalizedRecoverySuggestionErrorKey: @"Have you specified a valid 'certificateUrl'?" - } - ]; - [self finishLoadingWithError:licenseError]; - self->_requestingCertificateErrored = YES; - } - }); - return YES; - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoCertificateURL - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining DRM License.", - NSLocalizedFailureReasonErrorKey: @"No certificate URL has been found.", - NSLocalizedRecoverySuggestionErrorKey: @"Did you specified the prop certificateUrl?" - } - ]; - return [self finishLoadingWithError:licenseError]; - } - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoFairplayDRM - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining DRM license.", - NSLocalizedFailureReasonErrorKey: @"Not a valid DRM Scheme has found", - NSLocalizedRecoverySuggestionErrorKey: @"Have you specified the 'drm' 'type' as fairplay?" - } - ]; - return [self finishLoadingWithError:licenseError]; - } - - } else { - NSError *licenseError = [NSError errorWithDomain: @"RCTVideo" - code: RCTVideoErrorNoDRMData - userInfo: @{ - NSLocalizedDescriptionKey: @"Error obtaining DRM license.", - NSLocalizedFailureReasonErrorKey: @"No drm object found.", - NSLocalizedRecoverySuggestionErrorKey: @"Have you specified the 'drm' prop?" - } - ]; - return [self finishLoadingWithError:licenseError]; - } - - - return NO; -} - -- (NSData *)base64DataFromBase64String: (NSString *)base64String { - if (base64String != nil) { - // NSData from the Base64 encoded str - NSData *base64Data = [[NSData alloc] initWithBase64EncodedString:base64String options:NSASCIIStringEncoding]; - return base64Data; - } - return nil; -} -#pragma mark - Picture in Picture - -#if TARGET_OS_IOS -- (void)pictureInPictureControllerDidStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { - if (self.onPictureInPictureStatusChanged) { - self.onPictureInPictureStatusChanged(@{ - @"isActive": [NSNumber numberWithBool:false] - }); - } -} - -- (void)pictureInPictureControllerDidStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { - if (self.onPictureInPictureStatusChanged) { - self.onPictureInPictureStatusChanged(@{ - @"isActive": [NSNumber numberWithBool:true] - }); - } -} - -- (void)pictureInPictureControllerWillStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { - -} - -- (void)pictureInPictureControllerWillStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { - -} - -- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController failedToStartPictureInPictureWithError:(NSError *)error { - -} - -- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL))completionHandler { - NSAssert(_restoreUserInterfaceForPIPStopCompletionHandler == NULL, @"restoreUserInterfaceForPIPStopCompletionHandler was not called after picture in picture was exited."); - if (self.onRestoreUserInterfaceForPictureInPictureStop) { - self.onRestoreUserInterfaceForPictureInPictureStop(@{}); - } - _restoreUserInterfaceForPIPStopCompletionHandler = completionHandler; -} -#endif - -@end diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift new file mode 100644 index 00000000..76701229 --- /dev/null +++ b/ios/Video/RCTVideo.swift @@ -0,0 +1,1061 @@ +import AVFoundation +import AVKit +import Foundation +import React +import Promises + +class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverHandler { + + private var _player:AVPlayer? + private var _playerItem:AVPlayerItem? + private var _source:VideoSource? + private var _playerBufferEmpty:Bool = true + private var _playerLayer:AVPlayerLayer? + + private var _playerViewController:RCTVideoPlayerViewController? + private var _videoURL:NSURL? + + /* DRM */ + private var _drm:DRMParams? + + private var _localSourceEncryptionKeyScheme:String? + + /* Required to publish events */ + private var _eventDispatcher:RCTEventDispatcher? + private var _videoLoadStarted:Bool = false + + private var _pendingSeek:Bool = false + private var _pendingSeekTime:Float = 0.0 + private var _lastSeekTime:Float = 0.0 + + /* For sending videoProgress events */ + private var _controls:Bool = false + + /* Keep track of any modifiers, need to be applied after each play */ + private var _volume:Float = 1.0 + private var _rate:Float = 1.0 + private var _maxBitRate:Float? + + private var _automaticallyWaitsToMinimizeStalling:Bool = true + private var _muted:Bool = false + private var _paused:Bool = false + private var _repeat:Bool = false + private var _allowsExternalPlayback:Bool = true + private var _textTracks:[TextTrack]? + private var _selectedTextTrackCriteria:SelectedTrackCriteria? + private var _selectedAudioTrackCriteria:SelectedTrackCriteria? + private var _playbackStalled:Bool = false + private var _playInBackground:Bool = false + private var _preventsDisplaySleepDuringVideoPlayback:Bool = true + private var _preferredForwardBufferDuration:Float = 0.0 + private var _playWhenInactive:Bool = false + private var _ignoreSilentSwitch:String! = "inherit" // inherit, ignore, obey + private var _mixWithOthers:String! = "inherit" // inherit, mix, duck + private var _resizeMode:String! = "AVLayerVideoGravityResizeAspectFill" + private var _fullscreen:Bool = false + private var _fullscreenAutorotate:Bool = true + private var _fullscreenOrientation:String! = "all" + private var _fullscreenPlayerPresented:Bool = false + private var _filterName:String! + private var _filterEnabled:Bool = false + private var _presentingViewController:UIViewController? + + private var _resouceLoaderDelegate: RCTResourceLoaderDelegate? + private var _playerObserver: RCTPlayerObserver = RCTPlayerObserver() + +#if canImport(RCTVideoCache) + private let _videoCache:RCTVideoCachingHandler = RCTVideoCachingHandler() +#endif + +#if TARGET_OS_IOS + private let _pip:RCTPictureInPicture = RCTPictureInPicture(self.onPictureInPictureStatusChanged, self.onRestoreUserInterfaceForPictureInPictureStop) +#endif + + // Events + @objc var onVideoLoadStart: RCTDirectEventBlock? + @objc var onVideoLoad: RCTDirectEventBlock? + @objc var onVideoBuffer: RCTDirectEventBlock? + @objc var onVideoError: RCTDirectEventBlock? + @objc var onVideoProgress: RCTDirectEventBlock? + @objc var onBandwidthUpdate: RCTDirectEventBlock? + @objc var onVideoSeek: RCTDirectEventBlock? + @objc var onVideoEnd: RCTDirectEventBlock? + @objc var onTimedMetadata: RCTDirectEventBlock? + @objc var onVideoAudioBecomingNoisy: RCTDirectEventBlock? + @objc var onVideoFullscreenPlayerWillPresent: RCTDirectEventBlock? + @objc var onVideoFullscreenPlayerDidPresent: RCTDirectEventBlock? + @objc var onVideoFullscreenPlayerWillDismiss: RCTDirectEventBlock? + @objc var onVideoFullscreenPlayerDidDismiss: RCTDirectEventBlock? + @objc var onReadyForDisplay: RCTDirectEventBlock? + @objc var onPlaybackStalled: RCTDirectEventBlock? + @objc var onPlaybackResume: RCTDirectEventBlock? + @objc var onPlaybackRateChange: RCTDirectEventBlock? + @objc var onVideoExternalPlaybackChange: RCTDirectEventBlock? + @objc var onPictureInPictureStatusChanged: RCTDirectEventBlock? + @objc var onRestoreUserInterfaceForPictureInPictureStop: RCTDirectEventBlock? + @objc var onGetLicense: RCTDirectEventBlock? + + init(eventDispatcher:RCTEventDispatcher!) { + super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) + + _eventDispatcher = eventDispatcher + + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationWillResignActive(notification:)), + name: UIApplication.willResignActiveNotification, + object: nil + ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationDidEnterBackground(notification:)), + name: UIApplication.didEnterBackgroundNotification, + object: nil + ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationWillEnterForeground(notification:)), + name: UIApplication.willEnterForegroundNotification, + object: nil + ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(audioRouteChanged(notification:)), + name: AVAudioSession.routeChangeNotification, + object: nil + ) + _playerObserver._handlers = self +#if canImport(RCTVideoCache) + _videoCache.playerItemPrepareText = playerItemPrepareText +#endif + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + deinit { + NotificationCenter.default.removeObserver(self) + self.removePlayerLayer() + _playerObserver.clearPlayer() + } + + // MARK: - App lifecycle handlers + + @objc func applicationWillResignActive(notification:NSNotification!) { + if _playInBackground || _playWhenInactive || _paused {return} + + _player?.pause() + _player?.rate = 0.0 + } + + @objc func applicationDidEnterBackground(notification:NSNotification!) { + if _playInBackground { + // Needed to play sound in background. See https://developer.apple.com/library/ios/qa/qa1668/_index.html + _playerLayer?.player = nil + _playerViewController?.player = nil + } + } + + @objc func applicationWillEnterForeground(notification:NSNotification!) { + self.applyModifiers() + if _playInBackground { + _playerLayer?.player = _player + _playerViewController?.player = _player + } + } + + // MARK: - Audio events + + @objc func audioRouteChanged(notification:NSNotification!) { + if let userInfo = notification.userInfo { + let reason:AVAudioSession.RouteChangeReason! = userInfo[AVAudioSessionRouteChangeReasonKey] as? AVAudioSession.RouteChangeReason + // let previousRoute:NSNumber! = userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? NSNumber + if reason == .oldDeviceUnavailable, let onVideoAudioBecomingNoisy = onVideoAudioBecomingNoisy { + onVideoAudioBecomingNoisy(["target": reactTag as Any]) + } + } + } + + // MARK: - Progress + + func sendProgressUpdate() { + if let video = _player?.currentItem, + video == nil || video.status != AVPlayerItem.Status.readyToPlay { + return + } + + let playerDuration:CMTime = RCTVideoUtils.playerItemDuration(_player) + if CMTIME_IS_INVALID(playerDuration) { + return + } + + let currentTime = _player?.currentTime() + let currentPlaybackTime = _player?.currentItem?.currentDate() + let duration = CMTimeGetSeconds(playerDuration) + let currentTimeSecs = CMTimeGetSeconds(currentTime ?? .zero) + + NotificationCenter.default.post(name: NSNotification.Name("RCTVideo_progress"), object: nil, userInfo: [ + "progress": NSNumber(value: currentTimeSecs / duration) + ]) + + if currentTimeSecs >= 0 { + onVideoProgress?([ + "currentTime": NSNumber(value: Float(currentTimeSecs)), + "playableDuration": RCTVideoUtils.calculatePlayableDuration(_player), + "atValue": NSNumber(value: currentTime?.value ?? .zero), + "currentPlaybackTime": NSNumber(value: NSNumber(value: floor(currentPlaybackTime?.timeIntervalSince1970 ?? 0 * 1000)).int64Value), + "target": reactTag, + "seekableDuration": RCTVideoUtils.calculateSeekableDuration(_player) + ]) + } + } + + // MARK: - Player and source + + @objc + func setSrc(_ source:NSDictionary!) { + _source = VideoSource(source) + removePlayerLayer() + _playerObserver.player = nil + _playerObserver.playerItem = nil + + // perform on next run loop, otherwise other passed react-props may not be set + RCTVideoUtils.delay() + .then{ [weak self] in + guard let self = self else {throw NSError(domain: "", code: 0, userInfo: nil)} + guard let source = self._source, + let assetResult = RCTVideoUtils.prepareAsset(source: source), + let asset = assetResult.asset, + let assetOptions = assetResult.assetOptions else { + DebugLog("Could not find video URL in source '\(self._source)'") + throw NSError(domain: "", code: 0, userInfo: nil) + } + +#if canImport(RCTVideoCache) + if self._videoCache.shouldCache(source:source, textTracks:self._textTracks) { + return self._videoCache.playerItemForSourceUsingCache(uri: source.uri, assetOptions:assetOptions) + } +#endif + + if self._drm != nil || self._localSourceEncryptionKeyScheme != nil { + self._resouceLoaderDelegate = RCTResourceLoaderDelegate( + asset: asset, + drm: self._drm, + localSourceEncryptionKeyScheme: self._localSourceEncryptionKeyScheme, + onVideoError: self.onVideoError, + onGetLicense: self.onGetLicense, + reactTag: self.reactTag + ) + } + return Promise{self.playerItemPrepareText(asset: asset, assetOptions:assetOptions)} + }.then{[weak self] (playerItem:AVPlayerItem!) in + guard let self = self else {throw NSError(domain: "", code: 0, userInfo: nil)} + + self._player?.pause() + self._playerItem = playerItem + self._playerObserver.playerItem = self._playerItem + self.setPreferredForwardBufferDuration(self._preferredForwardBufferDuration) + self.setFilter(self._filterName) + if let maxBitRate = self._maxBitRate { + self._playerItem?.preferredPeakBitRate = Double(maxBitRate) + } + + self._player = AVPlayer(playerItem: self._playerItem) + self._playerObserver.player = self._player + self.applyModifiers() + self._player?.actionAtItemEnd = .none + + if #available(iOS 10.0, *) { + self.setAutomaticallyWaitsToMinimizeStalling(self._automaticallyWaitsToMinimizeStalling) + } + + //Perform on next run loop, otherwise onVideoLoadStart is nil + self.onVideoLoadStart?([ + "src": [ + "uri": self._source?.uri ?? NSNull(), + "type": self._source?.type ?? NSNull(), + "isNetwork": NSNumber(value: self._source?.isNetwork ?? false) + ], + "drm": self._drm?.json ?? NSNull(), + "target": self.reactTag + ]) + }.catch{_ in } + _videoLoadStarted = true + } + + @objc + func setDrm(_ drm:NSDictionary) { + _drm = DRMParams(drm) + } + + @objc + func setLocalSourceEncryptionKeyScheme(_ keyScheme:String) { + _localSourceEncryptionKeyScheme = keyScheme + } + + func playerItemPrepareText(asset:AVAsset!, assetOptions:NSDictionary?) -> AVPlayerItem { + if (_textTracks == nil) || _textTracks?.count==0 { + return AVPlayerItem(asset: asset) + } + + // AVPlayer can't airplay AVMutableCompositions + _allowsExternalPlayback = false + let mixComposition = RCTVideoUtils.generateMixComposition(asset) + let validTextTracks = RCTVideoUtils.getValidTextTracks( + asset:asset, + assetOptions:assetOptions, + mixComposition:mixComposition, + textTracks:_textTracks) + if validTextTracks.count != _textTracks?.count { + setTextTracks(validTextTracks) + } + + return AVPlayerItem(asset: mixComposition) + } + + // MARK: - Prop setters + + @objc + func setResizeMode(_ mode: String?) { + if _controls { + _playerViewController?.videoGravity = AVLayerVideoGravity(rawValue: mode ?? "") + } else { + _playerLayer?.videoGravity = AVLayerVideoGravity(rawValue: mode ?? "") + } + _resizeMode = mode + } + + @objc + func setPlayInBackground(_ playInBackground:Bool) { + _playInBackground = playInBackground + } + + @objc + func setPreventsDisplaySleepDuringVideoPlayback(_ preventsDisplaySleepDuringVideoPlayback:Bool) { + _preventsDisplaySleepDuringVideoPlayback = preventsDisplaySleepDuringVideoPlayback + self.applyModifiers() + } + + @objc + func setAllowsExternalPlayback(_ allowsExternalPlayback:Bool) { + _allowsExternalPlayback = allowsExternalPlayback + _player?.allowsExternalPlayback = _allowsExternalPlayback + } + + @objc + func setPlayWhenInactive(_ playWhenInactive:Bool) { + _playWhenInactive = playWhenInactive + } + + @objc + func setPictureInPicture(_ pictureInPicture:Bool) { +#if TARGET_OS_IOS + _pip.setPictureInPicture(pictureInPicture) +#endif + } + + @objc + func setRestoreUserInterfaceForPIPStopCompletionHandler(_ restore:Bool) { +#if TARGET_OS_IOS + _pip.setRestoreUserInterfaceForPIPStopCompletionHandler(restore) +#endif + } + + @objc + func setIgnoreSilentSwitch(_ ignoreSilentSwitch:String?) { + _ignoreSilentSwitch = ignoreSilentSwitch + RCTPlayerOperations.configureAudio(ignoreSilentSwitch:_ignoreSilentSwitch, mixWithOthers:_mixWithOthers) + applyModifiers() + } + + @objc + func setMixWithOthers(_ mixWithOthers:String?) { + _mixWithOthers = mixWithOthers + applyModifiers() + } + + @objc + func setPaused(_ paused:Bool) { + if paused { + _player?.pause() + _player?.rate = 0.0 + } else { + RCTPlayerOperations.configureAudio(ignoreSilentSwitch:_ignoreSilentSwitch, mixWithOthers:_mixWithOthers) + + if #available(iOS 10.0, *), !_automaticallyWaitsToMinimizeStalling { + _player?.playImmediately(atRate: _rate) + } else { + _player?.play() + _player?.rate = _rate + } + _player?.rate = _rate + } + + _paused = paused + } + + @objc + func setCurrentTime(_ currentTime:Float) { + let info:NSDictionary = [ + "time": NSNumber(value: currentTime), + "tolerance": NSNumber(value: 100) + ] + setSeek(info) + } + + @objc + func setSeek(_ info:NSDictionary!) { + let seekTime:NSNumber! = info["time"] as! NSNumber + let seekTolerance:NSNumber! = info["tolerance"] as! NSNumber + let item:AVPlayerItem? = _player?.currentItem + guard item != nil, let player = _player, let item = item, item.status == AVPlayerItem.Status.readyToPlay else { + _pendingSeek = true + _pendingSeekTime = seekTime.floatValue + return + } + let wasPaused = _paused + + RCTPlayerOperations.seek( + player:player, + playerItem:item, + paused:wasPaused, + seekTime:seekTime.floatValue, + seekTolerance:seekTolerance.floatValue) + .then{ [weak self] (finished:Bool) in + guard let self = self else { return } + + self._playerObserver.addTimeObserverIfNotSet() + if !wasPaused { + self.setPaused(false) + } + self.onVideoSeek?(["currentTime": NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))), + "seekTime": seekTime, + "target": self.reactTag]) + }.catch{_ in } + + _pendingSeek = false + } + + @objc + func setRate(_ rate:Float) { + _rate = rate + applyModifiers() + } + + @objc + func setMuted(_ muted:Bool) { + _muted = muted + applyModifiers() + } + + @objc + func setVolume(_ volume:Float) { + _volume = volume + applyModifiers() + } + + @objc + func setMaxBitRate(_ maxBitRate:Float) { + _maxBitRate = maxBitRate + _playerItem?.preferredPeakBitRate = Double(maxBitRate) + } + + @objc + func setPreferredForwardBufferDuration(_ preferredForwardBufferDuration:Float) { + _preferredForwardBufferDuration = preferredForwardBufferDuration + if #available(iOS 10.0, *) { + _playerItem?.preferredForwardBufferDuration = TimeInterval(preferredForwardBufferDuration) + } else { + // Fallback on earlier versions + } + } + + @objc + func setAutomaticallyWaitsToMinimizeStalling(_ waits:Bool) { + _automaticallyWaitsToMinimizeStalling = waits + if #available(iOS 10.0, *) { + _player?.automaticallyWaitsToMinimizeStalling = waits + } else { + // Fallback on earlier versions + } + } + + + func applyModifiers() { + if _muted { + if !_controls { + _player?.volume = 0 + } + _player?.isMuted = true + } else { + _player?.volume = _volume + _player?.isMuted = false + } + + if #available(iOS 12.0, *) { + _player?.preventsDisplaySleepDuringVideoPlayback = _preventsDisplaySleepDuringVideoPlayback + } else { + // Fallback on earlier versions + } + + if let _maxBitRate = _maxBitRate { + setMaxBitRate(_maxBitRate) + } + + setSelectedAudioTrack(_selectedAudioTrackCriteria) + setSelectedTextTrack(_selectedTextTrackCriteria) + setResizeMode(_resizeMode) + setRepeat(_repeat) + setControls(_controls) + setPaused(_paused) + setAllowsExternalPlayback(_allowsExternalPlayback) + } + + @objc + func setRepeat(_ `repeat`: Bool) { + _repeat = `repeat` + } + + + + @objc + func setSelectedAudioTrack(_ selectedAudioTrack:NSDictionary?) { + setSelectedAudioTrack(SelectedTrackCriteria(selectedAudioTrack)) + } + + func setSelectedAudioTrack(_ selectedAudioTrack:SelectedTrackCriteria?) { + _selectedAudioTrackCriteria = selectedAudioTrack + RCTPlayerOperations.setMediaSelectionTrackForCharacteristic(player:_player, characteristic: AVMediaCharacteristic.audible, + criteria:_selectedAudioTrackCriteria) + } + + @objc + func setSelectedTextTrack(_ selectedTextTrack:NSDictionary?) { + setSelectedTextTrack(SelectedTrackCriteria(selectedTextTrack)) + } + + func setSelectedTextTrack(_ selectedTextTrack:SelectedTrackCriteria?) { + _selectedTextTrackCriteria = selectedTextTrack + if (_textTracks != nil) { // sideloaded text tracks + RCTPlayerOperations.setSideloadedText(player:_player, textTracks:_textTracks, criteria:_selectedTextTrackCriteria) + } else { // text tracks included in the HLS playlist + RCTPlayerOperations.setMediaSelectionTrackForCharacteristic(player:_player, characteristic: AVMediaCharacteristic.legible, + criteria:_selectedTextTrackCriteria) + } + } + + @objc + func setTextTracks(_ textTracks:[NSDictionary]?) { + setTextTracks(textTracks?.map { TextTrack($0) }) + } + + func setTextTracks(_ textTracks:[TextTrack]?) { + _textTracks = textTracks + + // in case textTracks was set after selectedTextTrack + if (_selectedTextTrackCriteria != nil) {setSelectedTextTrack(_selectedTextTrackCriteria)} + } + + @objc + func setFullscreen(_ fullscreen:Bool) { + if fullscreen && !_fullscreenPlayerPresented && _player != nil { + // Ensure player view controller is not null + if _playerViewController == nil { + self.usePlayerViewController() + } + + // Set presentation style to fullscreen + _playerViewController?.modalPresentationStyle = .fullScreen + + // Find the nearest view controller + var viewController:UIViewController! = self.firstAvailableUIViewController() + if (viewController == nil) { + let keyWindow:UIWindow! = UIApplication.shared.keyWindow + viewController = keyWindow.rootViewController + if viewController.children.count > 0 + { + viewController = viewController.children.last + } + } + if viewController != nil { + _presentingViewController = viewController + + self.onVideoFullscreenPlayerWillPresent?(["target": reactTag as Any]) + + viewController.present(viewController, animated:true, completion:{ + self._playerViewController?.showsPlaybackControls = true + self._fullscreenPlayerPresented = fullscreen + self._playerViewController?.autorotate = self._fullscreenAutorotate + + self.onVideoFullscreenPlayerDidPresent?(["target": self.reactTag]) + + }) + } + } else if !fullscreen && _fullscreenPlayerPresented, let _playerViewController = _playerViewController { + self.videoPlayerViewControllerWillDismiss(playerViewController: _playerViewController) + _presentingViewController?.dismiss(animated: true, completion:{ + self.videoPlayerViewControllerDidDismiss(playerViewController: _playerViewController) + }) + } + } + + @objc + func setFullscreenAutorotate(_ autorotate:Bool) { + _fullscreenAutorotate = autorotate + if _fullscreenPlayerPresented { + _playerViewController?.autorotate = autorotate + } + } + + @objc + func setFullscreenOrientation(_ orientation:String?) { + _fullscreenOrientation = orientation + if _fullscreenPlayerPresented { + _playerViewController?.preferredOrientation = orientation + } + } + + func usePlayerViewController() { + guard let _player = _player, let _playerItem = _playerItem else { return } + + if _playerViewController == nil { + _playerViewController = createPlayerViewController(player:_player, withPlayerItem:_playerItem) + } + // to prevent video from being animated when resizeMode is 'cover' + // resize mode must be set before subview is added + setResizeMode(_resizeMode) + + guard let _playerViewController = _playerViewController else { return } + + if _controls { + let viewController:UIViewController! = self.reactViewController() + viewController.addChild(_playerViewController) + self.addSubview(_playerViewController.view) + } + + _playerObserver.playerViewController = _playerViewController + } + + func createPlayerViewController(player:AVPlayer, withPlayerItem playerItem:AVPlayerItem) -> RCTVideoPlayerViewController { + let viewController = RCTVideoPlayerViewController() + viewController.showsPlaybackControls = true + viewController.rctDelegate = self + viewController.preferredOrientation = _fullscreenOrientation + + viewController.view.frame = self.bounds + viewController.player = player + return viewController + } + + func usePlayerLayer() { + if let _player = _player { + _playerLayer = AVPlayerLayer(player: _player) + _playerLayer?.frame = self.bounds + _playerLayer?.needsDisplayOnBoundsChange = true + + // to prevent video from being animated when resizeMode is 'cover' + // resize mode must be set before layer is added + setResizeMode(_resizeMode) + _playerObserver.playerLayer = _playerLayer + + if let _playerLayer = _playerLayer { + self.layer.addSublayer(_playerLayer) + } + self.layer.needsDisplayOnBoundsChange = true +#if TARGET_OS_IOS + _pip.setupPipController(_playerLayer) +#endif + } + } + + @objc + func setControls(_ controls:Bool) { + if _controls != controls || ((_playerLayer == nil) && (_playerViewController == nil)) + { + _controls = controls + if _controls + { + self.removePlayerLayer() + self.usePlayerViewController() + } + else + { + _playerViewController?.view.removeFromSuperview() + _playerViewController = nil + _playerObserver.playerViewController = nil + self.usePlayerLayer() + } + } + } + + @objc + func setProgressUpdateInterval(_ progressUpdateInterval:Float) { + _playerObserver.replaceTimeObserverIfSet(Float64(progressUpdateInterval)) + } + + func removePlayerLayer() { + _resouceLoaderDelegate = nil + _playerLayer?.removeFromSuperlayer() + _playerLayer = nil + _playerObserver.playerLayer = nil + } + + // MARK: - RCTVideoPlayerViewControllerDelegate + + func videoPlayerViewControllerWillDismiss(playerViewController:AVPlayerViewController) { + if _playerViewController == playerViewController && _fullscreenPlayerPresented, let onVideoFullscreenPlayerWillDismiss = onVideoFullscreenPlayerWillDismiss { + _playerObserver.removePlayerViewControllerObservers() + onVideoFullscreenPlayerWillDismiss(["target": reactTag as Any]) + } + } + + + func videoPlayerViewControllerDidDismiss(playerViewController:AVPlayerViewController) { + if _playerViewController == playerViewController && _fullscreenPlayerPresented { + _fullscreenPlayerPresented = false + _presentingViewController = nil + _playerViewController = nil + _playerObserver.playerViewController = nil + self.applyModifiers() + + onVideoFullscreenPlayerDidDismiss?(["target": reactTag as Any]) + } + } + + @objc + func setFilter(_ filterName:String!) { + _filterName = filterName + + if !_filterEnabled { + return + } else if let uri = _source?.uri, uri.contains("m3u8") { + return // filters don't work for HLS... return + } else if _playerItem?.asset == nil { + return + } + + let filter:CIFilter! = CIFilter(name: filterName) + if #available(iOS 9.0, *), let _playerItem = _playerItem { + self._playerItem?.videoComposition = AVVideoComposition( + asset: _playerItem.asset, + applyingCIFiltersWithHandler: { (request:AVAsynchronousCIImageFilteringRequest) in + if filter == nil { + request.finish(with: request.sourceImage, context:nil) + } else { + let image:CIImage! = request.sourceImage.clampedToExtent() + filter.setValue(image, forKey:kCIInputImageKey) + let output:CIImage! = filter.outputImage?.cropped(to: request.sourceImage.extent) + request.finish(with: output, context:nil) + } + }) + } else { + // Fallback on earlier versions + } + } + + @objc + func setFilterEnabled(_ filterEnabled:Bool) { + _filterEnabled = filterEnabled + } + + // MARK: - React View Management + + func insertReactSubview(view:UIView!, atIndex:Int) { + // We are early in the game and somebody wants to set a subview. + // That can only be in the context of playerViewController. + if !_controls && (_playerLayer == nil) && (_playerViewController == nil) { + setControls(true) + } + + if _controls { + view.frame = self.bounds + _playerViewController?.contentOverlayView?.insertSubview(view, at:atIndex) + } else { + RCTLogError("video cannot have any subviews") + } + return + } + + func removeReactSubview(subview:UIView!) { + if _controls { + subview.removeFromSuperview() + } else { + RCTLog("video cannot have any subviews") + } + return + } + + override func layoutSubviews() { + super.layoutSubviews() + if _controls, let _playerViewController = _playerViewController { + _playerViewController.view.frame = bounds + + // also adjust all subviews of contentOverlayView + for subview in _playerViewController.contentOverlayView?.subviews ?? [] { + subview.frame = bounds + } + } else { + CATransaction.begin() + CATransaction.setAnimationDuration(0) + _playerLayer?.frame = bounds + CATransaction.commit() + } + } + + // MARK: - Lifecycle + + override func removeFromSuperview() { + _player?.pause() + _player = nil + _playerObserver.clearPlayer() + + self.removePlayerLayer() + + if let _playerViewController = _playerViewController { + _playerViewController.view.removeFromSuperview() + _playerViewController.rctDelegate = nil + _playerViewController.player = nil + self._playerViewController = nil + _playerObserver.playerViewController = nil + } + + _eventDispatcher = nil + NotificationCenter.default.removeObserver(self) + + super.removeFromSuperview() + } + + // MARK: - Export + + @objc + func save(options:NSDictionary!, resolve: @escaping RCTPromiseResolveBlock, reject:@escaping RCTPromiseRejectBlock) { + RCTVideoSave.save( + options:options, + resolve:resolve, + reject:reject, + playerItem:_playerItem + ) + } + + func setLicenseResult(_ license:String!) { + _resouceLoaderDelegate?.setLicenseResult(license) + } + + func setLicenseResultError(_ error:String!) { + _resouceLoaderDelegate?.setLicenseResultError(error) + } + + // MARK: - RCTPlayerObserverHandler + + func handleTimeUpdate(time:CMTime) { + sendProgressUpdate() + } + + func handleReadyForDisplay(changeObject: Any, change:NSKeyValueObservedChange) { + onReadyForDisplay?([ + "target": reactTag + ]) + } + + // When timeMetadata is read the event onTimedMetadata is triggered + func handleTimeMetadataChange(playerItem:AVPlayerItem, change:NSKeyValueObservedChange<[AVMetadataItem]?>) { + guard let newValue = change.newValue, let _items = newValue, _items.count > 0 else { + return + } + + var metadata: [[String:String?]?] = [] + for item in _items { + let value = item.value as? String + let identifier = item.identifier?.rawValue + + if let value = value { + metadata.append(["value":value, "identifier":identifier]) + } + } + + onTimedMetadata?([ + "target": reactTag, + "metadata": metadata + ]) + } + + // Handle player item status change. + func handlePlayerItemStatusChange(playerItem:AVPlayerItem, change:NSKeyValueObservedChange) { + guard let _playerItem = _playerItem else { + return + } + + if _playerItem.status == .readyToPlay { + handleReadyToPlay() + } else if _playerItem.status == .failed { + handlePlaybackFailed() + } + } + + func handleReadyToPlay() { + guard let _playerItem = _playerItem else { return } + var duration:Float = Float(CMTimeGetSeconds(_playerItem.asset.duration)) + + if duration.isNaN { + duration = 0.0 + } + + var width: Float? = nil + var height: Float? = nil + var orientation = "undefined" + + if _playerItem.asset.tracks(withMediaType: AVMediaType.video).count > 0 { + let videoTrack = _playerItem.asset.tracks(withMediaType: .video)[0] + width = Float(videoTrack.naturalSize.width) + height = Float(videoTrack.naturalSize.height) + let preferredTransform = videoTrack.preferredTransform + + if (videoTrack.naturalSize.width == preferredTransform.tx + && videoTrack.naturalSize.height == preferredTransform.ty) + || (preferredTransform.tx == 0 && preferredTransform.ty == 0) + { + orientation = "landscape" + } else { + orientation = "portrait" + } + } else if _playerItem.presentationSize.height != 0.0 { + width = Float(_playerItem.presentationSize.width) + height = Float(_playerItem.presentationSize.height) + orientation = _playerItem.presentationSize.width > _playerItem.presentationSize.height ? "landscape" : "portrait" + } + + if _pendingSeek { + setCurrentTime(_pendingSeekTime) + _pendingSeek = false + } + + if _videoLoadStarted { + onVideoLoad?(["duration": NSNumber(value: duration), + "currentTime": NSNumber(value: Float(CMTimeGetSeconds(_playerItem.currentTime()))), + "canPlayReverse": NSNumber(value: _playerItem.canPlayReverse), + "canPlayFastForward": NSNumber(value: _playerItem.canPlayFastForward), + "canPlaySlowForward": NSNumber(value: _playerItem.canPlaySlowForward), + "canPlaySlowReverse": NSNumber(value: _playerItem.canPlaySlowReverse), + "canStepBackward": NSNumber(value: _playerItem.canStepBackward), + "canStepForward": NSNumber(value: _playerItem.canStepForward), + "naturalSize": [ + "width": width != nil ? NSNumber(value: width!) : "undefinded", + "height": width != nil ? NSNumber(value: height!) : "undefinded", + "orientation": orientation + ], + "audioTracks": RCTVideoUtils.getAudioTrackInfo(_player), + "textTracks": _textTracks ?? RCTVideoUtils.getTextTrackInfo(_player), + "target": reactTag as Any]) + } + _videoLoadStarted = false + _playerObserver.attachPlayerEventListeners() + applyModifiers() + } + + func handlePlaybackFailed() { + guard let _playerItem = _playerItem else { return } + onVideoError?( + [ + "error": [ + "code": NSNumber(value: (_playerItem.error! as NSError).code), + "localizedDescription": _playerItem.error?.localizedDescription == nil ? "" : _playerItem.error?.localizedDescription, + "localizedFailureReason": ((_playerItem.error! as NSError).localizedFailureReason == nil ? "" : (_playerItem.error! as NSError).localizedFailureReason) ?? "", + "localizedRecoverySuggestion": ((_playerItem.error! as NSError).localizedRecoverySuggestion == nil ? "" : (_playerItem.error! as NSError).localizedRecoverySuggestion) ?? "", + "domain": (_playerItem.error as! NSError).domain + ], + "target": reactTag + ]) + } + + func handlePlaybackBufferKeyEmpty(playerItem:AVPlayerItem, change:NSKeyValueObservedChange) { + _playerBufferEmpty = true + onVideoBuffer?(["isBuffering": true, "target": reactTag as Any]) + } + + // Continue playing (or not if paused) after being paused due to hitting an unbuffered zone. + func handlePlaybackLikelyToKeepUp(playerItem:AVPlayerItem, change:NSKeyValueObservedChange) { + if (!(_controls || _fullscreenPlayerPresented) || _playerBufferEmpty) && ((_playerItem?.isPlaybackLikelyToKeepUp) != nil) { + setPaused(_paused) + } + _playerBufferEmpty = false + onVideoBuffer?(["isBuffering": false, "target": reactTag as Any]) + } + + func handlePlaybackRateChange(player: AVPlayer, change: NSKeyValueObservedChange) { + guard let _player = _player else { return } + onPlaybackRateChange?(["playbackRate": NSNumber(value: _player.rate), + "target": reactTag as Any]) + if _playbackStalled && _player.rate > 0 { + onPlaybackResume?(["playbackRate": NSNumber(value: _player.rate), + "target": reactTag as Any]) + _playbackStalled = false + } + } + + func handleExternalPlaybackActiveChange(player: AVPlayer, change: NSKeyValueObservedChange) { + guard let _player = _player else { return } + onVideoExternalPlaybackChange?(["isExternalPlaybackActive": NSNumber(value: _player.isExternalPlaybackActive), + "target": reactTag as Any]) + } + + func handleViewControllerOverlayViewFrameChange(overlayView:UIView, change:NSKeyValueObservedChange) { + let oldRect = change.oldValue + let newRect = change.newValue + if !oldRect!.equalTo(newRect!) { + if newRect!.equalTo(UIScreen.main.bounds) { + NSLog("in fullscreen") + + self.reactViewController().view.frame = UIScreen.main.bounds + self.reactViewController().view.setNeedsLayout() + } else {NSLog("not fullscreen")} + } + } + + @objc func handleDidFailToFinishPlaying(notification:NSNotification!) { + let error:NSError! = notification.userInfo?[AVPlayerItemFailedToPlayToEndTimeErrorKey] as? NSError + onVideoError?( + [ + "error": [ + "code": NSNumber(value: (error as NSError).code), + "localizedDescription": error.localizedDescription ?? "", + "localizedFailureReason": (error as NSError).localizedFailureReason ?? "", + "localizedRecoverySuggestion": (error as NSError).localizedRecoverySuggestion ?? "", + "domain": (error as NSError).domain + ], + "target": reactTag + ]) + } + + @objc func handlePlaybackStalled(notification:NSNotification!) { + onPlaybackStalled?(["target": reactTag as Any]) + _playbackStalled = true + } + + @objc func handlePlayerItemDidReachEnd(notification:NSNotification!) { + onVideoEnd?(["target": reactTag as Any]) + + if _repeat { + let item:AVPlayerItem! = notification.object as? AVPlayerItem + item.seek(to: CMTime.zero) + self.applyModifiers() + } else { + _playerObserver.removePlayerTimeObserver() + } + } + + //unused + // @objc func handleAVPlayerAccess(notification:NSNotification!) { + // let accessLog:AVPlayerItemAccessLog! = (notification.object as! AVPlayerItem).accessLog() + // let lastEvent:AVPlayerItemAccessLogEvent! = accessLog.events.last + // + // /* TODO: get this working + // if (self.onBandwidthUpdate) { + // self.onBandwidthUpdate(@{@"bitrate": [NSNumber numberWithFloat:lastEvent.observedBitrate]}); + // } + // */ + // } +} diff --git a/ios/Video/RCTVideoManager.h b/ios/Video/RCTVideoManager.h deleted file mode 100644 index b3bfccb5..00000000 --- a/ios/Video/RCTVideoManager.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface RCTVideoManager : RCTViewManager - -@end diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index 000a9e83..9979b440 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -1,22 +1,7 @@ -#import "RCTVideoManager.h" -#import "RCTVideo.h" #import -#import -#import +#import "React/RCTViewManager.h" -@implementation RCTVideoManager - -RCT_EXPORT_MODULE(); - -- (UIView *)view -{ - return [[RCTVideo alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; -} - -- (dispatch_queue_t)methodQueue -{ - return self.bridge.uiManager.methodQueue; -} +@interface RCT_EXTERN_MODULE(RCTVideoManager, RCTViewManager) RCT_EXPORT_VIEW_PROPERTY(src, NSDictionary); RCT_EXPORT_VIEW_PROPERTY(drm, NSDictionary); @@ -49,6 +34,8 @@ RCT_EXPORT_VIEW_PROPERTY(filter, NSString); RCT_EXPORT_VIEW_PROPERTY(filterEnabled, BOOL); RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float); RCT_EXPORT_VIEW_PROPERTY(restoreUserInterfaceForPIPStopCompletionHandler, BOOL); +RCT_EXPORT_VIEW_PROPERTY(localSourceEncryptionKeyScheme, NSString); + /* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */ RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTDirectEventBlock); @@ -70,64 +57,18 @@ RCT_EXPORT_VIEW_PROPERTY(onPlaybackResume, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onPlaybackRateChange, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoExternalPlaybackChange, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onGetLicense, RCTDirectEventBlock); -RCT_REMAP_METHOD(save, - options:(NSDictionary *)options - reactTag:(nonnull NSNumber *)reactTag - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - [self.bridge.uiManager prependUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTVideo *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RCTVideo class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RCTVideo, got: %@", view); - } else { - [view save:options resolve:resolve reject:reject]; - } - }]; -}; -RCT_REMAP_METHOD(setLicenseResult, - license:(NSString *)license - reactTag:(nonnull NSNumber *)reactTag) -{ - [self.bridge.uiManager prependUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTVideo *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RCTVideo class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RCTVideo, got: %@", view); - } else { - [view setLicenseResult:license]; - } - }]; -}; - -RCT_REMAP_METHOD(setLicenseResultError, - error:(NSString *)error - reactTag:(nonnull NSNumber *)reactTag) -{ - [self.bridge.uiManager prependUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTVideo *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RCTVideo class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RCTVideo, got: %@", view); - } else { - [view setLicenseResultError:error]; - } - }]; -}; RCT_EXPORT_VIEW_PROPERTY(onPictureInPictureStatusChanged, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onRestoreUserInterfaceForPictureInPictureStop, RCTDirectEventBlock); -- (NSDictionary *)constantsToExport -{ - return @{ - @"ScaleNone": AVLayerVideoGravityResizeAspect, - @"ScaleToFill": AVLayerVideoGravityResize, - @"ScaleAspectFit": AVLayerVideoGravityResizeAspect, - @"ScaleAspectFill": AVLayerVideoGravityResizeAspectFill - }; -} +RCT_EXTERN_METHOD(save:(NSDictionary *)options + reactTag:(nonnull NSNumber *)reactTag + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) -+ (BOOL)requiresMainQueueSetup -{ - return YES; -} +RCT_EXTERN_METHOD(setLicenseResult:(NSString *)license + reactTag:(nonnull NSNumber *)reactTag) + +RCT_EXTERN_METHOD(setLicenseResultError(NSString *)error + reactTag:(nonnull NSNumber *)reactTag) @end diff --git a/ios/Video/RCTVideoManager.swift b/ios/Video/RCTVideoManager.swift new file mode 100644 index 00000000..bf42f707 --- /dev/null +++ b/ios/Video/RCTVideoManager.swift @@ -0,0 +1,63 @@ +import AVFoundation +import React + +@objc(RCTVideoManager) +class RCTVideoManager: RCTViewManager { + + override func view() -> UIView { + return RCTVideo(eventDispatcher: bridge.eventDispatcher()) + } + + func methodQueue() -> DispatchQueue { + return bridge.uiManager.methodQueue + } + + @objc(save:reactTag:resolver:rejecter:) + func save(options: NSDictionary, reactTag: NSNumber, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void { + bridge.uiManager.prependUIBlock({_ , viewRegistry in + let view = viewRegistry?[reactTag] + if !(view is RCTVideo) { + RCTLogError("Invalid view returned from registry, expecting RCTVideo, got: %@", String(describing: view)) + } else if let view = view as? RCTVideo { + view.save(options: options, resolve: resolve, reject: reject) + } + }) + } + + @objc(setLicenseResult:reactTag:) + func setLicenseResult(license: NSString, reactTag: NSNumber) -> Void { + bridge.uiManager.prependUIBlock({_ , viewRegistry in + let view = viewRegistry?[reactTag] + if !(view is RCTVideo) { + RCTLogError("Invalid view returned from registry, expecting RCTVideo, got: %@", String(describing: view)) + } else if let view = view as? RCTVideo { + view.setLicenseResult(license as String) + } + }) + } + + @objc(setLicenseResultError:reactTag:) + func setLicenseResultError(error: NSString, reactTag: NSNumber) -> Void { + bridge.uiManager.prependUIBlock({_ , viewRegistry in + let view = viewRegistry?[reactTag] + if !(view is RCTVideo) { + RCTLogError("Invalid view returned from registry, expecting RCTVideo, got: %@", String(describing: view)) + } else if let view = view as? RCTVideo { + view.setLicenseResultError(error as String) + } + }) + } + + override func constantsToExport() -> [AnyHashable : Any]? { + return [ + "ScaleNone": AVLayerVideoGravity.resizeAspect, + "ScaleToFill": AVLayerVideoGravity.resize, + "ScaleAspectFit": AVLayerVideoGravity.resizeAspect, + "ScaleAspectFill": AVLayerVideoGravity.resizeAspectFill + ] + } + + override class func requiresMainQueueSetup() -> Bool { + return true + } +} diff --git a/ios/Video/RCTVideoPlayerViewController.h b/ios/Video/RCTVideoPlayerViewController.h deleted file mode 100644 index ed9ebdde..00000000 --- a/ios/Video/RCTVideoPlayerViewController.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// RCTVideoPlayerViewController.h -// RCTVideo -// -// Created by Stanisław Chmiela on 31.03.2016. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import -#import "RCTVideo.h" -#import "RCTVideoPlayerViewControllerDelegate.h" - -@interface RCTVideoPlayerViewController : AVPlayerViewController -@property (nonatomic, weak) id rctDelegate; - -// Optional paramters -@property (nonatomic, weak) NSString* preferredOrientation; -@property (nonatomic) BOOL autorotate; - -@end diff --git a/ios/Video/RCTVideoPlayerViewController.m b/ios/Video/RCTVideoPlayerViewController.m deleted file mode 100644 index 548a06ce..00000000 --- a/ios/Video/RCTVideoPlayerViewController.m +++ /dev/null @@ -1,43 +0,0 @@ -#import "RCTVideoPlayerViewController.h" - -@interface RCTVideoPlayerViewController () - -@end - -@implementation RCTVideoPlayerViewController - -- (BOOL)shouldAutorotate { - - if (self.autorotate || self.preferredOrientation.lowercaseString == nil || [self.preferredOrientation.lowercaseString isEqualToString:@"all"]) - return YES; - - return NO; -} - -- (void)viewDidDisappear:(BOOL)animated -{ - [super viewDidDisappear:animated]; - [_rctDelegate videoPlayerViewControllerWillDismiss:self]; - [_rctDelegate videoPlayerViewControllerDidDismiss:self]; -} - -#if !TARGET_OS_TV -- (UIInterfaceOrientationMask)supportedInterfaceOrientations { - return UIInterfaceOrientationMaskAll; -} - -- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { - if ([self.preferredOrientation.lowercaseString isEqualToString:@"landscape"]) { - return UIInterfaceOrientationLandscapeRight; - } - else if ([self.preferredOrientation.lowercaseString isEqualToString:@"portrait"]) { - return UIInterfaceOrientationPortrait; - } - else { // default case - UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; - return orientation; - } -} -#endif - -@end diff --git a/ios/Video/RCTVideoPlayerViewController.swift b/ios/Video/RCTVideoPlayerViewController.swift new file mode 100644 index 00000000..95c926af --- /dev/null +++ b/ios/Video/RCTVideoPlayerViewController.swift @@ -0,0 +1,44 @@ +import AVKit + +class RCTVideoPlayerViewController: AVPlayerViewController { + + var rctDelegate:RCTVideoPlayerViewControllerDelegate! + + // Optional paramters + var preferredOrientation:String? + var autorotate:Bool? + + func shouldAutorotate() -> Bool { + + if autorotate! || preferredOrientation == nil || (preferredOrientation!.lowercased() == "all") { + return true + } + + return false + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + rctDelegate.videoPlayerViewControllerWillDismiss(playerViewController: self) + rctDelegate.videoPlayerViewControllerDidDismiss(playerViewController: self) + } + + #if !TARGET_OS_TV + + func supportedInterfaceOrientations() -> UIInterfaceOrientationMask { + return .all + } + + func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation { + if preferredOrientation?.lowercased() == "landscape" { + return .landscapeRight + } else if preferredOrientation?.lowercased() == "portrait" { + return .portrait + } else { + // default case + let orientation = UIApplication.shared.statusBarOrientation + return orientation + } + } + #endif +} diff --git a/ios/Video/RCTVideoPlayerViewControllerDelegate.h b/ios/Video/RCTVideoPlayerViewControllerDelegate.h deleted file mode 100644 index e84b3f52..00000000 --- a/ios/Video/RCTVideoPlayerViewControllerDelegate.h +++ /dev/null @@ -1,7 +0,0 @@ -#import -#import "AVKit/AVKit.h" - -@protocol RCTVideoPlayerViewControllerDelegate -- (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController; -- (void)videoPlayerViewControllerDidDismiss:(AVPlayerViewController *)playerViewController; -@end diff --git a/ios/Video/RCTVideoPlayerViewControllerDelegate.swift b/ios/Video/RCTVideoPlayerViewControllerDelegate.swift new file mode 100644 index 00000000..6635975f --- /dev/null +++ b/ios/Video/RCTVideoPlayerViewControllerDelegate.swift @@ -0,0 +1,7 @@ +import Foundation +import AVKit + +protocol RCTVideoPlayerViewControllerDelegate : NSObject { + func videoPlayerViewControllerWillDismiss(playerViewController:AVPlayerViewController) + func videoPlayerViewControllerDidDismiss(playerViewController:AVPlayerViewController) +} diff --git a/ios/Video/UIView+FindUIViewController.h b/ios/Video/UIView+FindUIViewController.h deleted file mode 100644 index 09214261..00000000 --- a/ios/Video/UIView+FindUIViewController.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// UIView+FindUIViewController.h -// RCTVideo -// -// Created by Stanisław Chmiela on 31.03.2016. -// Copyright © 2016 Facebook. All rights reserved. -// -// Source: http://stackoverflow.com/a/3732812/1123156 - -#import - -@interface UIView (FindUIViewController) -- (UIViewController *) firstAvailableUIViewController; -- (id) traverseResponderChainForUIViewController; -@end diff --git a/ios/Video/UIView+FindUIViewController.m b/ios/Video/UIView+FindUIViewController.m deleted file mode 100644 index c29c742a..00000000 --- a/ios/Video/UIView+FindUIViewController.m +++ /dev/null @@ -1,21 +0,0 @@ -// Source: http://stackoverflow.com/a/3732812/1123156 - -#import "UIView+FindUIViewController.h" - -@implementation UIView (FindUIViewController) -- (UIViewController *) firstAvailableUIViewController { - // convenience function for casting and to "mask" the recursive function - return (UIViewController *)[self traverseResponderChainForUIViewController]; -} - -- (id) traverseResponderChainForUIViewController { - id nextResponder = [self nextResponder]; - if ([nextResponder isKindOfClass:[UIViewController class]]) { - return nextResponder; - } else if ([nextResponder isKindOfClass:[UIView class]]) { - return [nextResponder traverseResponderChainForUIViewController]; - } else { - return nil; - } -} -@end diff --git a/ios/Video/UIView+FindUIViewController.swift b/ios/Video/UIView+FindUIViewController.swift new file mode 100644 index 00000000..2947e1e3 --- /dev/null +++ b/ios/Video/UIView+FindUIViewController.swift @@ -0,0 +1,18 @@ +// Source: http://stackoverflow.com/a/3732812/1123156 + +extension UIView { + func firstAvailableUIViewController() -> UIViewController? { + // convenience function for casting and to "mask" the recursive function + return traverseResponderChainForUIViewController() + } + + func traverseResponderChainForUIViewController() -> UIViewController? { + if let nextUIViewController = next as? UIViewController { + return nextUIViewController + } else if let nextUIView = next as? UIView { + return nextUIView.traverseResponderChainForUIViewController() + } else { + return nil + } + } +} diff --git a/ios/VideoCaching/RCTVideoCachingHandler.swift b/ios/VideoCaching/RCTVideoCachingHandler.swift new file mode 100644 index 00000000..5f6ffb77 --- /dev/null +++ b/ios/VideoCaching/RCTVideoCachingHandler.swift @@ -0,0 +1,87 @@ +import Foundation +import AVFoundation +import DVAssetLoaderDelegate +import Promises + +class RCTVideoCachingHandler: NSObject, DVAssetLoaderDelegatesDelegate { + + private var _videoCache:RCTVideoCache! = RCTVideoCache.sharedInstance() + var playerItemPrepareText: ((AVAsset?, NSDictionary?) -> AVPlayerItem)? + + override init() { + super.init() + } + + func shouldCache(source: VideoSource, textTracks:[TextTrack]?) -> Bool { + if source.isNetwork && source.shouldCache && ((textTracks == nil) || (textTracks!.count == 0)) { + /* The DVURLAsset created by cache doesn't have a tracksWithMediaType property, so trying + * to bring in the text track code will crash. I suspect this is because the asset hasn't fully loaded. + * Until this is fixed, we need to bypass caching when text tracks are specified. + */ + DebugLog("Caching is not supported for uri '\(source.uri)' because text tracks are not compatible with the cache. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md") + return true + } + return false + } + + func playerItemForSourceUsingCache(uri:String!, assetOptions options:NSDictionary!) -> Promise { + let url = URL(string: uri) + return getItemForUri(uri) + .then{ [weak self] (videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?) -> AVPlayerItem in + guard let self = self, let playerItemPrepareText = self.playerItemPrepareText else {throw NSError(domain: "", code: 0, userInfo: nil)} + switch (videoCacheStatus) { + case .missingFileExtension: + DebugLog("Could not generate cache key for uri '\(uri)'. It is currently not supported to cache urls that do not include a file extension. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md") + let asset:AVURLAsset! = AVURLAsset(url: url!, options:options as! [String : Any]) + return playerItemPrepareText(asset, options) + + case .unsupportedFileExtension: + DebugLog("Could not generate cache key for uri '\(uri)'. The file extension of that uri is currently not supported. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md") + let asset:AVURLAsset! = AVURLAsset(url: url!, options:options as! [String : Any]) + return playerItemPrepareText(asset, options) + + default: + if let cachedAsset = cachedAsset { + DebugLog("Playing back uri '\(uri)' from cache") + // See note in playerItemForSource about not being able to support text tracks & caching + return AVPlayerItem(asset: cachedAsset) + } + } + + let asset:DVURLAsset! = DVURLAsset(url:url, options:options as! [String : Any], networkTimeout:10000) + asset.loaderDelegate = self + + /* More granular code to have control over the DVURLAsset + let resourceLoaderDelegate = DVAssetLoaderDelegate(url: url) + resourceLoaderDelegate.delegate = self + let components = NSURLComponents(url: url, resolvingAgainstBaseURL: false) + components?.scheme = DVAssetLoaderDelegate.scheme() + var asset: AVURLAsset? = nil + if let url = components?.url { + asset = AVURLAsset(url: url, options: options) + } + asset?.resourceLoader.setDelegate(resourceLoaderDelegate, queue: DispatchQueue.main) + */ + + return AVPlayerItem(asset: asset) + } + } + + func getItemForUri(_ uri:String) -> Promise<(videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?)> { + return Promise<(videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?)> { fulfill, reject in + self._videoCache.getItemForUri(uri, withCallback:{ (videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?) in + fulfill((videoCacheStatus, cachedAsset)) + }) + } + } + + // MARK: - DVAssetLoaderDelegate + + func dvAssetLoaderDelegate(loaderDelegate:DVAssetLoaderDelegate!, didLoadData data:NSData!, forURL url:NSURL!) { + _videoCache.storeItem(data as Data?, forUri:url.absoluteString, withCallback:{ (success:Bool) in + DebugLog("Cache data stored successfully 🎉") + }) + } + +} + diff --git a/package.json b/package.json index 3ce33a36..a4e335e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-video", - "version": "5.2.0", + "version": "6.0.0-alpha1", "description": "A From d1f3ad292dcdd54e95cb318ad9edca3c905acff9 Mon Sep 17 00:00:00 2001 From: Adam Gorman Date: Tue, 31 May 2022 23:25:08 -0700 Subject: [PATCH 66/76] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 569d9997..6a5bdef7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Add iOS support for decoding offline sources [#2527](https://github.com/react-native-video/react-native-video/pull/2527) - Update basic example applications (React Native 0.63.4) [#2527](https://github.com/react-native-video/react-native-video/pull/2527) - Fix volume reset issue in exoPlayer [#2371](https://github.com/react-native-video/react-native-video/pull/2371) +- Change WindowsTargetPlatformVersion to 10.0 [#2706](https://github.com/react-native-video/react-native-video/pull/2706) ### Version 5.2.0 From 0330943d52d3b5f5178cc273620df7f350b155a7 Mon Sep 17 00:00:00 2001 From: Armands Malejevs Date: Thu, 2 Jun 2022 15:08:07 +0300 Subject: [PATCH 67/76] Dont download dash manifest if contentStartTime is not set --- .../brentvatne/exoplayer/ReactExoplayerView.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 8de3bbd5..75cdd215 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -185,7 +185,7 @@ class ReactExoplayerView extends FrameLayout implements private ReadableArray textTracks; private boolean disableFocus; private boolean disableBuffering; - private long contentStartTime; + private long contentStartTime = -1L; private boolean disableDisconnectError; private boolean preventsDisplaySleepDuringVideoPlayback = true; private float mProgressUpdateInterval = 250.0f; @@ -1068,10 +1068,12 @@ class ReactExoplayerView extends FrameLayout implements } private WritableArray getVideoTrackInfo(int trackRendererIndex) { - WritableArray contentVideoTracks = this.getVideoTrackInfoFromManifest(); - if (contentVideoTracks != null) { - isUsingContentResolution = true; - return contentVideoTracks; + if (this.contentStartTime != -1L) { + WritableArray contentVideoTracks = this.getVideoTrackInfoFromManifest(); + if (contentVideoTracks != null) { + isUsingContentResolution = true; + return contentVideoTracks; + } } WritableArray videoTracks = Arguments.createArray(); @@ -1107,6 +1109,7 @@ class ReactExoplayerView extends FrameLayout implements return this.getVideoTrackInfoFromManifest(0); } + // We need retry count to in case where minefest request fails from poor network conditions private WritableArray getVideoTrackInfoFromManifest(int retryCount) { ExecutorService es = Executors.newSingleThreadExecutor(); final DataSource dataSource = this.mediaDataSourceFactory.createDataSource(); From c7b739026713598f84bb4095b7f22fd9e2d06b29 Mon Sep 17 00:00:00 2001 From: Armands Malejevs Date: Thu, 2 Jun 2022 15:10:23 +0300 Subject: [PATCH 68/76] Prevent unnecessary work when creating video track info --- .../com/brentvatne/exoplayer/ReactExoplayerView.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 75cdd215..703298a8 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1090,13 +1090,13 @@ class ReactExoplayerView extends FrameLayout implements for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { Format format = group.getFormat(trackIndex); - WritableMap videoTrack = Arguments.createMap(); - videoTrack.putInt("width", format.width == Format.NO_VALUE ? 0 : format.width); - videoTrack.putInt("height",format.height == Format.NO_VALUE ? 0 : format.height); - videoTrack.putInt("bitrate", format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); - videoTrack.putString("codecs", format.codecs != null ? format.codecs : ""); - videoTrack.putString("trackId", format.id == null ? String.valueOf(trackIndex) : format.id); if (isFormatSupported(format)) { + WritableMap videoTrack = Arguments.createMap(); + videoTrack.putInt("width", format.width == Format.NO_VALUE ? 0 : format.width); + videoTrack.putInt("height",format.height == Format.NO_VALUE ? 0 : format.height); + videoTrack.putInt("bitrate", format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); + videoTrack.putString("codecs", format.codecs != null ? format.codecs : ""); + videoTrack.putString("trackId", format.id == null ? String.valueOf(trackIndex) : format.id); videoTracks.pushMap(videoTrack); } } From 556192da1dd136d14d88b3ece46295595f14ba10 Mon Sep 17 00:00:00 2001 From: olivier bouillet Date: Fri, 3 Jun 2022 21:51:53 +0200 Subject: [PATCH 69/76] test(android): add additional sample streams --- examples/basic/src/VideoPlayer.android.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/basic/src/VideoPlayer.android.tsx b/examples/basic/src/VideoPlayer.android.tsx index 7572174f..0ca36fb6 100644 --- a/examples/basic/src/VideoPlayer.android.tsx +++ b/examples/basic/src/VideoPlayer.android.tsx @@ -50,18 +50,26 @@ class VideoPlayer extends Component { srcList = [ require('./broadchurch.mp4'), { - description: 'subtitles', + description: '(dash) sintel subtitles', uri: 'https://bitmovin-a.akamaihd.net/content/sintel/sintel.mpd', }, { - description: 'demo with sintel Subtitles', + description: '(mp4) big buck bunny', + uri: 'http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4', + }, + { + description: '(hls|live) red bull tv', + uri: 'https://rbmn-live.akamaized.net/hls/live/590964/BoRB-AT/master_928.m3u8' + }, + { + description: '(mp4|subtitles) demo with sintel Subtitles', uri: 'http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7.8506521BFC350652163895D4C26DEE124209AA9E&key=ik0', type: 'mpd', }, - { description: 'Stopped playback', uri: undefined }, + { description: '(no url) Stopped playback', uri: undefined }, { - description: 'no View', + description: '(no view) no View', noView: true, }, ] From 99ba90cc0972b15563e1c3f57424d20fe0818c76 Mon Sep 17 00:00:00 2001 From: olivier bouillet Date: Fri, 3 Jun 2022 22:02:31 +0200 Subject: [PATCH 70/76] test(android): enable app on androidtv --- examples/basic/android/app/src/main/AndroidManifest.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/basic/android/app/src/main/AndroidManifest.xml b/examples/basic/android/app/src/main/AndroidManifest.xml index 4ea9eb2a..6d2862d6 100644 --- a/examples/basic/android/app/src/main/AndroidManifest.xml +++ b/examples/basic/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,13 @@ + + + @@ -9,6 +16,7 @@ android:allowBackup="true" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" + android:banner="@mipmap/ic_launcher" android:usesCleartextTraffic="true" android:roundIcon="@mipmap/ic_launcher_round" android:theme="@style/AppTheme"> @@ -21,6 +29,7 @@ + From 9ce8e26bd4d051d5e0861af019f2d7da17056231 Mon Sep 17 00:00:00 2001 From: Eran Hammer Date: Sat, 4 Jun 2022 01:15:25 -0700 Subject: [PATCH 71/76] Update Video.js Co-authored-by: Elliott Kember --- Video.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Video.js b/Video.js index 2d3c397f..5c07d8de 100644 --- a/Video.js +++ b/Video.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { StyleSheet, requireNativeComponent, NativeModules, UIManager, View, Image, Platform, findNodeHandle } from 'react-native'; -import { ViewPropTypes } from "deprecated-react-native-prop-types"; +import { ViewPropTypes } from 'deprecated-react-native-prop-types'; import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; import TextTrackType from './TextTrackType'; import FilterType from './FilterType'; From e90ab21c91cac2126bc096822446920e1a8815ed Mon Sep 17 00:00:00 2001 From: Armands Malejevs Date: Tue, 7 Jun 2022 12:23:06 +0300 Subject: [PATCH 72/76] Fix single track playback --- .../exoplayer/ReactExoplayerView.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 703298a8..3eb6c9c1 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1612,14 +1612,19 @@ class ReactExoplayerView extends FrameLayout implements supportedFormatLength++; } } - tracks = new int[supportedFormatLength + 1]; - int o = 0; - for (int k = 0; k < allTracks.length; k++) { - Format format = group.getFormat(k); - if (isFormatSupported(format)) { - tracks[o] = allTracks[k]; - supportedTrackList.add(allTracks[k]); - o++; + if (allTracks.length == 1) { + // With only one tracks we can't remove any tracks so attempt to play it anyway + tracks = allTracks; + } else { + tracks = new int[supportedFormatLength + 1]; + int o = 0; + for (int k = 0; k < allTracks.length; k++) { + Format format = group.getFormat(k); + if (isFormatSupported(format)) { + tracks[o] = allTracks[k]; + supportedTrackList.add(allTracks[k]); + o++; + } } } } From c110822b3f7dea33036405648c423552655d804c Mon Sep 17 00:00:00 2001 From: MONSX662 Date: Tue, 7 Jun 2022 08:57:31 -0400 Subject: [PATCH 73/76] updated ReactVideoView.java to fix seeking issue on android --- android/src/main/java/com/brentvatne/react/ReactVideoView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoView.java b/android/src/main/java/com/brentvatne/react/ReactVideoView.java index f3f5fe7b..ed71c846 100644 --- a/android/src/main/java/com/brentvatne/react/ReactVideoView.java +++ b/android/src/main/java/com/brentvatne/react/ReactVideoView.java @@ -637,7 +637,7 @@ public class ReactVideoView extends ScalableVideoView implements public void seekTo(int msec) { if (mMediaPlayerValid) { mSeekTime = msec; - super.seekTo(msec); + mMediaPlayer.seekTo(msec,3); if (isCompleted && mVideoDuration != 0 && msec < mVideoDuration) { isCompleted = false; } From 8254a1d670a9accd1395aa3d69db4a09cc4c93e7 Mon Sep 17 00:00:00 2001 From: MONSX662 Date: Tue, 7 Jun 2022 09:07:25 -0400 Subject: [PATCH 74/76] updated CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a5bdef7..26d7c06d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Update basic example applications (React Native 0.63.4) [#2527](https://github.com/react-native-video/react-native-video/pull/2527) - Fix volume reset issue in exoPlayer [#2371](https://github.com/react-native-video/react-native-video/pull/2371) - Change WindowsTargetPlatformVersion to 10.0 [#2706](https://github.com/react-native-video/react-native-video/pull/2706) +- Fixed Android seeking bug [#2712](https://github.com/react-native-video/react-native-video/pull/2712) ### Version 5.2.0 From 8a057c553bda8aa35f7ba5d3150425ca143290ef Mon Sep 17 00:00:00 2001 From: Armands Malejevs Date: Wed, 8 Jun 2022 11:29:09 +0300 Subject: [PATCH 75/76] Remove buffer progress event --- CHANGELOG.md | 1 - README.md | 12 ------- Video.js | 7 ---- .../exoplayer/ReactExoplayerView.java | 32 ------------------- .../exoplayer/VideoEventEmitter.java | 8 ----- 5 files changed, 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5425575..f16fc2f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,6 @@ - Add support for customising back buffer duration and handle network errors gracefully to prevent releasing the player when network is lost. [#2689](https://github.com/react-native-video/react-native-video/pull/2689) - Allow player to be init before source is provided, and later update once a source is provided. [#2689](https://github.com/react-native-video/react-native-video/pull/2689) - Adds handling for providing a empty source in order to stop playback and clear out any existing content [#2689](https://github.com/react-native-video/react-native-video/pull/2689) -- Add support for onBufferProgress prop on Android to get buffer data even when the player is paused. [#2689](https://github.com/react-native-video/react-native-video/pull/2689) - Add support for detecting if format is supported and exclude unsupported resolutions from auto quality selection and video track info in RN. [#2689](https://github.com/react-native-video/react-native-video/pull/2689) - Improve error handling [#2689](https://github.com/react-native-video/react-native-video/pull/2689) - Add support for L1 to L3 Widevine fallback if playback fails initially. [#2689](https://github.com/react-native-video/react-native-video/pull/2689) diff --git a/README.md b/README.md index a60ddeb5..998d7ded 100644 --- a/README.md +++ b/README.md @@ -376,7 +376,6 @@ var styles = StyleSheet.create({ |--|--| |[onAudioBecomingNoisy](#onaudiobecomingnoisy)|Android ExoPlayer, iOS| |[onBandwidthUpdate](#onbandwidthupdate)|Android ExoPlayer| -|[onBufferProgress](#onbufferprogress)|Android ExopPlater| |[onBuffer](#onbuffer)|Android ExoPlayer, iOS| |[onEnd](#onend)|All| |[onExternalPlaybackChange](#onexternalplaybackchange)|iOS| @@ -1035,17 +1034,6 @@ Note: On Android ExoPlayer, you must set the [reportBandwidth](#reportbandwidth) Platforms: Android ExoPlayer -#### onBufferProgress -Callback function that is called on a set interval which contains the buffer start and end position in ms. - -Payload: -Property | Type | Description ---- | --- | --- -start | number | The buffer start (ms) -end | number | The buffer end (ms) - -Platforms: Android ExoPlayer - #### onBuffer Callback function that is called when the player buffers. diff --git a/Video.js b/Video.js index dd70750e..7ff55565 100644 --- a/Video.js +++ b/Video.js @@ -239,12 +239,6 @@ export default class Video extends Component { } }; - _onBufferProgress = (event) => { - if (this.props.onBufferProgress) { - this.props.onBufferProgress(event.nativeEvent); - } - }; - _onGetLicense = (event) => { if (this.props.drm && this.props.drm.getLicense instanceof Function) { const data = event.nativeEvent; @@ -326,7 +320,6 @@ export default class Video extends Component { onVideoSeek: this._onSeek, onVideoEnd: this._onEnd, onVideoBuffer: this._onBuffer, - onVideoBufferProgress: this._onBufferProgress, onVideoBandwidthUpdate: this._onBandwidthUpdate, onTimedMetadata: this._onTimedMetadata, onVideoAudioBecomingNoisy: this._onAudioBecomingNoisy, diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 3eb6c9c1..314345bb 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -169,7 +169,6 @@ class ReactExoplayerView extends FrameLayout implements private double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE; private double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE; private Handler mainHandler; - private Timer bufferCheckTimer; // Props from React private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS; @@ -477,36 +476,6 @@ class ReactExoplayerView extends FrameLayout implements VideoEventEmitter eventEmitter = this.eventEmitter; Handler mainHandler = this.mainHandler; - if (this.bufferCheckTimer != null) { - this.stopBufferCheckTimer(); - } - - this.bufferCheckTimer = new Timer(); - TimerTask bufferCheckTimerTask = new TimerTask() { - @Override - public void run() { - if (mainHandler != null) { - mainHandler.post(new Runnable() { - public void run() { - if (player != null) { - double bufferedDuration = (double) (player.getBufferedPercentage() * player.getDuration() / 100); - eventEmitter.bufferProgress(0d, bufferedDuration); - } - } - }); - } - }; - }; - - this.bufferCheckTimer.scheduleAtFixedRate(bufferCheckTimerTask, 500, 1000); - } - - private void stopBufferCheckTimer() { - if (this.bufferCheckTimer == null) { - return; - } - this.bufferCheckTimer.cancel(); - this.bufferCheckTimer = null; } private void initializePlayer() { @@ -779,7 +748,6 @@ class ReactExoplayerView extends FrameLayout implements private void releasePlayer() { if (player != null) { - stopBufferCheckTimer(); updateResumePosition(); player.release(); player.removeMetadataOutput(this); diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java index 5aeb09ce..da691bb5 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java @@ -45,7 +45,6 @@ class VideoEventEmitter { private static final String EVENT_READY = "onReadyForDisplay"; private static final String EVENT_BUFFER = "onVideoBuffer"; private static final String EVENT_PLAYBACK_STATE_CHANGED = "onVideoPlaybackStateChanged"; - private static final String EVENT_BUFFER_PROGRESS = "onVideoBufferProgress"; private static final String EVENT_IDLE = "onVideoIdle"; private static final String EVENT_TIMED_METADATA = "onTimedMetadata"; private static final String EVENT_AUDIO_BECOMING_NOISY = "onVideoAudioBecomingNoisy"; @@ -225,13 +224,6 @@ class VideoEventEmitter { receiveEvent(EVENT_PLAYBACK_STATE_CHANGED, map); } - void bufferProgress(double start, double end) { - WritableMap map = Arguments.createMap(); - map.putDouble(EVENT_PROP_BUFFER_START, start); - map.putDouble(EVENT_PROP_BUFFER_END, end); - receiveEvent(EVENT_BUFFER_PROGRESS, map); - } - void idle() { receiveEvent(EVENT_IDLE, null); } From 5a949857623c67d1d8e0fd0b2cff681b66d0abb4 Mon Sep 17 00:00:00 2001 From: MONSX662 Date: Wed, 8 Jun 2022 11:52:33 -0400 Subject: [PATCH 76/76] updated parameter to use constant instead of 3 --- android/src/main/java/com/brentvatne/react/ReactVideoView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoView.java b/android/src/main/java/com/brentvatne/react/ReactVideoView.java index ed71c846..09285fb7 100644 --- a/android/src/main/java/com/brentvatne/react/ReactVideoView.java +++ b/android/src/main/java/com/brentvatne/react/ReactVideoView.java @@ -637,7 +637,7 @@ public class ReactVideoView extends ScalableVideoView implements public void seekTo(int msec) { if (mMediaPlayerValid) { mSeekTime = msec; - mMediaPlayer.seekTo(msec,3); + mMediaPlayer.seekTo(msec, MediaPlayer.SEEK_CLOSEST); if (isCompleted && mVideoDuration != 0 && msec < mVideoDuration) { isCompleted = false; }