Vgony commited on
Commit
e6c8ac6
1 Parent(s): 0491fb2
Files changed (3) hide show
  1. public/index.css +22 -0
  2. public/index.html +257 -252
  3. public/vv.png +0 -0
public/index.css ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ html {
2
+ font-family: 'Inter', sans-serif;
3
+ }
4
+ /* GTA Text */
5
+ h1 {
6
+ text-shadow: 0 0 0.125em rgb(192 38 211 / 0.5), 0 0 0.45em currentColor;
7
+ }
8
+ /* Vice City Text */
9
+ h2 {
10
+ font-family: 'Satisfy', cursive;
11
+ text-shadow: 0 0 0.125em rgb(192 38 211 / 0.5), 0 0 0.45em currentColor;
12
+ }
13
+
14
+ div #logo_container::before {
15
+ content: "";
16
+ width:100%;
17
+ height:100%;
18
+ top:120%;
19
+ left:0;
20
+ position:absolute;
21
+ transform: perspective(1em) rotateX(40deg) scale(1, 0.35);
22
+ }
public/index.html CHANGED
@@ -1,94 +1,105 @@
1
  <html>
2
  <head>
3
- <title>Vgony Network</title>
4
- <link href="https://cdn.jsdelivr.net/npm/daisyui@3.1.6/dist/full.css" rel="stylesheet" type="text/css" />
5
- <link href="https://cdn.jsdelivr.net/npm/daisyui@3.1.6/dist/full.css" rel="stylesheet" type="text/css" />
6
  <script src="/mpegts.js"></script>
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
  </head>
9
- <body x-data="app()" x-init="init()" class="container mx-auto">
10
- <header class="flex items-center justify-between bg-gray-800 p-4">
11
- <div class="text-2xl font-bold text-white">Vgony Network</div>
12
- <div class="flex items-center space-x-2">
13
- <button @click="switchToPreviousChannel" class="btn btn-neutral">Prev</button>
14
- <button @click="switchToNextChannel" class="btn btn-neutral">Next</button>
15
- <button @click="updateChannelSettings({ label: 'Updated Label' })" class="btn btn-primary ml-auto">Update
16
- Current Channel</button>
17
- </div>
18
- </header>
19
- <div x-show="!enabled" class="text-center">Loading WebTV...</div>
20
  <div
21
- x-show="enabled && showToolbar"
22
- class="fixed top-0 w-full font-mono text-white flex flex-col lg:flex-row items-center justify-between space-x-1 bg-black bg-opacity-60"
23
- style="text-shadow: 0px 0px 3px #000000"
24
  >
25
-
26
- <div class="flex text-xl space-x-2">
27
- <div class="text-xl">Vgony Network</div>
28
- <div class="text-md"> Channel:</div>
29
- <template x-for="chan in channels">
30
- <div
31
- class="text-xl mr-2"
32
- :class="chan.id === channel.id
33
- ? 'font-bold'
34
- : 'hover:underline opacity-60 hover:opacity-80 cursor-pointer'"
35
- x-on:click="window.location = `${window.location.origin}/?channel=${chan.id}`"
36
- x-text="chan.label">
37
- <div class="animate-ping absolute inline-flex h-4 w-4 rounded-full bg-red-400 opacity-75"></div>
38
- </div>
39
- </template>
40
- </div>
41
-
42
- <div class="flex justify-between space-x-6 items-center">
43
-
44
- <div class="flex items-center justify-center text-white opacity-100 space-x-2">
45
- <div>
46
- <svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 640 512"><path fill="currentColor" d="M96 128a128 128 0 1 1 256 0A128 128 0 1 1 96 128zM0 482.3C0 383.8 79.8 304 178.3 304h91.4C368.2 304 448 383.8 448 482.3c0 16.4-13.3 29.7-29.7 29.7H29.7C13.3 512 0 498.7 0 482.3zM609.3 512H471.4c5.4-9.4 8.6-20.3 8.6-32v-8c0-60.7-27.1-115.2-69.8-151.8c2.4-.1 4.7-.2 7.1-.2h61.4C567.8 320 640 392.2 640 481.3c0 17-13.8 30.7-30.7 30.7zM432 256c-31 0-59-12.6-79.3-32.9C372.4 196.5 384 163.6 384 128c0-26.8-6.6-52.1-18.3-74.3C384.3 40.1 407.2 32 432 32c61.9 0 112 50.1 112 112s-50.1 112-112 112z"/></svg>
47
- </div>
48
- <div x-text="channel.audience"></div>
49
- <div x-text="channel.audience > 1 ? 'viewers' : 'viewer'"></div>
50
  </div>
