stormcloud-video-player 0.1.13 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.cjs CHANGED
@@ -30,15 +30,36 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ IS_BROWSER: () => IS_BROWSER,
34
+ IS_GLOBAL: () => IS_GLOBAL,
35
+ IS_IOS: () => IS_IOS,
36
+ IS_SAFARI: () => IS_SAFARI,
37
+ SUPPORTS_DASH: () => SUPPORTS_DASH,
38
+ SUPPORTS_HLS: () => SUPPORTS_HLS,
39
+ StormcloudPlayer: () => StormcloudPlayer_default,
33
40
  StormcloudVideoPlayer: () => StormcloudVideoPlayer,
34
41
  StormcloudVideoPlayerComponent: () => StormcloudVideoPlayerComponent,
42
+ canPlay: () => canPlay,
43
+ createStormcloudPlayer: () => createStormcloudPlayer,
44
+ default: () => StormcloudVideoPlayerComponent,
35
45
  getBrowserID: () => getBrowserID,
36
46
  getClientInfo: () => getClientInfo,
47
+ isMediaStream: () => isMediaStream,
48
+ lazy: () => lazy,
49
+ merge: () => merge,
50
+ omit: () => omit,
51
+ parseQuery: () => parseQuery,
52
+ players: () => players_default,
53
+ randomString: () => randomString,
37
54
  sendHeartbeat: () => sendHeartbeat,
38
- sendInitialTracking: () => sendInitialTracking
55
+ sendInitialTracking: () => sendInitialTracking,
56
+ supportsWebKitPresentationMode: () => supportsWebKitPresentationMode
39
57
  });
40
58
  module.exports = __toCommonJS(index_exports);
41
59
 
60
+ // src/ui/StormcloudVideoPlayer.tsx
61
+ var import_react = __toESM(require("react"), 1);
62
+
42
63
  // src/player/StormcloudVideoPlayer.ts
43
64
  var import_hls = __toESM(require("hls.js"), 1);
44
65
 
@@ -1133,7 +1154,6 @@ var StormcloudVideoPlayer = class {
1133
1154
  }
