chromecast test version
keep-around/b214ac7012ef42593bee62c207888a2593bc5a38
Rakov Roman 3 years ago
parent 60dedbb509
commit 1bc47d33b1
  1. 22
      package-lock.json
  2. 3
      public/index.html
  3. 177
      src/features/ChromeCast/CastVideos.js
  4. 70
      src/features/ChromeCast/index.tsx
  5. 19
      src/features/ChromeCast/styled.tsx
  6. 1
      src/features/MultiSourcePlayer/index.tsx
  7. 3
      src/features/StreamPlayer/components/Controls/Components/ControlsMobile/index.tsx
  8. 3
      src/features/StreamPlayer/components/Controls/Components/ControlsWeb/index.tsx
  9. 1
      src/features/StreamPlayer/components/Controls/index.tsx

22
package-lock.json generated

@ -10859,6 +10859,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"devOptional": true,
"engines": {
"node": ">=8"
}
@ -11758,6 +11759,7 @@
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
"devOptional": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@ -17650,9 +17652,9 @@
}
},
"node_modules/hls.js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.2.0.tgz",
"integrity": "sha512-QIEQIUpBRhcpBMq3NA+/qozG8lbNfVekuX7kCMUlhiVu4382xFWsnwcuBe/CA4Gp/wB/pf2aRBaGRFlxh/FN8g=="
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.1.1.tgz",
"integrity": "sha512-2VEVzO/gr5LOD6K/DEmBkKEary6hr4YZ/SLb0PV81jOAM/Tl9DviL0sFMoUHDq05/j4OZxIj691Zo0p40u3Gtw=="
},
"node_modules/hmac-drbg": {
"version": "1.0.1",
@ -18739,6 +18741,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"devOptional": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
@ -27117,6 +27120,7 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"devOptional": true,
"dependencies": {
"picomatch": "^2.2.1"
},
@ -41678,7 +41682,8 @@
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"devOptional": true
},
"bindings": {
"version": "1.5.0",
@ -42392,6 +42397,7 @@
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
"devOptional": true,
"requires": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@ -47009,9 +47015,9 @@
}
},
"hls.js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.2.0.tgz",
"integrity": "sha512-QIEQIUpBRhcpBMq3NA+/qozG8lbNfVekuX7kCMUlhiVu4382xFWsnwcuBe/CA4Gp/wB/pf2aRBaGRFlxh/FN8g=="
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.1.1.tgz",
"integrity": "sha512-2VEVzO/gr5LOD6K/DEmBkKEary6hr4YZ/SLb0PV81jOAM/Tl9DviL0sFMoUHDq05/j4OZxIj691Zo0p40u3Gtw=="
},
"hmac-drbg": {
"version": "1.0.1",
@ -47840,6 +47846,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"devOptional": true,
"requires": {
"binary-extensions": "^2.0.0"
}
@ -54334,6 +54341,7 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"devOptional": true,
"requires": {
"picomatch": "^2.2.1"
}

@ -56,5 +56,8 @@
></script>
<% } %>
<!-- End of tv-instat Zendesk Widget script -->
<!-- Start of ChromeCast script -->
<script src="//www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
<!-- End of ChromeCast script -->
</body>
</html>