51
-
52
-
53
- <div
54
- x-on:click="toggleAudio()"
55
- class="flex items-center justify-center text-white opacity-80 hover:opacity-100 cursor-pointer">
56
- <div x-show="muted">
57
- <svg aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="32px" height="32px"><path fill="currentColor" d="M215.03 71.05L126.06 160H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V88.02c0-21.46-25.96-31.98-40.97-16.97zM461.64 256l45.64-45.64c6.3-6.3 6.3-16.52 0-22.82l-22.82-22.82c-6.3-6.3-16.52-6.3-22.82 0L416 210.36l-45.64-45.64c-6.3-6.3-16.52-6.3-22.82 0l-22.82 22.82c-6.3 6.3-6.3 16.52 0 22.82L370.36 256l-45.63 45.63c-6.3 6.3-6.3 16.52 0 22.82l22.82 22.82c6.3 6.3 16.52 6.3 22.82 0L416 301.64l45.64 45.64c6.3 6.3 16.52 6.3 22.82 0l22.82-22.82c6.3-6.3 6.3-16.52 0-22.82L461.64 256z" class=""></path></svg>
58
- </div>
59
- <div x-show="!muted">
60
- <svg aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 512" width="32px" height="32px"><path fill="currentColor" d="M215.03 71.05L126.06 160H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V88.02c0-21.46-25.96-31.98-40.97-16.97zM480 256c0-63.53-32.06-121.94-85.77-156.24-11.19-7.14-26.03-3.82-33.12 7.46s-3.78 26.21 7.41 33.36C408.27 165.97 432 209.11 432 256s-23.73 90.03-63.48 115.42c-11.19 7.14-14.5 22.07-7.41 33.36 6.51 10.36 21.12 15.14 33.12 7.46C447.94 377.94 480 319.53 480 256zm-141.77-76.87c-11.58-6.33-26.19-2.16-32.61 9.45-6.39 11.61-2.16 26.2 9.45 32.61C327.98 228.28 336 241.63 336 256c0 14.38-8.02 27.72-20.92 34.81-11.61 6.41-15.84 21-9.45 32.61 6.43 11.66 21.05 15.8 32.61 9.45 28.23-15.55 45.77-45 45.77-76.88s-17.54-61.32-45.78-76.86z" class=""></path></svg>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  </div>
62
  </div>
63
  <div
64
- x-on:click="fullscreen()"
65
- class="text-white hover:text-white opacity-80 hover:opacity-100 cursor-pointer">
66
- <?xml version="1.0" ?><svg version="1.1" viewBox="0 0 14 14" width="24px" height="24px" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" xmlns:xlink="http://www.w3.org/1999/xlink"><title/><desc/><defs/><g fill="none" fill-rule="evenodd" id="Page-1" stroke="none" stroke-width="1"><g fill="currentColor" id="Core" transform="translate(-215.000000, -257.000000)"><g id="fullscreen" transform="translate(215.000000, 257.000000)"><path d="M2,9 L0,9 L0,14 L5,14 L5,12 L2,12 L2,9 L2,9 Z M0,5 L2,5 L2,2 L5,2 L5,0 L0,0 L0,5 L0,5 Z M12,12 L9,12 L9,14 L14,14 L14,9 L12,9 L12,12 L12,12 Z M9,0 L9,2 L12,2 L12,5 L14,5 L14,0 L9,0 L9,0 Z" id="Shape"/></g></g></g></svg>
 
 
67
  </div>
