mirror of
https://github.com/streamwall/streamwall.git
synced 2026-01-26 07:02:48 -05:00
Initial release
This commit is contained in:
201
src/node/viewStateMachine.js
Normal file
201
src/node/viewStateMachine.js
Normal file
@@ -0,0 +1,201 @@
|
||||
import { Machine, assign } from 'xstate'
|
||||
|
||||
const viewStateMachine = Machine(
|
||||
{
|
||||
id: 'view',
|
||||
initial: 'empty',
|
||||
context: {
|
||||
view: null,
|
||||
pos: null,
|
||||
url: null,
|
||||
info: {},
|
||||
},
|
||||
on: {
|
||||
CLEAR: 'empty',
|
||||
DISPLAY: 'displaying',
|
||||
},
|
||||
states: {
|
||||
empty: {
|
||||
entry: [
|
||||
assign({
|
||||
pos: { url: null },
|
||||
info: {},
|
||||
}),
|
||||
'hideView',
|
||||
],
|
||||
invoke: {
|
||||
src: 'clearView',
|
||||
onError: {
|
||||
target: '#view.error',
|
||||
},
|
||||
},
|
||||
},
|
||||
displaying: {
|
||||
id: 'displaying',
|
||||
initial: 'loading',
|
||||
entry: assign({
|
||||
pos: (context, event) => event.pos,
|
||||
url: (context, event) => event.url,
|
||||
}),
|
||||
on: {
|
||||
DISPLAY: {
|
||||
actions: assign({
|
||||
pos: (context, event) => event.pos,
|
||||
}),
|
||||
cond: 'urlUnchanged',
|
||||
},
|
||||
},
|
||||
states: {
|
||||
loading: {
|
||||
initial: 'page',
|
||||
states: {
|
||||
page: {
|
||||
invoke: {
|
||||
src: 'loadURL',
|
||||
onDone: {
|
||||
target: 'video',
|
||||
},
|
||||
onError: {
|
||||
target: '#view.error',
|
||||
},
|
||||
},
|
||||
},
|
||||
video: {
|
||||
invoke: {
|
||||
src: 'startVideo',
|
||||
onDone: {
|
||||
target: '#view.displaying.running',
|
||||
actions: assign({
|
||||
info: (context, event) => event.data,
|
||||
}),
|
||||
},
|
||||
onError: {
|
||||
target: '#view.error',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
running: {
|
||||
initial: 'muted',
|
||||
entry: 'positionView',
|
||||
on: {
|
||||
DISPLAY: {
|
||||
actions: [
|
||||
assign({
|
||||
pos: (context, event) => event.pos,
|
||||
}),
|
||||
'positionView',
|
||||
],
|
||||
cond: 'urlUnchanged',
|
||||
},
|
||||
MUTE: '.muted',
|
||||
UNMUTE: '.listening',
|
||||
},
|
||||
states: {
|
||||
muted: {
|
||||
entry: 'muteAudio',
|
||||
},
|
||||
listening: {
|
||||
entry: 'unmuteAudio',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
error: {
|
||||
entry: 'logError',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
actions: {
|
||||
logError: (context, event) => {
|
||||
console.warn(event)
|
||||
},
|
||||
muteAudio: (context, event) => {
|
||||
context.view.webContents.audioMuted = true
|
||||
},
|
||||
unmuteAudio: (context, event) => {
|
||||
context.view.webContents.audioMuted = false
|
||||
},
|
||||
},
|
||||
guards: {
|
||||
urlUnchanged: (context, event) => {
|
||||
return context.url === event.url
|
||||
},
|
||||
},
|
||||
services: {
|
||||
clearView: async (context, event) => {
|
||||
await context.view.webContents.loadURL('about:blank')
|
||||
},
|
||||
loadURL: async (context, event) => {
|
||||
const { url, view } = context
|
||||
const wc = view.webContents
|
||||
wc.audioMuted = true
|
||||
await wc.loadURL(url)
|
||||
wc.insertCSS(
|
||||
`
|
||||
* {
|
||||
display: none !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
html, body, video {
|
||||
display: block !important;
|
||||
background: black !important;
|
||||
}
|
||||
html, body {
|
||||
overflow: hidden !important;
|
||||
background: black !important;
|
||||
}
|
||||
video {
|
||||
display: block !important;
|
||||
position: fixed !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
top: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
object-fit: cover !important;
|
||||
z-index: 999999 !important;
|
||||
}
|
||||
`,
|
||||
{ cssOrigin: 'user' },
|
||||
)
|
||||
},
|
||||
startVideo: async (context, event) => {
|
||||
const wc = context.view.webContents
|
||||
const info = await wc.executeJavaScript(`
|
||||
const sleep = ms => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
async function waitForVideo() {
|
||||
// Give the client side a little time to load. In particular, YouTube seems to need a delay.
|
||||
await sleep(1000)
|
||||
|
||||
let tries = 0
|
||||
let video
|
||||
while (!video && tries < 20) {
|
||||
video = document.querySelector('video')
|
||||
tries++
|
||||
await sleep(200)
|
||||
}
|
||||
if (!video) {
|
||||
throw new Error('could not find video')
|
||||
}
|
||||
document.body.appendChild(video)
|
||||
video.muted = false
|
||||
video.autoPlay = true
|
||||
video.play()
|
||||
setInterval(() => video.play(), 1000)
|
||||
const info = {title: document.title}
|
||||
return info
|
||||
}
|
||||
waitForVideo()
|
||||
`)
|
||||
return info
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export default viewStateMachine
|
||||
Reference in New Issue
Block a user