@ -0,0 +1,177 @@
/* eslint-disable no-undef */
/* eslint-disable max-classes-per-file */
/**
* Constants of states for media playback
* @enum {string}
*/
const PLAYER_STATE = {
ERROR: 'ERROR',
IDLE: 'IDLE',
LOADED: 'LOADED',
LOADING: 'LOADING',
PAUSED: 'PAUSED',
PLAYING: 'PLAYING',
STOPPED: 'STOPPED',
}
/**
* Cast player object
* Main variables:
* - PlayerHandler object for handling media playback
* - Cast player variables for controlling Cast mode media playback
* - Current media variables for transition between Cast and local modes
* @struct @constructor
*/
export class CastPlayer {
constructor(source) {
/** @type {PlayerHandler} Delegation proxy for media playback */
this.playerHandler = new PlayerHandler(this)
/** @type {PLAYER_STATE} A state for media playback */
this.playerState = PLAYER_STATE.IDLE
/* Cast player variables */
/** @type {cast.framework.RemotePlayer} */
this.remotePlayer = null
/** @type {cast.framework.RemotePlayerController} */
this.remotePlayerController = null
this.src = source
}
initializeCastPlayer() {
const options = {}
// Set the receiver application ID to your own (created in the
// Google Cast Developer Console), or optionally
// use the chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID
options.receiverApplicationId = chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID
// Auto join policy can be one of the following three:
// ORIGIN_SCOPED - Auto connect from same appId and page origin
// TAB_AND_ORIGIN_SCOPED - Auto connect from same appId, page origin, and tab
// PAGE_SCOPED - No auto connect
options.autoJoinPolicy = chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED
options.androidReceiverCompatible = false
cast.framework.CastContext.getInstance().setOptions(options)
// const credentialsData = new chrome.cast.CredentialsData('{"userId": "abc"}')
// cast.framework.CastContext.getInstance().setLaunchCredentialsData(credentialsData)
this.remotePlayer = new cast.framework.RemotePlayer()
this.remotePlayerController = new cast.framework.RemotePlayerController(this.remotePlayer)
this.remotePlayerController.addEventListener(
cast.framework.RemotePlayerEventType.IS_CONNECTED_CHANGED,
this.switchPlayer.bind(this),
)
}
/*
* PlayerHandler and setup functions
*/
switchPlayer() {
this.playerState = PLAYER_STATE.IDLE
if (cast && cast.framework && this.remotePlayer.isConnected) {
this.setupRemotePlayer()
}
}
/**
* Set the PlayerHandler target to use the remote player
*/
setupRemotePlayer() {
const castSession = cast.framework.CastContext.getInstance().getCurrentSession()
// This object will implement PlayerHandler callbacks with
// remotePlayerController, and makes necessary UI updates specific
// to remote playback
const playerTarget = {}
playerTarget.play = function () {
if (this.remotePlayer.isPaused) {
this.remotePlayerController.playOrPause()
}
}.bind(this)
playerTarget.pause = function () {
if (!this.remotePlayer.isPaused) {
this.remotePlayerController.playOrPause()
}
}.bind(this)
playerTarget.stop = function () {
this.remotePlayerController.stop()
}.bind(this)
playerTarget.load = function () {
const mediaInfo = new chrome.cast.media.MediaInfo(this.src, 'video/mp4')
mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata()
mediaInfo.metadata.metadataType = chrome.cast.media.MetadataType.GENERIC
mediaInfo.metadata.title = 'title'
mediaInfo.metadata.images = [{ url: 'https://s3.eu-central-1.amazonaws.com/img.hromadske.ua/posts/224808/opng/medium.jpg' }]
const request = new chrome.cast.media.LoadRequest(mediaInfo)
request.credentials = 'user-credentials'
request.atvCredentials = 'atv-user-credentials'
castSession.loadMedia(request).then(
this.playerHandler.loaded.bind(this.playerHandler),
(errorCode) => {
this.playerState = PLAYER_STATE.ERROR
},
)
}.bind(this)
this.playerHandler.setTarget(playerTarget)
this.playerHandler.play()
}
}
/**
* PlayerHandler
*/
class PlayerHandler {
constructor(castPlayer) {
this.target = {}
this.setTarget = function (target) {
this.target = target
}
this.play = function () {
if (castPlayer.playerState !== PLAYER_STATE.PLAYING
&& castPlayer.playerState !== PLAYER_STATE.PAUSED
&& castPlayer.playerState !== PLAYER_STATE.LOADED) {
this.load(castPlayer.src)
return
}
this.target.play()
castPlayer.playerState = PLAYER_STATE.PLAYING
}
this.pause = function () {
if (castPlayer.playerState !== PLAYER_STATE.PLAYING) {
return
}
this.target.pause()
castPlayer.playerState = PLAYER_STATE.PAUSED
}
this.stop = function () {
this.pause()
castPlayer.playerState = PLAYER_STATE.STOPPED
}
this.load = function (src) {
castPlayer.playerState = PLAYER_STATE.LOADING
this.target.load(src)
}
this.loaded = function () {
castPlayer.playerState = PLAYER_STATE.LOADED
}
}
}