68
  </div>
69
- </div>
70
- <div class="flex w-full pt-16">
71
- <video id="videoElement" muted autoplay class="aspect-video mx-auto w-full"></video>
72
- <!--
73
- We probably want to display a nice logo or decoration somewhere
74
- <img src="/hf-logo.png" class="absolute mt-2 w-[16%]" />
75
- -->
76
  </div>
77
  <script>
78
  // disable analytics (we don't use VideoJS yet anyway)
79
- window.HELP_IMPROVE_VIDEOJS = false
80
  </script>
81
- <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
 
 
 
82
  <script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio"></script>
83
  <script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.2/iframeResizer.contentWindow.min.js"></script>
84
  <!--<script src="https://vjs.zencdn.net/8.3.0/video.min.js"></script>-->
85
  <script>
86
-
87
- function app() {
88
- return {
89
- enabled: false,
90
- channels: {
91
- /*
92
  legacy: {
93
  id: 'legacy',
94
  label: '#older',
@@ -101,7 +112,7 @@ function app() {
101
  modelUrl: 'https://huggingface.co/cerspense/zeroscope_v2_576w',
102
  },
103
  */
104
- /*
105
  hdtv: {
106
  id: 'hdtv',
107
  label: '#old',
@@ -114,206 +125,200 @@ function app() {
114
  modelUrl: 'https://huggingface.co/cerspense/zeroscope_v2_XL',
115
  },
116
  */
117
- random: {
118
- id: 'random',
119
- label: '#random',
120
- audience: 0,
121
- online: false,
122
- visible: true,
123
- url: 'https://jbilcke-hf-media-server.hf.space/live/random.flv',
124
- resolution: '1024x576_24FPS',
125
- model: 'zeroscope_v2_XL',
126
- modelUrl: 'https://huggingface.co/cerspense/zeroscope_v2_XL',
127
- },
128
- comedy: {
129
- id: 'comedy',
130
- label: '#comedy',
131
- audience: 0,
132
- online: false,
133
- visible: true,
134
- url: 'https://jbilcke-hf-media-server.hf.space/live/comedy.flv',
135
- resolution: '1024x576_24FPS',
136
- model: 'zeroscope_v2_XL',
137
- modelUrl: 'https://huggingface.co/cerspense/zeroscope_v2_XL',
138
- },
139
- documentary: {
140
- id: 'documentary',
141
- label: '#documentary',
142
- audience: 0,
143
- online: false,
144
- visible: true,
145
- url: 'https://jbilcke-hf-media-server.hf.space/live/documentary.flv',
146
- resolution: '1024x576_24FPS',
147
- model: 'zeroscope_v2_XL',
148
- modelUrl: 'https://huggingface.co/cerspense/zeroscope_v2_XL',
149
- },
150
- },
151
- showToolbar: true,
152
- muted: true,
153
- initialized: false,
154
- activityTimeout: null,
155
- defaultChannelId: 'random',
156
- video: null,
157
- channel: {
158
- },
159
- wakeUp() {
160
- this.showToolbar = true
161
- clearTimeout(this.activityTimeout)
162
- this.activityTimeout = setTimeout(() => {
163
- this.showToolbar = false
164
- }, 1500);
165
- },
166
- toggleAudio() {
167
- if (this.video.muted) {
168
- this.video.muted = false
169
- this.muted = false
170
- } else {
171
- this.video.muted = true
172
- this.muted = true
173
- }
174
- },
175
- async checkAudience() {
176
- let audience = {}
177
- try {
178
- const res = await fetch('/stats')
179
- audience = await res.json()
180
- } catch (err) {
181
- console.log('failed to check the audience, something is wrong')
182
- }
183
 
184
- window.DEBUGME = Object.entries(this.channels)
185
- this.channels = Object.entries(this.channels).reduce((acc, [channel, data]) => ((console.log('debug:', {
186
- ...data,
187
- audience: audience[channel] || 0
188
- } ), {
189
- ...acc,
190
- [channel]: {
191
- ...data,
192
- audience: audience[channel] || 0
193
- }
194
- })), {})
195
- this.channel = this.channels[this.channel.id]
196
- },
197
- fullscreen() {
198
- if (this.video.requestFullscreen) {
199
- this.video.requestFullscreen();
200
- } else if (this.video.mozRequestFullScreen) {
201
- this.video.mozRequestFullScreen();
202
- } else if (this.video.webkitRequestFullscreen) {
203
- this.video.webkitRequestFullscreen();
204
- } else if (this.video.msRequestFullscreen) {
205
- this.video.msRequestFullscreen();
206
- }
207
- },
208
- init() {
209
- if (this.initialized) {
210
- console.log("already initialized")
211
- return
212
- }
213
- this.initialized = true
214
- console.log('initializing WebTV..')
 
 
 
 
 
 
215
 
216
- const urlParams = new URLSearchParams(window.location.search)
217
 
218
- const requestedChannelId = `${urlParams.get('channel') || 'random'}`
 
 
219
 
220
- this.enabled = true
221
- // this.enabled = `${urlParams.get('beta') || 'false'}` === 'true'
222
 
223
- if (!this.enabled) {
224
- return
225
- }
226
 
227
- this.video = document.getElementById('videoElement')
228
 
229
- const defaultChannel = this.channels[this.defaultChannelId]
230
-
231
- this.channel = this.channels[requestedChannelId] || defaultChannel
232
 
233
- console.log(`Selected channel: ${this.channel.label}`)
234
- console.log(`Stream URL: ${this.channel.url}`)
235
 
236
-
237
- const handleActivity = () => {
238
- this.wakeUp()
239
- }
240
- handleActivity()
241
 
242
- document.addEventListener("touchstart", handleActivity)
243
- document.addEventListener("touchmove", handleActivity)
244
- document.addEventListener("click", handleActivity)
245
- document.addEventListener("mousemove", handleActivity)
246
 
247
- this.checkAudience()
248
- setInterval(() => {
249
- this.checkAudience()
250
- }, 1000)
251
 
252
- // detect mute/unmute events
253
- this.video.addEventListener("mute", () => {
254
- this.muted = true
255
- })
256
- this.video.addEventListener("unmute", () => {
257
- this.muted = false
258
- })
259
-
260
- // when we move outside the video, we always hide the toolbar
261
- document.addEventListener("mouseleave", () => {
262
- clearTimeout(this.activityTimeout)
263
- this.showToolbar = false
264
- })
265
 
266
- // as a bonus, we also allow fullscreen on double click
267
- this.video.addEventListener('dblclick', () => {
268
- this.fullscreen()
269
- })
270
 
271
- // some devices such as the iPhone don't support MSE Live Playback
272
- if (mpegts.getFeatureList().mseLivePlayback) {
273
- var player = mpegts.createPlayer({
274
- type: 'flv', // could also be mpegts, m2ts, flv
275
- isLive: true,
276
- url: this.channel.url,
277
- })
278
- player.attachMediaElement(this.video)
279
 
280
- player.on(mpegts.Events.ERROR, function (err) {
281
- console.log('got an error:', err)
282
- if (err.type === mpegts.ErrorTypes.NETWORK_ERROR) {
283
- console.log('Network error')
284
- }
285
- });
286
 
287
- player.load()
288
 
289
- // due to an issue with our stream when the FFMPEG playlist ends,
290
- // the stream gets interrupted for ~1sec, which causes the frontend to hangs up
291
- // the following code tries to restart the page when that happens, but in the long term
292
- // we should fix the issue on the server side (fix our FFMPEG bash script)
293
- this.video.addEventListener('ended', function() {
294
- console.log('Stream ended, trying to reload...')
295
- setTimeout(() => {
296
- console.log('Reloading the page..')
297
- // Unloading and loading the source again isn't enough it seems
298
- // player.unload()
299
- // player.load()
300
- window.location.reload()
301
- }, 1200)
302
- }, false)
 
 
 
 
303
 
304
- // Handle autoplay restrictions.
305
- let promise = this.video.play()
306
- if (promise !== undefined) {
307
- this.video.addEventListener('click', function() {
308
- this.video.play()
309
- })
310
- }
311
 
312
- player.play()
 
 
 
313
  }
314
- }
315
- }
316
- }
317
  </script>