1134
1155
  parseScte35Binary(data) {
1135
1156
  class BitReader {
1136
- // 0..7
1137
1157
  constructor(buf) {
1138
1158
  this.buf = buf;
1139
1159
  this.bytePos = 0;
@@ -1672,7 +1692,6 @@ var StormcloudVideoPlayer = class {
1672
1692
  };
1673
1693
 
1674
1694
  // src/ui/StormcloudVideoPlayer.tsx
1675
- var import_react = __toESM(require("react"), 1);
1676
1695
  var import_fa = require("react-icons/fa");
1677
1696
  var import_jsx_runtime = require("react/jsx-runtime");
1678
1697
  var CRITICAL_PROPS = [
@@ -1816,7 +1835,12 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
1816
1835
  const showNative = player.shouldShowNativeControls();
1817
1836
  setShouldShowNativeControls(showNative);
1818
1837
  onReady?.(player);
1819
- }).catch(() => {
1838
+ }).catch((error) => {
1839
+ console.error(
1840
+ "StormcloudVideoPlayer: Failed to load player:",
1841
+ error
1842
+ );
1843
+ onReady?.(player);
1820
1844
  });
1821
1845
  return () => {
1822
1846
  try {
@@ -2904,13 +2928,921 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2904
2928
  return true;
2905
2929
  }
2906
2930
  );
2931
+
2932
+ // src/StormcloudPlayer.tsx
2933
+ var import_react6 = __toESM(require("react"), 1);
2934
+
2935
+ // src/props.ts
2936
+ var noop = () => {
2937
+ };
2938
+ var defaultProps = {
2939
+ playing: false,
2940
+ loop: false,
2941
+ controls: true,
2942
+ volume: 1,
2943
+ muted: false,
2944
+ playbackRate: 1,
2945
+ width: "100%",
2946
+ height: "auto",
2947
+ style: {},
2948
+ progressInterval: 1e3,
2949
+ playsInline: false,
2950
+ autoplay: false,
2951
+ preload: "metadata",
2952
+ poster: "",
2953
+ className: "",
2954
+ wrapperClassName: "",
2955
+ wrapperStyle: {},
2956
+ allowNativeHls: false,
2957
+ lowLatencyMode: false,
2958
+ driftToleranceMs: 1e3,
2959
+ immediateManifestAds: true,
2960
+ debugAdTiming: false,
2961
+ showCustomControls: false,
2962
+ licenseKey: "",
2963
+ adFailsafeTimeoutMs: 1e4,
2964
+ onStart: noop,
2965
+ onPlay: noop,
2966
+ onPause: noop,
2967
+ onBuffer: noop,
2968
+ onBufferEnd: noop,
2969
+ onEnded: noop,
2970
+ onError: noop,
2971
+ onDuration: noop,
2972
+ onSeek: noop,
2973
+ onProgress: noop,
2974
+ onVolumeToggle: noop,
2975
+ onFullscreenToggle: noop,
2976
+ onControlClick: noop
2977
+ };
2978
+
2979
+ // src/utils.ts
2980
+ var import_react2 = require("react");
2981
+ var lazy = import_react2.lazy;
2982
+ var omit = (object, keys) => {
2983
+ const result = { ...object };
2984
+ keys.forEach((key) => {
2985
+ delete result[key];
2986
+ });
2987
+ return result;
2988
+ };
2989
+ var isMediaStream = (url) => {
2990
+ return typeof window !== "undefined" && window.MediaStream && url instanceof window.MediaStream;
2991
+ };
2992
+ var supportsWebKitPresentationMode = () => {
2993
+ if (typeof window === "undefined") return false;
2994
+ const video = document.createElement("video");
2995
+ return "webkitSupportsPresentationMode" in video;
2996
+ };
2997
+ var randomString = () => {
2998
+ return Math.random().toString(36).substr(2, 9);
2999
+ };
3000
+ var parseQuery = (url) => {
3001
+ const query = {};
3002
+ const params = new URLSearchParams(url.split("?")[1] || "");
3003
+ params.forEach((value, key) => {
3004
+ query[key] = value;
3005
+ });
3006
+ return query;
3007
+ };
3008
+ var merge = (target, ...sources) => {
3009
+ if (!sources.length) return target;
3010
+ const source = sources.shift();
3011
+ if (isObject(target) && isObject(source)) {
3012
+ for (const key in source) {
3013
+ if (isObject(source[key])) {
3014
+ if (!target[key]) Object.assign(target, { [key]: {} });
3015
+ merge(target[key], source[key]);
3016
+ } else {
3017
+ Object.assign(target, { [key]: source[key] });
3018
+ }
3019
+ }
3020
+ }
3021
+ return merge(target, ...sources);
3022
+ };
3023
+ var isObject = (item) => {
3024
+ return item && typeof item === "object" && !Array.isArray(item);
3025
+ };
3026
+ var IS_BROWSER = typeof window !== "undefined" && window.document;
3027
+ var IS_GLOBAL = typeof globalThis !== "undefined" && globalThis.window && globalThis.window.document;
3028
+ var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(navigator.userAgent);
3029
+ var IS_SAFARI = IS_BROWSER && /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
3030
+ var SUPPORTS_HLS = () => {
3031
+ if (!IS_BROWSER) return false;
3032
+ const video = document.createElement("video");
3033
+ return Boolean(video.canPlayType("application/vnd.apple.mpegurl"));
3034
+ };
3035
+ var SUPPORTS_DASH = () => {
3036
+ if (!IS_BROWSER) return false;
3037
+ const video = document.createElement("video");
3038
+ return Boolean(video.canPlayType("application/dash+xml"));
3039
+ };
3040
+
3041
+ // src/patterns.ts
3042
+ var HLS_EXTENSIONS = /\.(m3u8)($|\?)/i;
3043
+ var HLS_PATHS = /\/hls\//i;
3044
+ var DASH_EXTENSIONS = /\.(mpd)($|\?)/i;
3045
+ var VIDEO_EXTENSIONS = /\.(mp4|webm|ogg|avi|mov|wmv|flv|mkv)($|\?)/i;
3046
+ var AUDIO_EXTENSIONS = /\.(mp3|wav|ogg|aac|wma|flac|m4a)($|\?)/i;
3047
+ var canPlay = {
3048
+ hls: (url) => {
3049
+ if (!url || typeof url !== "string") return false;
3050
+ return HLS_EXTENSIONS.test(url) || HLS_PATHS.test(url);
3051
+ },
3052
+ dash: (url) => {
3053
+ if (!url || typeof url !== "string") return false;
3054
+ return DASH_EXTENSIONS.test(url);
3055
+ },
3056
+ video: (url) => {
3057
+ if (!url || typeof url !== "string") return false;
3058
+ return VIDEO_EXTENSIONS.test(url);
3059
+ },
3060
+ audio: (url) => {
3061
+ if (!url || typeof url !== "string") return false;
3062
+ return AUDIO_EXTENSIONS.test(url);
3063
+ },
3064
+ file: (url) => {
3065
+ if (!url || typeof url !== "string") return false;
3066
+ return VIDEO_EXTENSIONS.test(url) || AUDIO_EXTENSIONS.test(url);
3067
+ }
3068
+ };
3069
+
3070
+ // src/players/HlsPlayer.tsx
3071
+ var import_react3 = require("react");
3072
+ var HlsPlayer = class extends import_react3.Component {
3073
+ constructor() {
3074
+ super(...arguments);
3075
+ this.player = null;
3076
+ this.mounted = false;
3077
+ this.load = async () => {
3078
+ if (!this.props.videoElement || !this.props.src) return;
3079
+ try {
3080
+ if (this.player) {
3081
+ this.player.destroy();
3082
+ this.player = null;
3083
+ }
3084
+ const config = {
3085
+ src: this.props.src,
3086
+ videoElement: this.props.videoElement
3087
+ };
3088
+ if (this.props.autoplay !== void 0)
3089
+ config.autoplay = this.props.autoplay;
3090
+ if (this.props.muted !== void 0) config.muted = this.props.muted;
3091
+ if (this.props.lowLatencyMode !== void 0)
3092
+ config.lowLatencyMode = this.props.lowLatencyMode;
3093
+ if (this.props.allowNativeHls !== void 0)
3094
+ config.allowNativeHls = this.props.allowNativeHls;
3095
+ if (this.props.driftToleranceMs !== void 0)
3096
+ config.driftToleranceMs = this.props.driftToleranceMs;
3097
+ if (this.props.immediateManifestAds !== void 0)
3098
+ config.immediateManifestAds = this.props.immediateManifestAds;
3099
+ if (this.props.debugAdTiming !== void 0)
3100
+ config.debugAdTiming = this.props.debugAdTiming;
3101
+ if (this.props.showCustomControls !== void 0)
3102
+ config.showCustomControls = this.props.showCustomControls;
3103
+ if (this.props.onVolumeToggle !== void 0)
3104
+ config.onVolumeToggle = this.props.onVolumeToggle;
3105
+ if (this.props.onFullscreenToggle !== void 0)
3106
+ config.onFullscreenToggle = this.props.onFullscreenToggle;
3107
+ if (this.props.onControlClick !== void 0)
3108
+ config.onControlClick = this.props.onControlClick;
3109
+ if (this.props.licenseKey !== void 0)
3110
+ config.licenseKey = this.props.licenseKey;
3111
+ if (this.props.adFailsafeTimeoutMs !== void 0)
3112
+ config.adFailsafeTimeoutMs = this.props.adFailsafeTimeoutMs;
3113
+ this.player = new StormcloudVideoPlayer(config);
3114
+ this.props.onMount?.(this);
3115
+ await this.player.load();
3116
+ if (this.mounted) {
3117
+ this.props.onReady?.();
3118
+ }
3119
+ } catch (error) {
3120
+ if (this.mounted) {
3121
+ this.props.onError?.(error);
3122
+ }
3123
+ }
3124
+ };
3125
+ this.play = () => {
3126
+ if (this.props.videoElement) {
3127
+ this.props.videoElement.play();
3128
+ this.props.onPlay?.();
3129
+ }
3130
+ };
3131
+ this.pause = () => {
3132
+ if (this.props.videoElement) {
3133
+ this.props.videoElement.pause();
3134
+ this.props.onPause?.();
3135
+ }
3136
+ };
3137
+ this.stop = () => {
3138
+ this.pause();
3139
+ if (this.props.videoElement) {
3140
+ this.props.videoElement.currentTime = 0;
3141
+ }
3142
+ };
3143
+ this.seekTo = (seconds, keepPlaying) => {
3144
+ if (this.props.videoElement) {
3145
+ this.props.videoElement.currentTime = seconds;
3146
+ if (!keepPlaying) {
3147
+ this.pause();
3148
+ }
3149
+ }
3150
+ };
3151
+ this.setVolume = (volume) => {
3152
+ if (this.props.videoElement) {
3153
+ this.props.videoElement.volume = Math.max(0, Math.min(1, volume));
3154
+ }
3155
+ };
3156
+ this.mute = () => {
3157
+ if (this.props.videoElement) {
3158
+ this.props.videoElement.muted = true;
3159
+ }
3160
+ };
3161
+ this.unmute = () => {
3162
+ if (this.props.videoElement) {
3163
+ this.props.videoElement.muted = false;
3164
+ }
3165
+ };
3166
+ this.setPlaybackRate = (rate) => {
3167
+ if (this.props.videoElement && rate > 0) {
3168
+ this.props.videoElement.playbackRate = rate;
3169
+ }
3170
+ };
3171
+ this.getDuration = () => {
3172
+ if (this.props.videoElement && isFinite(this.props.videoElement.duration)) {
3173
+ return this.props.videoElement.duration;
3174
+ }
3175
+ return null;
3176
+ };
3177
+ this.getCurrentTime = () => {
3178
+ if (this.props.videoElement && isFinite(this.props.videoElement.currentTime)) {
3179
+ return this.props.videoElement.currentTime;
3180
+ }
3181
+ return null;
3182
+ };
3183
+ this.getSecondsLoaded = () => {
3184
+ if (this.props.videoElement && this.props.videoElement.buffered.length > 0) {
3185
+ return this.props.videoElement.buffered.end(
3186
+ this.props.videoElement.buffered.length - 1
3187
+ );
3188
+ }
3189
+ return null;
3190
+ };
3191
+ this.getInternalPlayer = (key = "player") => {
3192
+ if (key === "player") return this.player;
3193
+ if (key === "video") return this.props.videoElement;
3194
+ if (key === "hls" && this.player) return this.player.hls;
3195
+ return null;
3196
+ };
3197
+ }
3198
+ componentDidMount() {
3199
+ this.mounted = true;
3200
+ this.load();
3201
+ }
3202
+ componentWillUnmount() {
3203
+ this.mounted = false;
3204
+ if (this.player) {
3205
+ this.player.destroy();
3206
+ this.player = null;
3207
+ }
3208
+ }
3209
+ componentDidUpdate(prevProps) {
3210
+ if (prevProps.src !== this.props.src) {
3211
+ this.load();
3212
+ }
3213
+ }
3214
+ render() {
3215
+ return null;
3216
+ }
3217
+ };
3218
+ HlsPlayer.displayName = "HlsPlayer";
3219
+ HlsPlayer.canPlay = canPlay.hls;
3220
+
3221
+ // src/players/FilePlayer.tsx
3222
+ var import_react4 = require("react");
3223
+ var FilePlayer = class extends import_react4.Component {
3224
+ constructor() {
3225
+ super(...arguments);
3226
+ this.mounted = false;
3227
+ this.ready = false;
3228
+ this.load = () => {
3229
+ if (!this.props.videoElement || !this.props.src) return;
3230
+ const video = this.props.videoElement;
3231
+ const handleLoadedMetadata = () => {
3232
+ if (this.mounted && !this.ready) {
3233
+ this.ready = true;
3234
+ this.props.onReady?.();
3235
+ }
3236
+ };
3237
+ const handlePlay = () => {
3238
+ if (this.mounted) {
3239
+ this.props.onPlay?.();
3240
+ }
3241
+ };
3242
+ const handlePause = () => {
3243
+ if (this.mounted) {
3244
+ this.props.onPause?.();
3245
+ }
3246
+ };
3247
+ const handleEnded = () => {
3248
+ if (this.mounted) {
3249
+ this.props.onEnded?.();
3250
+ }
3251
+ };
3252
+ const handleError = (error) => {
3253
+ if (this.mounted) {
3254
+ this.props.onError?.(error);
3255
+ }
3256
+ };
3257
+ const handleLoadedData = () => {
3258
+ if (this.mounted) {
3259
+ this.props.onLoaded?.();
3260
+ }
3261
+ };
3262
+ video.addEventListener("loadedmetadata", handleLoadedMetadata);
3263
+ video.addEventListener("play", handlePlay);
3264
+ video.addEventListener("pause", handlePause);
3265
+ video.addEventListener("ended", handleEnded);
3266
+ video.addEventListener("error", handleError);
3267
+ video.addEventListener("loadeddata", handleLoadedData);
3268
+ video.src = this.props.src;
3269
+ if (this.props.autoplay !== void 0) video.autoplay = this.props.autoplay;
3270
+ if (this.props.muted !== void 0) video.muted = this.props.muted;
3271
+ if (this.props.loop !== void 0) video.loop = this.props.loop;
3272
+ if (this.props.controls !== void 0) video.controls = this.props.controls;
3273
+ if (this.props.playsInline !== void 0)
3274
+ video.playsInline = this.props.playsInline;
3275
+ if (this.props.preload !== void 0)
3276
+ video.preload = this.props.preload;
3277
+ if (this.props.poster !== void 0) video.poster = this.props.poster;
3278
+ this.props.onMount?.(this);
3279
+ return () => {
3280
+ video.removeEventListener("loadedmetadata", handleLoadedMetadata);
3281
+ video.removeEventListener("play", handlePlay);
3282
+ video.removeEventListener("pause", handlePause);
3283
+ video.removeEventListener("ended", handleEnded);
3284
+ video.removeEventListener("error", handleError);
3285
+ video.removeEventListener("loadeddata", handleLoadedData);
3286
+ };
3287
+ };
3288
+ this.play = () => {
3289
+ if (this.props.videoElement) {
3290
+ this.props.videoElement.play();
3291
+ }
3292
+ };
3293
+ this.pause = () => {
3294
+ if (this.props.videoElement) {
3295
+ this.props.videoElement.pause();
3296
+ }
3297
+ };
3298
+ this.stop = () => {
3299
+ this.pause();
3300
+ if (this.props.videoElement) {
3301
+ this.props.videoElement.currentTime = 0;
3302
+ }
3303
+ };
3304
+ this.seekTo = (seconds, keepPlaying) => {
3305
+ if (this.props.videoElement) {
3306
+ this.props.videoElement.currentTime = seconds;
3307
+ if (!keepPlaying) {
3308
+ this.pause();
3309
+ }
3310
+ }
3311
+ };
3312
+ this.setVolume = (volume) => {
3313
+ if (this.props.videoElement) {
3314
+ this.props.videoElement.volume = Math.max(0, Math.min(1, volume));
3315
+ }
3316
+ };
3317
+ this.mute = () => {
3318
+ if (this.props.videoElement) {
3319
+ this.props.videoElement.muted = true;
3320
+ }
3321
+ };
3322
+ this.unmute = () => {
3323
+ if (this.props.videoElement) {
3324
+ this.props.videoElement.muted = false;
3325
+ }
3326
+ };
3327
+ this.setPlaybackRate = (rate) => {
3328
+ if (this.props.videoElement && rate > 0) {
3329
+ this.props.videoElement.playbackRate = rate;
3330
+ }
3331
+ };
3332
+ this.setLoop = (loop) => {
3333
+ if (this.props.videoElement) {
3334
+ this.props.videoElement.loop = loop;
3335
+ }
3336
+ };
3337
+ this.getDuration = () => {
3338
+ if (this.props.videoElement && isFinite(this.props.videoElement.duration)) {
3339
+ return this.props.videoElement.duration;
3340
+ }
3341
+ return null;
3342
+ };
3343
+ this.getCurrentTime = () => {
3344
+ if (this.props.videoElement && isFinite(this.props.videoElement.currentTime)) {
3345
+ return this.props.videoElement.currentTime;
3346
+ }
3347
+ return null;
3348
+ };
3349
+ this.getSecondsLoaded = () => {
3350
+ if (this.props.videoElement && this.props.videoElement.buffered.length > 0) {
3351
+ return this.props.videoElement.buffered.end(
3352
+ this.props.videoElement.buffered.length - 1
3353
+ );
3354
+ }
3355
+ return null;
3356
+ };
3357
+ this.getInternalPlayer = (key = "player") => {
3358
+ if (key === "video") return this.props.videoElement;
3359
+ return null;
3360
+ };
3361
+ this.enablePIP = async () => {
3362
+ if (this.props.videoElement && "requestPictureInPicture" in this.props.videoElement) {
3363
+ try {
3364
+ await this.props.videoElement.requestPictureInPicture();
3365
+ } catch (error) {
3366
+ console.warn("Picture-in-Picture failed:", error);
3367
+ }
3368
+ }
3369
+ };
3370
+ this.disablePIP = async () => {
3371
+ if (document.pictureInPictureElement) {
3372
+ try {
3373
+ await document.exitPictureInPicture();
3374
+ } catch (error) {
3375
+ console.warn("Exit Picture-in-Picture failed:", error);
3376
+ }
3377
+ }
3378
+ };
3379
+ }
3380
+ componentDidMount() {
3381
+ this.mounted = true;
3382
+ this.load();
3383
+ }
3384
+ componentWillUnmount() {
3385
+ this.mounted = false;
3386
+ }
3387
+ componentDidUpdate(prevProps) {
3388
+ if (prevProps.src !== this.props.src) {
3389
+ this.load();
3390
+ }
3391
+ }
3392
+ render() {
3393
+ return null;
3394
+ }
3395
+ };
3396
+ FilePlayer.displayName = "FilePlayer";
3397
+ FilePlayer.canPlay = canPlay.file;
3398
+
3399
+ // src/players/index.ts
3400
+ var players = [
3401
+ {
3402
+ key: "hls",
3403
+ name: "HLS Player",
3404
+ canPlay: canPlay.hls,
3405
+ lazyPlayer: lazy(() => Promise.resolve({ default: HlsPlayer }))
3406
+ },
3407
+ {
3408
+ key: "file",
3409
+ name: "File Player",
3410
+ canPlay: canPlay.file,
3411
+ canEnablePIP: (url) => {
3412
+ return canPlay.file(url) && (document.pictureInPictureEnabled || typeof document.webkitSupportsPresentationMode === "function");
3413
+ },
3414
+ lazyPlayer: lazy(() => Promise.resolve({ default: FilePlayer }))
3415
+ }
3416
+ ];
3417
+ var players_default = players;
3418
+
3419
+ // src/Player.tsx
3420
+ var import_react5 = __toESM(require("react"), 1);
3421
+ var SEEK_ON_PLAY_EXPIRY = 5e3;
3422
+ var Player = class extends import_react5.Component {
3423
+ constructor() {
3424
+ super(...arguments);
3425
+ this.mounted = false;
3426
+ this.isReady = false;
3427
+ this.isPlaying = false;
3428
+ this.isLoading = true;
3429
+ this.loadOnReady = null;
3430
+ this.startOnPlay = true;
3431
+ this.seekOnPlay = null;
3432
+ this.onDurationCalled = false;
3433
+ this.handlePlayerMount = (player) => {
3434
+ if (this.player) {
3435
+ this.progress();
3436
+ return;
3437
+ }
3438
+ this.player = player;
3439
+ this.player.load(this.props.src);
3440
+ this.progress();
3441
+ };
3442
+ this.getInternalPlayer = (key) => {
3443
+ if (!this.player) return null;
3444
+ return this.player.getInternalPlayer(key);
3445
+ };
3446
+ this.progress = () => {
3447
+ if (this.props.src && this.player && this.isReady) {
3448
+ const playedSeconds = this.getCurrentTime() || 0;
3449
+ const loadedSeconds = this.getSecondsLoaded();
3450
+ const duration = this.getDuration();
3451
+ if (duration) {
3452
+ const progress = {
3453
+ playedSeconds,
3454
+ played: playedSeconds / duration,
3455
+ loaded: 0,
3456
+ loadedSeconds: 0
3457
+ };
3458
+ if (loadedSeconds !== null) {
3459
+ progress.loadedSeconds = loadedSeconds;
3460
+ progress.loaded = loadedSeconds / duration;
3461
+ }
3462
+ if (progress.playedSeconds !== this.prevPlayed || progress.loadedSeconds !== this.prevLoaded) {
3463
+ this.props.onProgress?.(progress);
3464
+ }
3465
+ this.prevPlayed = progress.playedSeconds;
3466
+ this.prevLoaded = progress.loadedSeconds;
3467
+ }
3468
+ }
3469
+ this.progressTimeout = window.setTimeout(
3470
+ this.progress,
3471
+ this.props.progressInterval || 1e3
3472
+ );
3473
+ };
3474
+ this.handleReady = () => {
3475
+ if (!this.mounted) return;
3476
+ this.isReady = true;
3477
+ this.isLoading = false;
3478
+ const { onReady, playing, volume, muted } = this.props;
3479
+ onReady();
3480
+ if (!muted && volume !== null) {
3481
+ this.player.setVolume(volume);
3482
+ }
3483
+ if (this.loadOnReady) {
3484
+ this.player.load(this.loadOnReady, true);
3485
+ this.loadOnReady = null;
3486
+ } else if (playing) {
3487
+ this.player.play();
3488
+ }
3489
+ this.handleDurationCheck();
3490
+ };
3491
+ this.handlePlay = () => {
3492
+ this.isPlaying = true;
3493
+ this.isLoading = false;
3494
+ const { onStart, onPlay, playbackRate } = this.props;
3495
+ if (this.startOnPlay) {
3496
+ if (this.player.setPlaybackRate && playbackRate !== 1) {
3497
+ this.player.setPlaybackRate(playbackRate);
3498
+ }
3499
+ onStart?.();
3500
+ this.startOnPlay = false;
3501
+ }
3502
+ onPlay?.();
3503
+ if (this.seekOnPlay) {
3504
+ this.seekTo(this.seekOnPlay);
3505
+ this.seekOnPlay = null;
3506
+ }
3507
+ this.handleDurationCheck();
3508
+ };
3509
+ this.handlePause = (e) => {
3510
+ this.isPlaying = false;
3511
+ if (!this.isLoading) {
3512
+ this.props.onPause?.(e);
3513
+ }
3514
+ };
3515
+ this.handleEnded = () => {
3516
+ const { activePlayer, loop, onEnded } = this.props;
3517
+ if (activePlayer.loopOnEnded && loop) {
3518
+ this.seekTo(0);
3519
+ }
3520
+ if (!loop) {
3521
+ this.isPlaying = false;
3522
+ onEnded?.();
3523
+ }
3524
+ };
3525
+ this.handleError = (...args) => {
3526
+ this.isLoading = false;
3527
+ this.props.onError?.(args[0], args[1], args[2], args[3]);
3528
+ };
3529
+ this.handleDurationCheck = () => {
3530
+ clearTimeout(this.durationCheckTimeout);
3531
+ const duration = this.getDuration();
3532
+ if (duration) {
3533
+ if (!this.onDurationCalled) {
3534
+ this.props.onDuration?.(duration);
3535
+ this.onDurationCalled = true;
3536
+ }
3537
+ } else {
3538
+ this.durationCheckTimeout = window.setTimeout(
3539
+ this.handleDurationCheck,
3540
+ 100
3541
+ );
3542
+ }
3543
+ };
3544
+ this.handleLoaded = () => {
3545
+ this.isLoading = false;
3546
+ };
3547
+ }
3548
+ componentDidMount() {
3549
+ this.mounted = true;
3550
+ }
3551
+ componentWillUnmount() {
3552
+ clearTimeout(this.progressTimeout);
3553
+ clearTimeout(this.durationCheckTimeout);
3554
+ this.mounted = false;
3555
+ }
3556
+ componentDidUpdate(prevProps) {
3557
+ if (!this.player) return;
3558
+ const { src, playing, volume, muted, playbackRate, loop, activePlayer } = this.props;
3559
+ if (prevProps.src !== src) {
3560
+ if (this.isLoading && !activePlayer.forceLoad && !isMediaStream(src)) {
3561
+ console.warn(
3562
+ `StormcloudPlayer: the attempt to load ${src} is being deferred until the player has loaded`
3563
+ );
3564
+ this.loadOnReady = src || null;
3565
+ return;
3566
+ }
3567
+ this.isLoading = true;
3568
+ this.startOnPlay = true;
3569
+ this.onDurationCalled = false;
3570
+ this.player.load(src, this.isReady);
3571
+ }
3572
+ if (!prevProps.playing && playing && !this.isPlaying) {
3573
+ this.player.play();
3574
+ }
3575
+ if (prevProps.playing && !playing && this.isPlaying) {
3576
+ this.player.pause();
3577
+ }
3578
+ if (prevProps.volume !== volume && volume !== null) {
3579
+ this.player.setVolume(volume);
3580
+ }
3581
+ if (prevProps.muted !== muted) {
3582
+ if (muted) {
3583
+ this.player.mute();
3584
+ } else {
3585
+ this.player.unmute();
3586
+ if (volume !== null) {
3587
+ setTimeout(() => this.player.setVolume(volume));
3588
+ }
3589
+ }
3590
+ }
3591
+ if (prevProps.playbackRate !== playbackRate && this.player.setPlaybackRate) {
3592
+ this.player.setPlaybackRate(playbackRate);
3593
+ }
3594
+ if (prevProps.loop !== loop && this.player.setLoop) {
3595
+ this.player.setLoop(loop);
3596
+ }
3597
+ }
3598
+ getDuration() {
3599
+ if (!this.isReady) return null;
3600
+ return this.player.getDuration();
3601
+ }
3602
+ getCurrentTime() {
3603
+ if (!this.isReady) return null;
3604
+ return this.player.getCurrentTime();
3605
+ }
3606
+ getSecondsLoaded() {
3607
+ if (!this.isReady) return null;
3608
+ return this.player.getSecondsLoaded();
3609
+ }
3610
+ seekTo(amount, type, keepPlaying) {
3611
+ if (!this.isReady) {
3612
+ if (amount !== 0) {
3613
+ this.seekOnPlay = amount;
3614
+ setTimeout(() => {
3615
+ this.seekOnPlay = null;
3616
+ }, SEEK_ON_PLAY_EXPIRY);
3617
+ }
3618
+ return;
3619
+ }
3620
+ const isFraction = !type ? amount > 0 && amount < 1 : type === "fraction";
3621
+ if (isFraction) {
3622
+ const duration = this.player.getDuration();
3623
+ if (!duration) {
3624
+ console.warn(
3625
+ "StormcloudPlayer: could not seek using fraction \u2013 duration not yet available"
3626
+ );
3627
+ return;
3628
+ }
3629
+ this.player.seekTo(duration * amount, keepPlaying);
3630
+ return;
3631
+ }
3632
+ this.player.seekTo(amount, keepPlaying);
3633
+ }
3634
+ render() {
3635
+ const Player2 = this.props.activePlayer;
3636
+ if (!Player2) {
3637
+ return null;
3638
+ }
3639
+ return import_react5.default.createElement(Player2, {
3640
+ ...this.props,
3641
+ onMount: this.handlePlayerMount,
3642
+ onReady: this.handleReady,
3643
+ onPlay: this.handlePlay,
3644
+ onPause: this.handlePause,
3645
+ onEnded: this.handleEnded,
3646
+ onLoaded: this.handleLoaded,
3647
+ onError: this.handleError
3648
+ });
3649
+ }
3650
+ };
3651
+ Player.displayName = "Player";
3652
+ Player.defaultProps = defaultProps;
3653
+
3654
+ // src/StormcloudPlayer.tsx
3655
+ var IS_BROWSER2 = typeof window !== "undefined" && window.document;
3656
+ var IS_GLOBAL2 = typeof globalThis !== "undefined" && globalThis.window && globalThis.window.document;
3657
+ var UniversalSuspense = IS_BROWSER2 || IS_GLOBAL2 ? import_react6.Suspense : () => null;
3658
+ var SUPPORTED_PROPS = [
3659
+ "src",
3660
+ "playing",
3661
+ "loop",
3662
+ "controls",
3663
+ "volume",
3664
+ "muted",
3665
+ "playbackRate",
3666
+ "width",
3667
+ "height",
3668
+ "style",
3669
+ "progressInterval",
3670
+ "playsInline",
3671
+ "autoplay",
3672
+ "preload",
3673
+ "poster",
3674
+ "className",
3675
+ "wrapperClassName",
3676
+ "wrapperStyle",
3677
+ "allowNativeHls",
3678
+ "lowLatencyMode",
3679
+ "driftToleranceMs",
3680
+ "immediateManifestAds",
3681
+ "debugAdTiming",
3682
+ "showCustomControls",
3683
+ "licenseKey",
3684
+ "adFailsafeTimeoutMs",
3685
+ "onReady",
3686
+ "onStart",
3687
+ "onPlay",
3688
+ "onPause",
3689
+ "onBuffer",
3690
+ "onBufferEnd",
3691
+ "onEnded",
3692
+ "onError",
3693
+ "onDuration",
3694
+ "onSeek",
3695
+ "onProgress",
3696
+ "onVolumeToggle",
3697
+ "onFullscreenToggle",
3698
+ "onControlClick"
3699
+ ];
3700
+ var customPlayers = [];
3701
+ var createStormcloudPlayer = (playerList, fallback) => {
3702
+ var _a;
3703
+ return _a = class extends import_react6.Component {
3704
+ constructor() {
3705
+ super(...arguments);
3706
+ this.state = {
3707
+ showPreview: false
3708
+ };
3709
+ this.references = {
3710
+ wrapper: (wrapper) => {
3711
+ this.wrapper = wrapper;
3712
+ },
3713
+ player: (player) => {
3714
+ this.player = player;
3715
+ }
3716
+ };
3717
+ this.getActivePlayer = (src) => {
3718
+ if (!src) return null;
3719
+ for (const player of [...customPlayers, ...playerList]) {
3720
+ if (player.canPlay(src)) {
3721
+ return player;
3722
+ }
3723
+ }
3724
+ if (fallback) {
3725
+ return fallback;
3726
+ }
3727
+ return null;
3728
+ };
3729
+ this.getAttributes = (src) => {
3730
+ return omit(this.props, SUPPORTED_PROPS);
3731
+ };
3732
+ this.handleReady = () => {
3733
+ this.props.onReady?.(this);
3734
+ };
3735
+ this.seekTo = (fraction, type, keepPlaying) => {
3736
+ if (!this.player) return null;
3737
+ this.player.seekTo(fraction, type, keepPlaying);
3738
+ };
3739
+ this.getCurrentTime = () => {
3740
+ if (!this.player) return null;
3741
+ return this.player.getCurrentTime();
3742
+ };
3743
+ this.getSecondsLoaded = () => {
3744
+ if (!this.player) return null;
3745
+ return this.player.getSecondsLoaded();
3746
+ };
3747
+ this.getDuration = () => {
3748
+ if (!this.player) return null;
3749
+ return this.player.getDuration();
3750
+ };
3751
+ this.getInternalPlayer = (key = "player") => {
3752
+ if (!this.player) return null;
3753
+ return this.player.getInternalPlayer(key);
3754
+ };
3755
+ this.renderActivePlayer = (src) => {
3756
+ if (!src) return null;
3757
+ const activePlayer = this.getActivePlayer(src);
3758
+ if (!activePlayer) return null;
3759
+ return import_react6.default.createElement(Player, {
3760
+ ...this.props,
3761
+ key: activePlayer.key,
3762
+ ref: this.references.player,
3763
+ activePlayer: activePlayer.lazyPlayer || activePlayer,
3764
+ onReady: this.handleReady
3765
+ });
3766
+ };
3767
+ }
3768
+ render() {
3769
+ const {
3770
+ src,
3771
+ style,
3772
+ width,
3773
+ height,
3774
+ fallback: fallbackElement,
3775
+ wrapper: Wrapper
3776
+ } = this.props;
3777
+ const attributes = this.getAttributes(src);
3778
+ const wrapperRef = typeof Wrapper === "string" ? this.references.wrapper : void 0;
3779
+ return import_react6.default.createElement(
3780
+ Wrapper,
3781
+ {
3782
+ ref: wrapperRef,
3783
+ style: { ...style, width, height },
3784
+ ...attributes
3785
+ },
3786
+ import_react6.default.createElement(
3787
+ UniversalSuspense,
3788
+ { fallback: fallbackElement },
3789
+ this.renderActivePlayer(src)
3790
+ )
3791
+ );
3792
+ }
3793
+ }, _a.displayName = "StormcloudPlayer", _a.defaultProps = {
3794
+ ...defaultProps,
3795
+ fallback: null,
3796
+ wrapper: "div"
3797
+ }, _a.addCustomPlayer = (player) => {
3798
+ customPlayers.push(player);
3799
+ }, _a.removeCustomPlayers = () => {
3800
+ customPlayers.length = 0;
3801
+ }, _a.canPlay = (src) => {
3802
+ for (const Player2 of [...customPlayers, ...playerList]) {
3803
+ if (Player2.canPlay(src)) {
3804
+ return true;
3805
+ }
3806
+ }
3807
+ return false;
3808
+ }, _a.canEnablePIP = (src) => {
3809
+ for (const Player2 of [...customPlayers, ...playerList]) {
3810
+ if (Player2.canEnablePIP && Player2.canEnablePIP(src)) {
3811
+ return true;
3812
+ }
3813
+ }
3814
+ return false;
3815
+ }, _a;
3816
+ };
3817
+ var StormcloudPlayer = createStormcloudPlayer(
3818
+ players_default,
3819
+ players_default[players_default.length - 1]
3820
+ );
3821
+ var StormcloudPlayer_default = StormcloudPlayer;
2907
3822
  // Annotate the CommonJS export names for ESM import in node:
2908
3823
  0 && (module.exports = {
3824
+ IS_BROWSER,
3825
+ IS_GLOBAL,
3826
+ IS_IOS,
3827
+ IS_SAFARI,
3828
+ SUPPORTS_DASH,
3829
+ SUPPORTS_HLS,
3830
+ StormcloudPlayer,
2909
3831
  StormcloudVideoPlayer,
2910
3832
  StormcloudVideoPlayerComponent,
3833
+ canPlay,
3834
+ createStormcloudPlayer,
2911
3835
  getBrowserID,
2912
3836
  getClientInfo,
3837
+ isMediaStream,
3838
+ lazy,
3839
+ merge,
3840
+ omit,
3841
+ parseQuery,
3842
+ players,
3843
+ randomString,
2913
3844
  sendHeartbeat,
2914
- sendInitialTracking
3845
+ sendInitialTracking,
3846
+ supportsWebKitPresentationMode
2915
3847
  });
2916
3848
  //# sourceMappingURL=index.cjs.map