@cloudflare/stream-react
Advanced tools
Comparing version 0.3.0 to 1.0.0-rc.1
@@ -1,1 +0,3 @@ | ||
export { Stream, StreamProps, HTMLStreamElement } from "./Stream"; | ||
export { Stream } from "./Stream"; | ||
export { useStreamSDK } from "./useStreamSDK"; | ||
export { StreamPlayerApi, StreamProps } from "./types"; |
@@ -10,75 +10,55 @@ 'use strict'; | ||
var scriptLocation = "https://embed.videodelivery.net/embed/r4xu.fla9.latest.js"; | ||
var sdkScriptLocation = "https://embed.videodelivery.net/embed/sdk.latest.js"; | ||
function useStreamSDK() { | ||
// Because we're storing the Stream function in state, we need to pass a | ||
// function in that returns it because React will invoke functions that | ||
// are passed into state | ||
var _useState = React.useState(function () { | ||
return window.Stream; | ||
}), | ||
streamSdk = _useState[0], | ||
setStreamSdk = _useState[1]; | ||
function useStreamElement(containerRef, streamRef) { | ||
// Need to create stream element with document.createElement | ||
// because React will log console warnings if we render | ||
// invalid HTML elements in JSX | ||
React.useEffect(function () { | ||
if (!containerRef.current) return; // grab current container from ref so we can use it in the | ||
// callback we return for cleanup. | ||
if (!streamSdk) { | ||
var existingScript = document.querySelector("script[src='https://embed.videodelivery.net/embed/sdk.latest.js']"); | ||
var script = existingScript !== null && existingScript !== void 0 ? existingScript : document.createElement("script"); | ||
script.addEventListener("load", function () { | ||
// Same thing here, passing in a function that will return window.Stream | ||
setStreamSdk(function () { | ||
return window.Stream; | ||
}); | ||
}); | ||
var container = containerRef.current; | ||
var stream = document.createElement("stream"); // store stream element on ref | ||
streamRef.current = stream; // insert stream element into dom | ||
container.appendChild(stream); | ||
return function () { | ||
// clean up before unmounting or re-running the effect | ||
container.removeChild(stream); | ||
}; | ||
}, [streamRef, containerRef]); | ||
if (!existingScript) { | ||
script.src = sdkScriptLocation; | ||
document.head.appendChild(script); | ||
} | ||
} | ||
}, [streamSdk]); | ||
return streamSdk; | ||
} | ||
/** | ||
* Script to load the player. This initializes the player on the stream element | ||
*/ | ||
function useStreamScript(ref) { | ||
React.useEffect(function () { | ||
var existingScript = document.querySelector("script[src=\"" + scriptLocation + "\"]"); | ||
if (existingScript === null) { | ||
var streamScript = document.createElement("script"); | ||
streamScript.setAttribute("data-cfasync", "false"); | ||
streamScript.setAttribute("defer", "true"); | ||
streamScript.setAttribute("type", "text/javascript"); | ||
streamScript.setAttribute("src", scriptLocation); | ||
document.head.appendChild(streamScript); | ||
return; | ||
} | ||
var streamElement = ref.current; | ||
if (window.__stream && streamElement) { | ||
window.__stream.initElement(streamElement); | ||
} // no dependencies in the dependency array means this only fires on mount | ||
// and the cleanup only fires on unmount. | ||
}, []); | ||
function useIframeSrc(src, _ref) { | ||
var muted = _ref.muted, | ||
preload = _ref.preload, | ||
loop = _ref.loop, | ||
autoplay = _ref.autoplay, | ||
controls = _ref.controls, | ||
poster = _ref.poster, | ||
adUrl = _ref.adUrl; | ||
var paramString = [poster && "poster=" + encodeURIComponent(poster), adUrl && "ad-url=" + encodeURIComponent(adUrl), muted && "muted=true", preload && "preload=" + preload, loop && "loop=true", autoplay && "autoplay=true", !controls && "controls=false"].filter(Boolean).join("&"); | ||
var iframeSrc = React.useMemo(function () { | ||
return "https://iframe.videodelivery.net/" + src + "?" + paramString; | ||
}, // we intentionally do NOT include paramString here because we want | ||
// to avoid changing the URL when these options change. Changes to | ||
// these options will instead be handled separately via the SDK. | ||
[src]); | ||
return iframeSrc; | ||
} | ||
/** | ||
* Hook for syncing attributes to an element when they change | ||
*/ | ||
function useAttribute(attributeName, ref, value) { | ||
React.useEffect(function () { | ||
if (!ref.current) return; | ||
var el = ref.current; // explicitly checking for false or undefined to remove attribbutes | ||
// so that other falsy values such as 0 or "" may be set | ||
if (value === false || value === undefined) { | ||
el.removeAttribute(attributeName); | ||
} else { | ||
el.setAttribute(attributeName, value.toString()); | ||
} | ||
}, [attributeName, value, ref]); | ||
} | ||
/** | ||
* Hook for syncing properties to an element when they change | ||
* Hook for syncing properties to the SDK api when they change | ||
*/ | ||
function useProperty(name, ref, value) { | ||
@@ -117,75 +97,127 @@ React.useEffect(function () { | ||
var Stream = function Stream(_ref) { | ||
var src = _ref.src, | ||
adUrl = _ref.adUrl, | ||
controls = _ref.controls, | ||
muted = _ref.muted, | ||
autoplay = _ref.autoplay, | ||
loop = _ref.loop, | ||
preload = _ref.preload, | ||
height = _ref.height, | ||
width = _ref.width, | ||
poster = _ref.poster, | ||
currentTime = _ref.currentTime, | ||
volume = _ref.volume, | ||
streamRef = _ref.streamRef, | ||
onAbort = _ref.onAbort, | ||
onCanPlay = _ref.onCanPlay, | ||
onCanPlayThrough = _ref.onCanPlayThrough, | ||
onDurationChange = _ref.onDurationChange, | ||
onEnded = _ref.onEnded, | ||
onError = _ref.onError, | ||
onLoadedData = _ref.onLoadedData, | ||
onLoadedMetaData = _ref.onLoadedMetaData, | ||
onLoadStart = _ref.onLoadStart, | ||
onPause = _ref.onPause, | ||
onPlay = _ref.onPlay, | ||
onPlaying = _ref.onPlaying, | ||
onProgress = _ref.onProgress, | ||
onRateChange = _ref.onRateChange, | ||
onSeeked = _ref.onSeeked, | ||
onSeeking = _ref.onSeeking, | ||
onStalled = _ref.onStalled, | ||
onSuspend = _ref.onSuspend, | ||
onTimeUpdate = _ref.onTimeUpdate, | ||
onVolumeChange = _ref.onVolumeChange, | ||
onWaiting = _ref.onWaiting, | ||
onStreamAdStart = _ref.onStreamAdStart, | ||
onStreamAdEnd = _ref.onStreamAdEnd, | ||
onStreamAdTimeout = _ref.onStreamAdTimeout; | ||
// initialize with no argument to get a mutable ref back instead | ||
// of readonly RefObject which cannot be mutated directly | ||
var internalStreamRef = React.useRef(null); // Because useRef needs to be called the same number of times | ||
// across renders, we create an internal ref that we only use | ||
// when playerRef is not provided | ||
var Stream = function Stream(props) { | ||
var streamSdk = useStreamSDK(); | ||
return streamSdk ? React__default.createElement(StreamEmbed, Object.assign({}, props)) : null; | ||
}; | ||
var responsiveIframeStyles = { | ||
position: "absolute", | ||
top: 0, | ||
left: 0, | ||
right: 0, | ||
bottom: 0, | ||
height: "100%", | ||
width: "100%" | ||
}; | ||
var ref = streamRef !== null && streamRef !== void 0 ? streamRef : internalStreamRef; // initialize with null to get readonly RefObject back since we | ||
// don't need to mutate this ref | ||
var Container = function Container(_ref) { | ||
var children = _ref.children, | ||
responsive = _ref.responsive, | ||
className = _ref.className, | ||
videoDimensions = _ref.videoDimensions; | ||
var videoHeight = videoDimensions.videoHeight, | ||
videoWidth = videoDimensions.videoWidth; | ||
var responsiveStyles = React.useMemo(function () { | ||
return { | ||
position: "relative", | ||
paddingTop: videoWidth > 0 ? videoHeight / videoWidth * 100 + "%" : undefined | ||
}; | ||
}, [videoWidth, videoHeight]); | ||
return React__default.createElement("div", { | ||
className: className, | ||
style: responsive ? responsiveStyles : undefined | ||
}, children); | ||
}; | ||
var containerRef = React.useRef(null); | ||
useStreamElement(containerRef, ref); // set attributes | ||
var StreamEmbed = function StreamEmbed(_ref2) { | ||
var src = _ref2.src, | ||
adUrl = _ref2.adUrl, | ||
_ref2$controls = _ref2.controls, | ||
controls = _ref2$controls === void 0 ? false : _ref2$controls, | ||
_ref2$muted = _ref2.muted, | ||
muted = _ref2$muted === void 0 ? false : _ref2$muted, | ||
_ref2$autoplay = _ref2.autoplay, | ||
autoplay = _ref2$autoplay === void 0 ? false : _ref2$autoplay, | ||
_ref2$loop = _ref2.loop, | ||
loop = _ref2$loop === void 0 ? false : _ref2$loop, | ||
_ref2$preload = _ref2.preload, | ||
preload = _ref2$preload === void 0 ? "metadata" : _ref2$preload, | ||
height = _ref2.height, | ||
width = _ref2.width, | ||
poster = _ref2.poster, | ||
_ref2$currentTime = _ref2.currentTime, | ||
currentTime = _ref2$currentTime === void 0 ? 0 : _ref2$currentTime, | ||
_ref2$volume = _ref2.volume, | ||
volume = _ref2$volume === void 0 ? 1 : _ref2$volume, | ||
streamRef = _ref2.streamRef, | ||
_ref2$responsive = _ref2.responsive, | ||
responsive = _ref2$responsive === void 0 ? true : _ref2$responsive, | ||
className = _ref2.className, | ||
onAbort = _ref2.onAbort, | ||
onCanPlay = _ref2.onCanPlay, | ||
onCanPlayThrough = _ref2.onCanPlayThrough, | ||
onDurationChange = _ref2.onDurationChange, | ||
onEnded = _ref2.onEnded, | ||
onError = _ref2.onError, | ||
onLoadedData = _ref2.onLoadedData, | ||
onLoadedMetaData = _ref2.onLoadedMetaData, | ||
onLoadStart = _ref2.onLoadStart, | ||
onPause = _ref2.onPause, | ||
onPlay = _ref2.onPlay, | ||
onPlaying = _ref2.onPlaying, | ||
onProgress = _ref2.onProgress, | ||
onRateChange = _ref2.onRateChange, | ||
onResize = _ref2.onResize, | ||
onSeeked = _ref2.onSeeked, | ||
onSeeking = _ref2.onSeeking, | ||
onStalled = _ref2.onStalled, | ||
onSuspend = _ref2.onSuspend, | ||
onTimeUpdate = _ref2.onTimeUpdate, | ||
onVolumeChange = _ref2.onVolumeChange, | ||
onWaiting = _ref2.onWaiting, | ||
onStreamAdStart = _ref2.onStreamAdStart, | ||
onStreamAdEnd = _ref2.onStreamAdEnd, | ||
onStreamAdTimeout = _ref2.onStreamAdTimeout; | ||
var internalRef = React.useRef(); | ||
var ref = streamRef !== null && streamRef !== void 0 ? streamRef : internalRef; | ||
useAttribute("ad-url", ref, adUrl); | ||
useAttribute("src", ref, src); | ||
useAttribute("autoplay", ref, autoplay); | ||
useAttribute("controls", ref, controls); | ||
useAttribute("loop", ref, loop); | ||
useAttribute("preload", ref, preload); | ||
useAttribute("height", ref, height); | ||
useAttribute("width", ref, width); | ||
useAttribute("poster", ref, poster); | ||
useAttribute("muted", ref, muted); // These props need to be set directly on the stream element as properties | ||
// in order for the player to respond to them when they change | ||
var _useState = React.useState({ | ||
videoHeight: 0, | ||
videoWidth: 0 | ||
}), | ||
videoDimensions = _useState[0], | ||
setVideoDimensions = _useState[1]; | ||
useProperty("autoplay", ref, autoplay !== null && autoplay !== void 0 ? autoplay : false); | ||
useProperty("controls", ref, controls !== null && controls !== void 0 ? controls : false); | ||
useProperty("currentTime", ref, currentTime !== null && currentTime !== void 0 ? currentTime : 0); | ||
useProperty("muted", ref, muted !== null && muted !== void 0 ? muted : false); | ||
useProperty("loop", ref, loop !== null && loop !== void 0 ? loop : false); | ||
useProperty("volume", ref, volume !== null && volume !== void 0 ? volume : 1); | ||
useProperty("preload", ref, typeof preload === "string" ? // if it's a string pass as is | ||
preload : // else if it's true, map to auto | ||
preload === true ? "auto" : // otherwise (undefined | false) maps to none | ||
"none"); // bind events | ||
var iframeRef = React.useRef(null); | ||
var iframeSrc = useIframeSrc(src, { | ||
muted: muted, | ||
preload: preload, | ||
loop: loop, | ||
autoplay: autoplay, | ||
controls: controls, | ||
poster: poster, | ||
adUrl: adUrl | ||
}); | ||
useProperty("muted", ref, muted); | ||
useProperty("controls", ref, controls); | ||
useProperty("src", ref, src); | ||
useProperty("autoplay", ref, autoplay); | ||
useProperty("currentTime", ref, currentTime); | ||
useProperty("loop", ref, loop); | ||
useProperty("preload", ref, preload); | ||
useProperty("volume", ref, volume); // instantiate API after properties are bound because we want undefined | ||
// values to be set before defining the properties | ||
React.useEffect(function () { | ||
if (iframeRef.current && window.Stream) { | ||
var api = window.Stream(iframeRef.current); | ||
ref.current = api; | ||
var videoHeight = api.videoHeight, | ||
videoWidth = api.videoWidth; | ||
if (videoHeight && videoWidth) setVideoDimensions({ | ||
videoHeight: videoHeight, | ||
videoWidth: videoWidth | ||
}); | ||
} | ||
}, []); // bind events | ||
useEvent("abort", ref, onAbort); | ||
@@ -214,15 +246,33 @@ useEvent("canplay", ref, onCanPlay); | ||
useEvent("stream-adend", ref, onStreamAdEnd); | ||
useEvent("stream-adtimeout", ref, onStreamAdTimeout); // stream element is set up from effects, load script | ||
useStreamScript(ref); | ||
return React__default.createElement("div", { | ||
style: { | ||
height: height, | ||
width: width | ||
}, | ||
ref: containerRef | ||
useEvent("stream-adtimeout", ref, onStreamAdTimeout); | ||
useEvent("resize", ref, function () { | ||
if (ref.current) { | ||
var _ref$current = ref.current, | ||
videoHeight = _ref$current.videoHeight, | ||
videoWidth = _ref$current.videoWidth; | ||
setVideoDimensions({ | ||
videoHeight: videoHeight, | ||
videoWidth: videoWidth | ||
}); | ||
onResize && onResize(); | ||
} | ||
}); | ||
return React__default.createElement(Container, { | ||
className: className, | ||
responsive: responsive, | ||
videoDimensions: videoDimensions | ||
}, React__default.createElement("iframe", { | ||
ref: iframeRef, | ||
src: iframeSrc, | ||
style: responsive ? responsiveIframeStyles : undefined, | ||
frameBorder: 0, | ||
height: height, | ||
width: width, | ||
allow: "accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture;", | ||
allowFullScreen: true | ||
})); | ||
}; | ||
exports.Stream = Stream; | ||
exports.useStreamSDK = useStreamSDK; | ||
//# sourceMappingURL=stream-react.cjs.development.js.map |
@@ -1,2 +0,2 @@ | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t=require("react"),n=(e=t)&&"object"==typeof e&&"default"in e?e.default:e,r="https://embed.videodelivery.net/embed/r4xu.fla9.latest.js";function a(e,n,r){t.useEffect((function(){if(n.current){var t=n.current;!1===r||void 0===r?t.removeAttribute(e):t.setAttribute(e,r.toString())}}),[e,r,n])}function o(e,n,r){t.useEffect((function(){n.current&&(n.current[e]=r)}),[e,r,n])}function u(e,n,r){void 0===r&&(r=d),t.useEffect((function(){if(n.current){var t=n.current;return t.addEventListener(e,r),function(){return t.removeEventListener(e,r)}}}),[r,e,n])}var d=function(){};exports.Stream=function(e){var d=e.src,i=e.adUrl,l=e.controls,c=e.muted,s=e.autoplay,f=e.loop,m=e.preload,p=e.height,h=e.width,v=e.poster,g=e.currentTime,y=e.volume,E=e.streamRef,b=e.onAbort,S=e.onCanPlay,A=e.onCanPlayThrough,w=e.onDurationChange,C=e.onEnded,P=e.onError,_=e.onLoadedData,L=e.onLoadedMetaData,T=e.onLoadStart,j=e.onPause,k=e.onPlay,x=e.onPlaying,R=e.onProgress,D=e.onRateChange,q=e.onSeeked,M=e.onSeeking,U=e.onStalled,O=e.onSuspend,V=e.onTimeUpdate,W=e.onVolumeChange,z=e.onWaiting,B=e.onStreamAdStart,F=e.onStreamAdEnd,G=e.onStreamAdTimeout,H=t.useRef(null),I=null!=E?E:H,J=t.useRef(null);return function(e,n){t.useEffect((function(){if(e.current){var t=e.current,r=document.createElement("stream");return n.current=r,t.appendChild(r),function(){t.removeChild(r)}}}),[n,e])}(J,I),a("ad-url",I,i),a("src",I,d),a("autoplay",I,s),a("controls",I,l),a("loop",I,f),a("preload",I,m),a("height",I,p),a("width",I,h),a("poster",I,v),a("muted",I,c),o("autoplay",I,null!=s&&s),o("controls",I,null!=l&&l),o("currentTime",I,null!=g?g:0),o("muted",I,null!=c&&c),o("loop",I,null!=f&&f),o("volume",I,null!=y?y:1),o("preload",I,"string"==typeof m?m:!0===m?"auto":"none"),u("abort",I,b),u("canplay",I,S),u("canplaythrough",I,A),u("durationchange",I,w),u("ended",I,C),u("error",I,P),u("loadeddata",I,_),u("loadedmetadata",I,L),u("loadstart",I,T),u("pause",I,j),u("play",I,k),u("playing",I,x),u("progress",I,R),u("ratechange",I,D),u("seeked",I,q),u("seeking",I,M),u("stalled",I,U),u("suspend",I,O),u("timeupdate",I,V),u("volumechange",I,W),u("waiting",I,z),u("stream-adstart",I,B),u("stream-adend",I,F),u("stream-adtimeout",I,G),function(e){t.useEffect((function(){if(null===document.querySelector('script[src="'+r+'"]')){var t=document.createElement("script");return t.setAttribute("data-cfasync","false"),t.setAttribute("defer","true"),t.setAttribute("type","text/javascript"),t.setAttribute("src",r),void document.head.appendChild(t)}var n=e.current;window.__stream&&n&&window.__stream.initElement(n)}),[])}(I),n.createElement("div",{style:{height:p,width:h},ref:J})}; | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t=require("react"),o=(e=t)&&"object"==typeof e&&"default"in e?e.default:e;function n(){var e=t.useState((function(){return window.Stream})),o=e[0],n=e[1];return t.useEffect((function(){if(!o){var e=document.querySelector("script[src='https://embed.videodelivery.net/embed/sdk.latest.js']"),t=null!=e?e:document.createElement("script");t.addEventListener("load",(function(){n((function(){return window.Stream}))})),e||(t.src="https://embed.videodelivery.net/embed/sdk.latest.js",document.head.appendChild(t))}}),[o]),o}function r(e,o,n){t.useEffect((function(){o.current&&(o.current[e]=n)}),[e,n,o])}function a(e,o,n){void 0===n&&(n=i),t.useEffect((function(){if(o.current){var t=o.current;return t.addEventListener(e,n),function(){return t.removeEventListener(e,n)}}}),[n,e,o])}var i=function(){},d={position:"absolute",top:0,left:0,right:0,bottom:0,height:"100%",width:"100%"},u=function(e){var n=e.children,r=e.responsive,a=e.className,i=e.videoDimensions,d=i.videoHeight,u=i.videoWidth,s=t.useMemo((function(){return{position:"relative",paddingTop:u>0?d/u*100+"%":void 0}}),[u,d]);return o.createElement("div",{className:a,style:r?s:void 0},n)},s=function(e){var n=e.src,i=e.adUrl,s=e.controls,l=void 0!==s&&s,c=e.muted,m=void 0!==c&&c,p=e.autoplay,v=void 0!==p&&p,f=e.loop,h=void 0!==f&&f,g=e.preload,y=void 0===g?"metadata":g,S=e.height,E=e.width,w=e.poster,b=e.currentTime,C=void 0===b?0:b,P=e.volume,R=void 0===P?1:P,W=e.streamRef,j=e.responsive,k=void 0===j||j,D=e.className,H=e.onAbort,L=e.onCanPlay,T=e.onCanPlayThrough,U=e.onDurationChange,A=e.onEnded,M=e.onError,N=e.onLoadedData,x=e.onLoadedMetaData,q=e.onLoadStart,z=e.onPause,B=e.onPlay,I=e.onPlaying,O=e.onProgress,_=e.onRateChange,F=e.onResize,K=e.onSeeked,V=e.onSeeking,G=e.onStalled,J=e.onSuspend,Q=e.onTimeUpdate,X=e.onVolumeChange,Y=e.onWaiting,Z=e.onStreamAdStart,$=e.onStreamAdEnd,ee=e.onStreamAdTimeout,te=t.useRef(),oe=null!=W?W:te,ne=t.useState({videoHeight:0,videoWidth:0}),re=ne[0],ae=ne[1],ie=t.useRef(null),de=function(e,o){var n=o.muted,r=o.preload,a=o.loop,i=o.autoplay,d=o.controls,u=o.poster,s=o.adUrl,l=[u&&"poster="+encodeURIComponent(u),s&&"ad-url="+encodeURIComponent(s),n&&"muted=true",r&&"preload="+r,a&&"loop=true",i&&"autoplay=true",!d&&"controls=false"].filter(Boolean).join("&");return t.useMemo((function(){return"https://iframe.videodelivery.net/"+e+"?"+l}),[e])}(n,{muted:m,preload:y,loop:h,autoplay:v,controls:l,poster:w,adUrl:i});return r("muted",oe,m),r("controls",oe,l),r("src",oe,n),r("autoplay",oe,v),r("currentTime",oe,C),r("loop",oe,h),r("preload",oe,y),r("volume",oe,R),t.useEffect((function(){if(ie.current&&window.Stream){var e=window.Stream(ie.current);oe.current=e;var t=e.videoHeight,o=e.videoWidth;t&&o&&ae({videoHeight:t,videoWidth:o})}}),[]),a("abort",oe,H),a("canplay",oe,L),a("canplaythrough",oe,T),a("durationchange",oe,U),a("ended",oe,A),a("error",oe,M),a("loadeddata",oe,N),a("loadedmetadata",oe,x),a("loadstart",oe,q),a("pause",oe,z),a("play",oe,B),a("playing",oe,I),a("progress",oe,O),a("ratechange",oe,_),a("seeked",oe,K),a("seeking",oe,V),a("stalled",oe,G),a("suspend",oe,J),a("timeupdate",oe,Q),a("volumechange",oe,X),a("waiting",oe,Y),a("stream-adstart",oe,Z),a("stream-adend",oe,$),a("stream-adtimeout",oe,ee),a("resize",oe,(function(){if(oe.current){var e=oe.current;ae({videoHeight:e.videoHeight,videoWidth:e.videoWidth}),F&&F()}})),o.createElement(u,{className:D,responsive:k,videoDimensions:re},o.createElement("iframe",{ref:ie,src:de,style:k?d:void 0,frameBorder:0,height:S,width:E,allow:"accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture;",allowFullScreen:!0}))};exports.Stream=function(e){return n()?o.createElement(s,Object.assign({},e)):null},exports.useStreamSDK=n; | ||
//# sourceMappingURL=stream-react.cjs.production.min.js.map |
@@ -1,76 +0,56 @@ | ||
import React, { useRef, useEffect } from 'react'; | ||
import React, { useState, useEffect, useMemo, useRef } from 'react'; | ||
var scriptLocation = "https://embed.videodelivery.net/embed/r4xu.fla9.latest.js"; | ||
var sdkScriptLocation = "https://embed.videodelivery.net/embed/sdk.latest.js"; | ||
function useStreamSDK() { | ||
// Because we're storing the Stream function in state, we need to pass a | ||
// function in that returns it because React will invoke functions that | ||
// are passed into state | ||
var _useState = useState(function () { | ||
return window.Stream; | ||
}), | ||
streamSdk = _useState[0], | ||
setStreamSdk = _useState[1]; | ||
function useStreamElement(containerRef, streamRef) { | ||
// Need to create stream element with document.createElement | ||
// because React will log console warnings if we render | ||
// invalid HTML elements in JSX | ||
useEffect(function () { | ||
if (!containerRef.current) return; // grab current container from ref so we can use it in the | ||
// callback we return for cleanup. | ||
if (!streamSdk) { | ||
var existingScript = document.querySelector("script[src='https://embed.videodelivery.net/embed/sdk.latest.js']"); | ||
var script = existingScript !== null && existingScript !== void 0 ? existingScript : document.createElement("script"); | ||
script.addEventListener("load", function () { | ||
// Same thing here, passing in a function that will return window.Stream | ||
setStreamSdk(function () { | ||
return window.Stream; | ||
}); | ||
}); | ||
var container = containerRef.current; | ||
var stream = document.createElement("stream"); // store stream element on ref | ||
streamRef.current = stream; // insert stream element into dom | ||
container.appendChild(stream); | ||
return function () { | ||
// clean up before unmounting or re-running the effect | ||
container.removeChild(stream); | ||
}; | ||
}, [streamRef, containerRef]); | ||
if (!existingScript) { | ||
script.src = sdkScriptLocation; | ||
document.head.appendChild(script); | ||
} | ||
} | ||
}, [streamSdk]); | ||
return streamSdk; | ||
} | ||
/** | ||
* Script to load the player. This initializes the player on the stream element | ||
*/ | ||
function useStreamScript(ref) { | ||
useEffect(function () { | ||
var existingScript = document.querySelector("script[src=\"" + scriptLocation + "\"]"); | ||
if (existingScript === null) { | ||
var streamScript = document.createElement("script"); | ||
streamScript.setAttribute("data-cfasync", "false"); | ||
streamScript.setAttribute("defer", "true"); | ||
streamScript.setAttribute("type", "text/javascript"); | ||
streamScript.setAttribute("src", scriptLocation); | ||
document.head.appendChild(streamScript); | ||
return; | ||
} | ||
var streamElement = ref.current; | ||
if (window.__stream && streamElement) { | ||
window.__stream.initElement(streamElement); | ||
} // no dependencies in the dependency array means this only fires on mount | ||
// and the cleanup only fires on unmount. | ||
}, []); | ||
function useIframeSrc(src, _ref) { | ||
var muted = _ref.muted, | ||
preload = _ref.preload, | ||
loop = _ref.loop, | ||
autoplay = _ref.autoplay, | ||
controls = _ref.controls, | ||
poster = _ref.poster, | ||
adUrl = _ref.adUrl; | ||
var paramString = [poster && "poster=" + encodeURIComponent(poster), adUrl && "ad-url=" + encodeURIComponent(adUrl), muted && "muted=true", preload && "preload=" + preload, loop && "loop=true", autoplay && "autoplay=true", !controls && "controls=false"].filter(Boolean).join("&"); | ||
var iframeSrc = useMemo(function () { | ||
return "https://iframe.videodelivery.net/" + src + "?" + paramString; | ||
}, // we intentionally do NOT include paramString here because we want | ||
// to avoid changing the URL when these options change. Changes to | ||
// these options will instead be handled separately via the SDK. | ||
[src]); | ||
return iframeSrc; | ||
} | ||
/** | ||
* Hook for syncing attributes to an element when they change | ||
*/ | ||
function useAttribute(attributeName, ref, value) { | ||
useEffect(function () { | ||
if (!ref.current) return; | ||
var el = ref.current; // explicitly checking for false or undefined to remove attribbutes | ||
// so that other falsy values such as 0 or "" may be set | ||
if (value === false || value === undefined) { | ||
el.removeAttribute(attributeName); | ||
} else { | ||
el.setAttribute(attributeName, value.toString()); | ||
} | ||
}, [attributeName, value, ref]); | ||
} | ||
/** | ||
* Hook for syncing properties to an element when they change | ||
* Hook for syncing properties to the SDK api when they change | ||
*/ | ||
function useProperty(name, ref, value) { | ||
@@ -109,75 +89,127 @@ useEffect(function () { | ||
var Stream = function Stream(_ref) { | ||
var src = _ref.src, | ||
adUrl = _ref.adUrl, | ||
controls = _ref.controls, | ||
muted = _ref.muted, | ||
autoplay = _ref.autoplay, | ||
loop = _ref.loop, | ||
preload = _ref.preload, | ||
height = _ref.height, | ||
width = _ref.width, | ||
poster = _ref.poster, | ||
currentTime = _ref.currentTime, | ||
volume = _ref.volume, | ||
streamRef = _ref.streamRef, | ||
onAbort = _ref.onAbort, | ||
onCanPlay = _ref.onCanPlay, | ||
onCanPlayThrough = _ref.onCanPlayThrough, | ||
onDurationChange = _ref.onDurationChange, | ||
onEnded = _ref.onEnded, | ||
onError = _ref.onError, | ||
onLoadedData = _ref.onLoadedData, | ||
onLoadedMetaData = _ref.onLoadedMetaData, | ||
onLoadStart = _ref.onLoadStart, | ||
onPause = _ref.onPause, | ||
onPlay = _ref.onPlay, | ||
onPlaying = _ref.onPlaying, | ||
onProgress = _ref.onProgress, | ||
onRateChange = _ref.onRateChange, | ||
onSeeked = _ref.onSeeked, | ||
onSeeking = _ref.onSeeking, | ||
onStalled = _ref.onStalled, | ||
onSuspend = _ref.onSuspend, | ||
onTimeUpdate = _ref.onTimeUpdate, | ||
onVolumeChange = _ref.onVolumeChange, | ||
onWaiting = _ref.onWaiting, | ||
onStreamAdStart = _ref.onStreamAdStart, | ||
onStreamAdEnd = _ref.onStreamAdEnd, | ||
onStreamAdTimeout = _ref.onStreamAdTimeout; | ||
// initialize with no argument to get a mutable ref back instead | ||
// of readonly RefObject which cannot be mutated directly | ||
var internalStreamRef = useRef(null); // Because useRef needs to be called the same number of times | ||
// across renders, we create an internal ref that we only use | ||
// when playerRef is not provided | ||
var Stream = function Stream(props) { | ||
var streamSdk = useStreamSDK(); | ||
return streamSdk ? React.createElement(StreamEmbed, Object.assign({}, props)) : null; | ||
}; | ||
var responsiveIframeStyles = { | ||
position: "absolute", | ||
top: 0, | ||
left: 0, | ||
right: 0, | ||
bottom: 0, | ||
height: "100%", | ||
width: "100%" | ||
}; | ||
var ref = streamRef !== null && streamRef !== void 0 ? streamRef : internalStreamRef; // initialize with null to get readonly RefObject back since we | ||
// don't need to mutate this ref | ||
var Container = function Container(_ref) { | ||
var children = _ref.children, | ||
responsive = _ref.responsive, | ||
className = _ref.className, | ||
videoDimensions = _ref.videoDimensions; | ||
var videoHeight = videoDimensions.videoHeight, | ||
videoWidth = videoDimensions.videoWidth; | ||
var responsiveStyles = useMemo(function () { | ||
return { | ||
position: "relative", | ||
paddingTop: videoWidth > 0 ? videoHeight / videoWidth * 100 + "%" : undefined | ||
}; | ||
}, [videoWidth, videoHeight]); | ||
return React.createElement("div", { | ||
className: className, | ||
style: responsive ? responsiveStyles : undefined | ||
}, children); | ||
}; | ||
var containerRef = useRef(null); | ||
useStreamElement(containerRef, ref); // set attributes | ||
var StreamEmbed = function StreamEmbed(_ref2) { | ||
var src = _ref2.src, | ||
adUrl = _ref2.adUrl, | ||
_ref2$controls = _ref2.controls, | ||
controls = _ref2$controls === void 0 ? false : _ref2$controls, | ||
_ref2$muted = _ref2.muted, | ||
muted = _ref2$muted === void 0 ? false : _ref2$muted, | ||
_ref2$autoplay = _ref2.autoplay, | ||
autoplay = _ref2$autoplay === void 0 ? false : _ref2$autoplay, | ||
_ref2$loop = _ref2.loop, | ||
loop = _ref2$loop === void 0 ? false : _ref2$loop, | ||
_ref2$preload = _ref2.preload, | ||
preload = _ref2$preload === void 0 ? "metadata" : _ref2$preload, | ||
height = _ref2.height, | ||
width = _ref2.width, | ||
poster = _ref2.poster, | ||
_ref2$currentTime = _ref2.currentTime, | ||
currentTime = _ref2$currentTime === void 0 ? 0 : _ref2$currentTime, | ||
_ref2$volume = _ref2.volume, | ||
volume = _ref2$volume === void 0 ? 1 : _ref2$volume, | ||
streamRef = _ref2.streamRef, | ||
_ref2$responsive = _ref2.responsive, | ||
responsive = _ref2$responsive === void 0 ? true : _ref2$responsive, | ||
className = _ref2.className, | ||
onAbort = _ref2.onAbort, | ||
onCanPlay = _ref2.onCanPlay, | ||
onCanPlayThrough = _ref2.onCanPlayThrough, | ||
onDurationChange = _ref2.onDurationChange, | ||
onEnded = _ref2.onEnded, | ||
onError = _ref2.onError, | ||
onLoadedData = _ref2.onLoadedData, | ||
onLoadedMetaData = _ref2.onLoadedMetaData, | ||
onLoadStart = _ref2.onLoadStart, | ||
onPause = _ref2.onPause, | ||
onPlay = _ref2.onPlay, | ||
onPlaying = _ref2.onPlaying, | ||
onProgress = _ref2.onProgress, | ||
onRateChange = _ref2.onRateChange, | ||
onResize = _ref2.onResize, | ||
onSeeked = _ref2.onSeeked, | ||
onSeeking = _ref2.onSeeking, | ||
onStalled = _ref2.onStalled, | ||
onSuspend = _ref2.onSuspend, | ||
onTimeUpdate = _ref2.onTimeUpdate, | ||
onVolumeChange = _ref2.onVolumeChange, | ||
onWaiting = _ref2.onWaiting, | ||
onStreamAdStart = _ref2.onStreamAdStart, | ||
onStreamAdEnd = _ref2.onStreamAdEnd, | ||
onStreamAdTimeout = _ref2.onStreamAdTimeout; | ||
var internalRef = useRef(); | ||
var ref = streamRef !== null && streamRef !== void 0 ? streamRef : internalRef; | ||
useAttribute("ad-url", ref, adUrl); | ||
useAttribute("src", ref, src); | ||
useAttribute("autoplay", ref, autoplay); | ||
useAttribute("controls", ref, controls); | ||
useAttribute("loop", ref, loop); | ||
useAttribute("preload", ref, preload); | ||
useAttribute("height", ref, height); | ||
useAttribute("width", ref, width); | ||
useAttribute("poster", ref, poster); | ||
useAttribute("muted", ref, muted); // These props need to be set directly on the stream element as properties | ||
// in order for the player to respond to them when they change | ||
var _useState = useState({ | ||
videoHeight: 0, | ||
videoWidth: 0 | ||
}), | ||
videoDimensions = _useState[0], | ||
setVideoDimensions = _useState[1]; | ||
useProperty("autoplay", ref, autoplay !== null && autoplay !== void 0 ? autoplay : false); | ||
useProperty("controls", ref, controls !== null && controls !== void 0 ? controls : false); | ||
useProperty("currentTime", ref, currentTime !== null && currentTime !== void 0 ? currentTime : 0); | ||
useProperty("muted", ref, muted !== null && muted !== void 0 ? muted : false); | ||
useProperty("loop", ref, loop !== null && loop !== void 0 ? loop : false); | ||
useProperty("volume", ref, volume !== null && volume !== void 0 ? volume : 1); | ||
useProperty("preload", ref, typeof preload === "string" ? // if it's a string pass as is | ||
preload : // else if it's true, map to auto | ||
preload === true ? "auto" : // otherwise (undefined | false) maps to none | ||
"none"); // bind events | ||
var iframeRef = useRef(null); | ||
var iframeSrc = useIframeSrc(src, { | ||
muted: muted, | ||
preload: preload, | ||
loop: loop, | ||
autoplay: autoplay, | ||
controls: controls, | ||
poster: poster, | ||
adUrl: adUrl | ||
}); | ||
useProperty("muted", ref, muted); | ||
useProperty("controls", ref, controls); | ||
useProperty("src", ref, src); | ||
useProperty("autoplay", ref, autoplay); | ||
useProperty("currentTime", ref, currentTime); | ||
useProperty("loop", ref, loop); | ||
useProperty("preload", ref, preload); | ||
useProperty("volume", ref, volume); // instantiate API after properties are bound because we want undefined | ||
// values to be set before defining the properties | ||
useEffect(function () { | ||
if (iframeRef.current && window.Stream) { | ||
var api = window.Stream(iframeRef.current); | ||
ref.current = api; | ||
var videoHeight = api.videoHeight, | ||
videoWidth = api.videoWidth; | ||
if (videoHeight && videoWidth) setVideoDimensions({ | ||
videoHeight: videoHeight, | ||
videoWidth: videoWidth | ||
}); | ||
} | ||
}, []); // bind events | ||
useEvent("abort", ref, onAbort); | ||
@@ -206,15 +238,32 @@ useEvent("canplay", ref, onCanPlay); | ||
useEvent("stream-adend", ref, onStreamAdEnd); | ||
useEvent("stream-adtimeout", ref, onStreamAdTimeout); // stream element is set up from effects, load script | ||
useStreamScript(ref); | ||
return React.createElement("div", { | ||
style: { | ||
height: height, | ||
width: width | ||
}, | ||
ref: containerRef | ||
useEvent("stream-adtimeout", ref, onStreamAdTimeout); | ||
useEvent("resize", ref, function () { | ||
if (ref.current) { | ||
var _ref$current = ref.current, | ||
videoHeight = _ref$current.videoHeight, | ||
videoWidth = _ref$current.videoWidth; | ||
setVideoDimensions({ | ||
videoHeight: videoHeight, | ||
videoWidth: videoWidth | ||
}); | ||
onResize && onResize(); | ||
} | ||
}); | ||
return React.createElement(Container, { | ||
className: className, | ||
responsive: responsive, | ||
videoDimensions: videoDimensions | ||
}, React.createElement("iframe", { | ||
ref: iframeRef, | ||
src: iframeSrc, | ||
style: responsive ? responsiveIframeStyles : undefined, | ||
frameBorder: 0, | ||
height: height, | ||
width: width, | ||
allow: "accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture;", | ||
allowFullScreen: true | ||
})); | ||
}; | ||
export { Stream }; | ||
export { Stream, useStreamSDK }; | ||
//# sourceMappingURL=stream-react.esm.js.map |
@@ -1,178 +0,4 @@ | ||
import { MutableRefObject, FC } from "react"; | ||
declare global { | ||
interface Window { | ||
__stream?: { | ||
init: () => void; | ||
initElement: (streamElement: HTMLStreamElement) => void; | ||
}; | ||
} | ||
} | ||
export declare type HTMLStreamElement = HTMLVideoElement; | ||
declare type AttributeProps = { | ||
/** | ||
* Either the video id or the signed url for the video you’ve uploaded to Cloudflare Stream should be included here. | ||
*/ | ||
src: string; | ||
adUrl?: string; | ||
/** | ||
* The height of the video’s display area, in CSS pixels. | ||
*/ | ||
height?: string; | ||
/** | ||
* The width of the video’s display area, in CSS pixels. | ||
*/ | ||
width?: string; | ||
/** | ||
* A URL for an image to be shown before the video is started or while the video is downloading. If this attribute isn’t specified, a thumbnail image of the video is shown. | ||
*/ | ||
poster?: string; | ||
}; | ||
declare type PropertyProps = { | ||
/** | ||
* Tells the browser to immediately start downloading the video and play it as soon as it can. Note that mobile browsers generally do not support this attribute, the user must tap the screen to begin video playback. Please consider mobile users or users with Internet usage limits as some users don’t have unlimited Internet access before using this attribute. | ||
* | ||
* To disable video autoplay, the autoplay attribute needs to be removed altogether as this attribute. Setting autoplay="false" will not work; the video will autoplay if the attribute is there in the <stream> tag. | ||
* | ||
* In addition, some browsers now prevent videos with audio from playing automatically. You may add the mute attribute to allow your videos to autoplay. For more information, [go here](https://webkit.org/blog/6784/new-video-policies-for-ios/). | ||
*/ | ||
autoplay?: boolean; | ||
/** | ||
* Shows the default video controls such as buttons for play/pause, volume controls. You may choose to build buttons and controls that work with the player. | ||
*/ | ||
controls?: boolean; | ||
/** | ||
* Returns the current playback time in seconds. Setting this value seeks the video to a new time. | ||
*/ | ||
currentTime?: number; | ||
/** | ||
* A Boolean attribute which indicates the default setting of the audio contained in the video. If set, the audio will be initially silenced. | ||
*/ | ||
muted?: boolean; | ||
/** | ||
* A Boolean attribute; if included the player will automatically seek back to the start upon reaching the end of the video. | ||
*/ | ||
loop?: boolean; | ||
/** | ||
* This enumerated attribute is intended to provide a hint to the browser about what the author thinks will lead to the best user experience. You may choose to include this attribute as a boolean attribute without a value, or you may specify the value preload="auto" to preload the beginning of the video. Not including the attribute or using preload="metadata" will just load the metadata needed to start video playback when requested. | ||
* | ||
* The <video> element does not force the browser to follow the value of this attribute; it is a mere hint. Even though the preload="none" option is a valid HTML5 attribute, Stream player will always load some metadata to initialize the player. The amount of data loaded in this case is negligable. | ||
*/ | ||
preload?: "auto" | "metadata" | "none" | boolean; | ||
/** | ||
* Sets volume from 0.0 (silent) to 1.0 (maximum value) | ||
*/ | ||
volume?: number; | ||
}; | ||
export declare type Events = { | ||
/** | ||
* Sent when playback is aborted; for example, if the media is playing and is restarted from the beginning, this event is sent. | ||
*/ | ||
onAbort?: EventListener; | ||
/** | ||
* Sent when enough data is available that the media can be played, at least for a couple of frames. | ||
*/ | ||
onCanPlay?: EventListener; | ||
/** | ||
* Sent when the entire media can be played without interruption, assuming the download rate remains at least at the current level. It will also be fired when playback is toggled between paused and playing. Note: Manually setting the currentTime will eventually fire a canplaythrough event in firefox. Other browsers might not fire this event. | ||
*/ | ||
onCanPlayThrough?: EventListener; | ||
/** | ||
* The metadata has loaded or changed, indicating a change in duration of the media. This is sent, for example, when the media has loaded enough that the duration is known. | ||
*/ | ||
onDurationChange?: EventListener; | ||
/** | ||
* Sent when playback completes. | ||
*/ | ||
onEnded?: EventListener; | ||
/** | ||
* Sent when an error occurs. (e.g. the video has not finished encoding yet, or the video fails to load due to an incorrect signed URL) | ||
*/ | ||
onError?: EventListener; | ||
/** | ||
* The first frame of the media has finished loading. | ||
*/ | ||
onLoadedData?: EventListener; | ||
/** | ||
* The media’s metadata has finished loading; all attributes now contain as much useful information as they’re going to. | ||
*/ | ||
onLoadedMetaData?: EventListener; | ||
/** | ||
* Sent when loading of the media begins. | ||
*/ | ||
onLoadStart?: EventListener; | ||
/** | ||
* Sent when the playback state is changed to paused (paused property is true). | ||
*/ | ||
onPause?: EventListener; | ||
/** | ||
* Sent when the playback state is no longer paused, as a result of the play method, or the autoplay attribute. | ||
*/ | ||
onPlay?: EventListener; | ||
/** | ||
* Sent when the media has enough data to start playing, after the play event, but also when recovering from being stalled, when looping media restarts, and after seeked, if it was playing before seeking. | ||
*/ | ||
onPlaying?: EventListener; | ||
/** | ||
* Sent periodically to inform interested parties of progress downloading the media. Information about the current amount of the media that has been downloaded is available in the media element’s buffered attribute. | ||
*/ | ||
onProgress?: EventListener; | ||
/** | ||
* Sent when the playback speed changes. | ||
*/ | ||
onRateChange?: EventListener; | ||
/** | ||
* Sent when a seek operation completes. | ||
*/ | ||
onSeeked?: EventListener; | ||
/** | ||
* Sent when a seek operation begins. | ||
*/ | ||
onSeeking?: EventListener; | ||
/** | ||
* Sent when the user agent is trying to fetch media data, but data is unexpectedly not forthcoming. | ||
*/ | ||
onStalled?: EventListener; | ||
/** | ||
* Sent when loading of the media is suspended; this may happen either because the download has completed or because it has been paused for any other reason. | ||
*/ | ||
onSuspend?: EventListener; | ||
/** | ||
* The time indicated by the element’s currentTime attribute has changed. | ||
*/ | ||
onTimeUpdate?: EventListener; | ||
/** | ||
* Sent when the audio volume changes (both when the volume is set and when the muted attribute is changed). | ||
*/ | ||
onVolumeChange?: EventListener; | ||
/** | ||
* Sent when the requested operation (such as playback) is delayed pending the completion of another operation (such as a seek). | ||
*/ | ||
onWaiting?: EventListener; | ||
/** | ||
* Fires when ad-url attribute is present and the ad begins playback | ||
*/ | ||
onStreamAdStart?: EventListener; | ||
/** | ||
* Fires when ad-url attribute is present and the ad finishes playback | ||
*/ | ||
onStreamAdEnd?: EventListener; | ||
/** | ||
* Fires when ad-url attribute is present and the ad took too long to load. | ||
*/ | ||
onStreamAdTimeout?: EventListener; | ||
}; | ||
/** | ||
* Type helper that forces type checker to evaluate a type. | ||
*/ | ||
declare type Compute<T> = T extends Function ? T : {} & { | ||
[Key in keyof T]: T[Key]; | ||
}; | ||
export declare type StreamProps = Compute<{ | ||
/** | ||
* Ref for accessing the underlying stream element. Useful for providing imperative access to the player API: | ||
* https://developers.cloudflare.com/stream/viewing-videos/using-the-player-api | ||
*/ | ||
streamRef?: MutableRefObject<HTMLStreamElement | null>; | ||
} & AttributeProps & PropertyProps & Events>; | ||
import { FC } from "react"; | ||
import { StreamProps } from "types"; | ||
export declare const Stream: FC<StreamProps>; | ||
export {}; | ||
export declare const StreamEmbed: FC<StreamProps>; |
{ | ||
"name": "@cloudflare/stream-react", | ||
"version": "0.3.0", | ||
"version": "1.0.0-rc.1", | ||
"license": "BSD-3-Clause", | ||
@@ -5,0 +5,0 @@ "publishConfig": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
20
1331
111009
1