318
  </body>
319
- </html>
 
1
  <html>
2
  <head>
3
+ <title>V-Pod</title>
4
+ <link rel="stylesheet" href="index.css">
 
5
  <script src="/mpegts.js"></script>
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
  </head>
8
+ <body>
 
 
 
 
 
 
 
 
 
 
9
  <div
10
+ class="h-screen w-auto grid place-content-center place-items-center overflow-hidden bg-gradient-to-b from-slate-900 to-black"
 
 
11
  >
12
+ <div x-data="app()" x-init="init()" class="container mx-auto">
13
+ <div class="flex w-full pt-16">
14
+ <video
15
+ id="videoElement"
16
+ class="aspect-video mx-auto w-auto border-4 border-slate-900/10 rounded-full"
17
+ preload="auto"
18
+ muted
19
+ autoplay
20
+ ></video>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  </div>
22
+ <div class="inset-x-0 bottom-0 pb-4 z-10">
23
+ <div class="container mx-auto px-2 opacity-85">
24
+ <div class="flex items-center justify-between">
25
+ <div
26
+ class="flex items-center space-x-4 text-xs focus:cursor-pointer"
27
+ >
28
+ <template x-for="(chan, index) in channels">
29
+ <div
30
+ class="text-sm capitalize truncate mr-2"
31
+ :class="chan.id === channel.id ? 'font-semibold cursor-pointer text-white' : 'text-slate-100 cursor-pointer hover:underline'"
32
+ @click="window.location = `${window.location.origin}/?channel=${chan.id}`"
33
+ x-text="chan.label"
34
+ >
35
+ <span
36
+ class="animate-ping absolute inline-flex h-3 w-3 rounded-full bg-blue-500 opacity-75"
37
+ ></span>
38
+ </div>
39
+ </template>
40
+ </div>
41
+ <div class="flex-col justify-center space-y-4 items-center">
42
+ <div class="flex items-center justify-center space-x-1 text-lg">
43
+ <span>🔴</span>
44
+ <a class="text-white font-bold" x-text="'LIVE'"></a>
45
+ </div>
46
+ <div
47
+ class="flex items-center justify-center text-white opacity-80 hover:opacity-100 cursor-pointer"
48
+ x-on:click="toggleAudio()"
49
+ >
50
+ <span x-show="muted">&#x1F507;</span>
51
+ <span x-show="!muted">&#x1F508;</span>
52
+ </div>
53
+ <div
54
+ class="flex items-center justify-center text-white hover:text-white opacity-80 hover:opacity-100 cursor-pointer"
55
+ >
56
+ <a href="https://vgony.tech">
57
+ <svg
58
+ xmlns="http://www.w3.org/2000/svg"
59
+ class="h-6 w-6"
60
+ fill="none"
61
+ viewBox="0 0 24 24"
62
+ stroke="currentColor"
63
+ >
64
+ <path
65
+ stroke-linecap="round"
66
+ stroke-linejoin="round"
67
+ stroke-width="2"
68
+ d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
69
+ />
70
+ </svg>
71
+ </a>
72
+ </div>
73
+ </div>
74
+ </div>
75
  </div>