@ -0,0 +1,70 @@
import {
Fragment,
memo,
useEffect,
useRef,
useState,
} from 'react'
import { readToken } from 'helpers'
import { API_ROOT } from 'config'
import { usePageParams } from 'hooks/usePageParams'
import { Container } from './styled'
import { CastPlayer } from './CastVideos'
type Props = {
src?: string,
}
export const ChromeCast = memo(({ src } : Props) => {
const [isCastAvailable, setIsCastAvailable] = useState(false)
const { profileId: matchId, sportType } = usePageParams()
const containerRef = useRef<HTMLDivElement | null>(null)
const GoogleCastLauncher = (document as any).createElement('google-cast-launcher')
GoogleCastLauncher.setAttribute('id', 'castbutton')
const baseUrl = src ?? `${API_ROOT}/video/chromecast/stream/${sportType}/${matchId}.m3u8`
const urlWithToken = (/\d.m3u8/.test(baseUrl)) ? `${baseUrl}?access_token=${readToken()}` : baseUrl
useEffect(() => {
if (
containerRef.current
&& GoogleCastLauncher
&& containerRef.current.childNodes.length < 1
) {
(containerRef.current as any).appendChild(GoogleCastLauncher)
}
}, [GoogleCastLauncher])
useEffect(() => {
const script = document.createElement('script')
script.src = '//www.gstatic.com/cv/js/sender/v1/cast_sender.js'
script.async = true
document.body.appendChild(script)
const castPlayer = new CastPlayer(urlWithToken);
(window as any).__onGCastApiAvailable = (isAvailable: boolean) => {
if (isAvailable) {
castPlayer.initializeCastPlayer()
setIsCastAvailable(true)
}
}
return () => {
document.body.removeChild(script)
setIsCastAvailable(false)
}
}, [urlWithToken])
return (
<Fragment>
{isCastAvailable && <Container ref={containerRef} />}
</Fragment>
)
})

@ -0,0 +1,19 @@
import styled, { css } from 'styled-components'
import { isMobileDevice } from 'config/userAgent'
export const Container = styled.div`
display: flex;
align-items: center;
margin-left: 25px;
height: 24px;
width: 24px;
& #castbutton:hover {
--disconnected-color: #FFFFFF;
}
${isMobileDevice ? css`
margin-left: 15px;
` : ''}
`

@ -194,6 +194,7 @@ export const MultiSourcePlayer = (props: Props) => {
rewindBackward={rewindBackward}
rewindForward={rewindForward}
selectedQuality={selectedQuality}
src={firstPlayerActive ? activeSrc : nextSrc}
togglePlaying={togglePlaying}
videoQualities={videoQualities}
volume={volume}

@ -1,5 +1,6 @@
import { T9n } from 'features/T9n'
import { Settings } from 'features/MultiSourcePlayer/components/Settings'
import { ChromeCast } from 'features/ChromeCast'
import { ControlsPropsExtended } from '../..'
import { LiveBtn } from '../../../../styled'
@ -24,6 +25,7 @@ export const ControlsMobile = (controlsProps: {props: ControlsPropsExtended}) =>
playBackTime,
progressBarElement,
selectedQuality,
src,
videoQualities,
} = props
@ -42,6 +44,7 @@ export const ControlsMobile = (controlsProps: {props: ControlsPropsExtended}) =>
<T9n t='live' />
</LiveBtn>
)}
<ChromeCast src={src} />
<Settings
onSelect={onQualitySelect}
selectedQuality={selectedQuality}

@ -8,6 +8,7 @@ import {
} from 'features/MultiSourcePlayer/styled'
import { Settings } from 'features/MultiSourcePlayer/components/Settings'
import { T9n } from 'features/T9n'
import { ChromeCast } from 'features/ChromeCast'
import { ControlsPropsExtended } from '../..'
import { VolumeBar } from '../../../VolumeBar'
@ -48,6 +49,7 @@ export const ControlsWeb = (controlsProps: { props: ControlsPropsExtended }) =>
rewindBackward,
rewindForward,
selectedQuality,
src,
togglePlaying,
videoQualities,
volumeInPercent,
@ -98,6 +100,7 @@ export const ControlsWeb = (controlsProps: { props: ControlsPropsExtended }) =>
<T9n t='live' />
</LiveBtn>
)}
<ChromeCast src={src} />
<Settings
onSelect={onQualitySelect}
selectedQuality={selectedQuality}

@ -44,6 +44,7 @@ export type ControlsProps = {
rewindBackward: () => void,
rewindForward: () => void,
selectedQuality: string,
src?: string,
togglePlaying: () => void,
videoQualities: Array<string>,
volume: number,

Loading…
Cancel
Save