76
  </div>
77
  <div
78
+ id="logo_container"
79
+ class="relative grid place-content-center place-items-center gap-2 before:bg-gradient-to-t before:from-teal-500/70 before:via-fuchsia-600 before:to-transparent before:blur-xl before:filter">
80
+ >
81
+ <h1 class="title text-6xl font-black text-teal-300">VGФЙЧ</h1>
82
+ <h2 class="cursive text-6xl font-thin text-fuchsia-500">ҒФЯԐVԐЯ</h2>
83
  </div>
84
  </div>
 
 
 
 
 
 
 
85
  </div>
86
  <script>
87
  // disable analytics (we don't use VideoJS yet anyway)
88
+ window.HELP_IMPROVE_VIDEOJS = false;
89
  </script>
90
+ <script
91
+ defer
92
+ src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"
93
+ ></script>
94
  <script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio"></script>
95
  <script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.2/iframeResizer.contentWindow.min.js"></script>
96
  <!--<script src="https://vjs.zencdn.net/8.3.0/video.min.js"></script>-->
97
  <script>
98
+ function app() {
99
+ return {
100
+ enabled: false,
101
+ channels: {
102
+ /*
 
103
  legacy: {
104
  id: 'legacy',
105
  label: '#older',
 
112
  modelUrl: 'https://huggingface.co/cerspense/zeroscope_v2_576w',
113
  },
114
  */
115
+ /*
116
  hdtv: {
117
  id: 'hdtv',
118
  label: '#old',
 
125
  modelUrl: 'https://huggingface.co/cerspense/zeroscope_v2_XL',
126
  },
127
  */
128
+ random: {
129
+ id: "random",
130
+ label: "#random",
131
+ audience: 0,
132
+ online: false,
133
+ visible: true,
134
+ url: "https://jbilcke-hf-media-server.hf.space/live/random.flv",
135
+ resolution: "1024x576_24FPS",
136
+ model: "zeroscope_v2_XL",
137
+ modelUrl: "https://huggingface.co/cerspense/zeroscope_v2_XL",
138
+ },
139
+ comedy: {
140
+ id: "comedy",
141
+ label: "#comedy",
142
+ audience: 0,
143
+ online: false,
144
+ visible: true,
145
+ url: "https://jbilcke-hf-media-server.hf.space/live/comedy.flv",
146
+ resolution: "1024x576_24FPS",
147
+ model: "zeroscope_v2_XL",
148
+ modelUrl: "https://huggingface.co/cerspense/zeroscope_v2_XL",
149
+ },
150
+ documentary: {
151
+ id: "documentary",
152
+ label: "#documentary",
153
+ audience: 0,
154
+ online: false,
155
+ visible: true,
156
+ url: "https://jbilcke-hf-media-server.hf.space/live/documentary.flv",
157
+ resolution: "1024x576_24FPS",
158
+ model: "zeroscope_v2_XL",
159
+ modelUrl: "https://huggingface.co/cerspense/zeroscope_v2_XL",
160
+ },
161
+ },
162
+ muted: true,
163
+ initialized: false,
164
+ activityTimeout: null,
165
+ defaultChannelId: "random",
166
+ video: null,
167
+ channel: {},
168
+ wakeUp() {
169
+ this.showToolbar = true;
170
+ },
171
+ toggleAudio() {
172
+ if (this.video.muted) {
173
+ this.video.muted = false;
174
+ this.muted = false;
175
+ } else {
176
+ this.video.muted = true;
177
+ this.muted = true;
178
+ }
179
+ },
180
+ async checkAudience() {
181
+ let audience = {};
182
+ try {
183
+ const res = await fetch("/stats");
184
+ audience = await res.json();
185
+ } catch (err) {
186
+ console.log("failed to check the audience, something is wrong");
187
+ }
 
 
 
 
 
 
188
 
189
+ window.DEBUGME = Object.entries(this.channels);
190
+ this.channels = Object.entries(this.channels).reduce(
191
+ (acc, [channel, data]) => (
192
+ console.log("debug:", {
193
+ ...data,
194
+ audience: audience[channel] || 0,
195
+ }),
196
+ {
197
+ ...acc,
198
+ [channel]: {
199
+ ...data,
200
+ audience: audience[channel] || 0,
201
+ },
202
+ }
203
+ ),
204
+ {}
205
+ );
206
+ this.channel = this.channels[this.channel.id];
207
+ },
208
+ fullscreen() {
209
+ if (this.video.requestFullscreen) {
210
+ this.video.requestFullscreen();
211
+ } else if (this.video.mozRequestFullScreen) {
212
+ this.video.mozRequestFullScreen();
213
+ } else if (this.video.webkitRequestFullscreen) {
214
+ this.video.webkitRequestFullscreen();
215
+ } else if (this.video.msRequestFullscreen) {
216
+ this.video.msRequestFullscreen();
217
+ }
218
+ },
219
+ init() {
220
+ if (this.initialized) {
221
+ console.log("already initialized");
222
+ return;
223
+ }
224
+ this.initialized = true;
225
+ console.log("initializing..");
226
 
227
+ const urlParams = new URLSearchParams(window.location.search);
228
 
229
+ const requestedChannelId = `${
230
+ urlParams.get("channel") || "random"
231
+ }`;
232
 
233
+ this.enabled = true;
234
+ // this.enabled = `${urlParams.get('beta') || 'false'}` === 'true'
235
 
236
+ if (!this.enabled) {
237
+ return;
238
+ }
239
 
240
+ this.video = document.getElementById("videoElement");
241
 
242
+ const defaultChannel = this.channels[this.defaultChannelId];
 
 
243
 
244
+ this.channel = this.channels[requestedChannelId] || defaultChannel;
 
245
 
246
+ console.log(`Selected channel: ${this.channel.label}`);
247
+ console.log(`Stream URL: ${this.channel.url}`);
 
 
 
248
 
249
+ const handleActivity = () => {
250
+ this.wakeUp();
251
+ };
252
+ handleActivity();
253
 
254
+ this.checkAudience();
255
+ setInterval(() => {
256
+ this.checkAudience();
257
+ }, 1000);
258
 
259
+ // detect mute/unmute events
260
+ this.video.addEventListener("mute", () => {
261
+ this.muted = true;
262
+ });
263
+ this.video.addEventListener("unmute", () => {
264
+ this.muted = false;
265
+ });
 
 
 
 
 
 
266
 
267
+ // as a bonus, we also allow fullscreen on double click
268
+ this.video.addEventListener("dblclick", () => {
269
+ this.fullscreen();
270
+ });
271
 
272
+ // some devices such as the iPhone don't support MSE Live Playback
273
+ if (mpegts.getFeatureList().mseLivePlayback) {
274
+ var player = mpegts.createPlayer({
275
+ type: "flv", // could also be mpegts, m2ts, flv
276
+ isLive: true,
277
+ url: this.channel.url,
278
+ });
279
+ player.attachMediaElement(this.video);
280
 
281
+ player.on(mpegts.Events.ERROR, function (err) {
282
+ console.log("got an error:", err);
283
+ if (err.type === mpegts.ErrorTypes.NETWORK_ERROR) {
284
+ console.log("Network error");
285
+ }
286
+ });
287
 
288
+ player.load();
289
 
290
+ // due to an issue with our stream when the FFMPEG playlist ends,
291
+ // the stream gets interrupted for ~1sec, which causes the frontend to hangs up
292
+ // the following code tries to restart the page when that happens, but in the long term
293
+ // we should fix the issue on the server side (fix our FFMPEG bash script)
294
+ this.video.addEventListener(
295
+ "ended",
296
+ function () {
297
+ console.log("Stream ended, trying to reload...");
298
+ setTimeout(() => {
299
+ console.log("Reloading the page..");
300
+ // Unloading and loading the source again isn't enough it seems
301
+ // player.unload()
302
+ // player.load()
303
+ window.location.reload();
304
+ }, 1200);
305
+ },
306
+ false
307
+ );
308
 
309
+ // Handle autoplay restrictions.
310
+ let promise = this.video.play();
311
+ if (promise !== undefined) {
312
+ this.video.addEventListener("click", function () {
313
+ this.video.play();
314
+ });
315
+ }
316
 
317
+ player.play();
318
+ }
319
+ },
320
+ };
321
  }
 
 
 
322
  </script>
323
  </body>
324
+ </html>
public/vv.png ADDED