Evict apps/video → κ-store (M3 E0 pilot): 64 blake3-addressed objects
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +25 -0
- b/022a97533ed03d64fab9788eedfd3051f5d4ae1e12a61e3347b5df8cdcb5f97f +3 -0
- b/0320a1879f663fad96719cede89e482a44e515a17c8504f8de0cb21071e716e2 +3 -0
- b/0ab773261c3d252351f1be8aea9e182111853ef6a5e9b45c8ac91f2e91a6bac8 +3 -0
- b/12823c8a1a3c9e0084c34565bb5cd88938e4311c1f57f03236a2f9b6da24de05 +3 -0
- b/15bb754526268bf9e2149a23c6e39e99fc811a31a2119f68569ac1f14c5bb954 +1 -0
- b/172b2e7bdc5d7b3c9be55b46a36744187755f1671e0382e75d3ba5da8baa1e45 +0 -0
- b/1a9a0e5d2a4b2deefd9c02e2c03534c665aa3268751faa0b0d0f65d5db46ba97 +3 -0
- b/1cbf6a00a9ab9358f4ccb53cc434ec3d8c180abbd4a94e89c7447770ee007fbf +23 -0
- b/1e75d59c8b442c3b550d09de7425df69f7d73775812de981af07a4efe35e0def +40 -0
- b/1ff06debd7f34106c40c7920612f382759d6080adac51e98f2c0e1c3a80e85b3 +785 -0
- b/26cd85a5de850229aae046e87490e4a32c7bfae6c01fa83ca0a6593241f5e54f +0 -0
- b/29fcd622a2dfc0cccfece8a717e5f8f09061dd0497bd0921717ffd5c104cf71d +3 -0
- b/2e485cef50d0c92a82dc47e998dc692fb064bb04080a3ce42f9436a85866c7c5 +3 -0
- b/3237bdea9058724a3dedc441e487a3c477489a1b71df668ff6d9ad9e838a3f1d +0 -0
- b/3444f1c9211901b5676d5a44777fab07ff67fed01deda3c5aaa3c1717442af39 +83 -0
- b/374cc5daa86b2dddbceb83552d561d4856eb60ef631b30916dc92b57275f9eca +8 -0
- b/375ca224bf188af103815a995dd7ce74e7ba74a5c67f473a03600797db9af450 +3 -0
- b/3a0c90e6fe3baa82e5bf20681a902182c69cbb56d850fce5fa86eb86e863afe8 +3 -0
- b/3e8be10158d7e4a20f03d1bc7b59a04f97aab20ccbc37b3b599abec0d3681b9f +3 -0
- b/3fd9ff18cd3872dc3f5596a9aa9a02b094ce2b357d2de893ad70dfea23006f24 +5 -0
- b/477220d636a4e6c65d61affa15c10a81de2dcf6df73b8aafb719db547e9074fc +8 -0
- b/4c9124649152e87d2b41d053579997189860bfffccc8adfc14e86aaccb2471af +3 -0
- b/5ca266f01ea019c2f4d8a4f146f91a262b05625807304653f92dbb97f261d95a +0 -0
- b/5d755801f6ad62cf150df6c035e45fb89144bbb163c9a58687a78425f8f609d5 +0 -0
- b/61f5de63f35414694a789bcf05c03382b15ca209e36334ff4e83b4c466c306c0 +6 -0
- b/61fd98fa00653be79b0948475914d2522be9f83eafe4858180b2bd2e2b5668c4 +9 -0
- b/6224c8e805b0f7e2d478edcbd216c01af74150bde7e433f05c8016a6b4dd03f9 +3 -0
- b/6902f4b785750559a8993a4e55fcdf9c80a44c0d3049b10c1960450d37759a7f +5 -0
- b/792fd7431ff360863e34a67d4190902e01022792eefe63ac2a35c965f5d97af0 +15 -0
- b/7930427c195c0a738a3d19652b9eeab2012637f8f5e678a41163a89c3b1b78b8 +3 -0
- b/7aa800d6da2f38a86d41f9de4eab32d9418eb453d1ce59d56d8a8c151ec54d23 +3 -0
- b/7e6baad8d384f52a584e2432e8a12a78c92afbaa9be9ae9731d6a076a4a61ffc +47 -0
- b/84acaa85618686868e6b28091c81674894a194d27123b91ab0d4e86f2fd1b973 +0 -0
- b/85ac280da66d2cba7a4ad1487e0b391b40a702b45e63bfae884859d0c50d390f +0 -0
- b/8bda1d7bee91aac65e1a1db701ea902fba0116e30175c88e245740a9e9a7a33a +3 -0
- b/9149515d6ce8bc6ef4d01509ae02d4cc072c0e04a98e9b411a1a807e5286f2f6 +5 -0
- b/931fc03dc173d2b04f5f3e450414ff58e8d15064ca5e4abc0ed5f092fd6b3688 +22 -0
- b/967267c207182bbddd00f3783746889ba53739c566e4c9bfeb76bf52bf9a611e +24 -0
- b/9a9c309bfdb2c4792eb598870d3eba8cd22d6704d6240a0993fab28c34aa0e79 +3 -0
- b/9ca150f76026a6841d2043a38f1a9ef512a53f6c7651f56723364011e322af08 +3 -0
- b/a29511197cf5065147b26ac3796fe45da8c057f4c115ab977d3f35920ecb54df +25 -0
- b/a4545a22f6cab447274c9e14c5119f3b238b88c831a811af3ac3f06bf0519d59 +9 -0
- b/b2df281d2eed192aeca5c87c9f2347edf5f9297341ae1f742a21d488f97c3dee +0 -0
- b/b69a617ad775cda67c2e7f9a9189218c079c6e621b7b3f9e3b5d07f2d3dc8cb1 +6 -0
- b/b826e5ac4bfe8bfe3c92c2d6e64c45a1bf1aff9fa829342a2e60e1f942186f26 +3 -0
- b/b85f748153b9ca262b04d5839efe26babd283515e59d2f34e621dba5ab2d571d +3 -0
- b/b932a39c7f64f0ebbe544307060dd2a9836a4b1999b01903f38898d00fdee690 +3 -0
- b/bb421b0cf44a37b1dfbea14fa58cabf69939a21483be3a828c4733e17c5bebb5 +3 -0
- b/bea568400b40279f4470f3ec72a96b952772946e09a878d26867fe2ba5133019 +0 -0
.gitattributes
CHANGED
|
@@ -37,3 +37,28 @@ b/f6caa9eeead2c99deeb2cdb70b7e25bbaa2cf5c60555f5b4030ecf6b6d9efa51 filter=lfs di
|
|
| 37 |
b/00cfa4960b319ba1ba7291cddb20dea130a407ce8b42426218854f4f68f4dcd7 filter=lfs diff=lfs merge=lfs -text
|
| 38 |
b/3370b22070b56218a57df6406e2e4ba7cc171e942d4d1f2ae4297ea2c4c08d9c filter=lfs diff=lfs merge=lfs -text
|
| 39 |
b/9d90b4ea8bccee8c239073c7ec7dd5158c6d11f1f668487570d17475d79e661e filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
b/00cfa4960b319ba1ba7291cddb20dea130a407ce8b42426218854f4f68f4dcd7 filter=lfs diff=lfs merge=lfs -text
|
| 38 |
b/3370b22070b56218a57df6406e2e4ba7cc171e942d4d1f2ae4297ea2c4c08d9c filter=lfs diff=lfs merge=lfs -text
|
| 39 |
b/9d90b4ea8bccee8c239073c7ec7dd5158c6d11f1f668487570d17475d79e661e filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
b/022a97533ed03d64fab9788eedfd3051f5d4ae1e12a61e3347b5df8cdcb5f97f filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
b/0320a1879f663fad96719cede89e482a44e515a17c8504f8de0cb21071e716e2 filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
b/0ab773261c3d252351f1be8aea9e182111853ef6a5e9b45c8ac91f2e91a6bac8 filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
b/12823c8a1a3c9e0084c34565bb5cd88938e4311c1f57f03236a2f9b6da24de05 filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
b/1a9a0e5d2a4b2deefd9c02e2c03534c665aa3268751faa0b0d0f65d5db46ba97 filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
b/29fcd622a2dfc0cccfece8a717e5f8f09061dd0497bd0921717ffd5c104cf71d filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
b/2e485cef50d0c92a82dc47e998dc692fb064bb04080a3ce42f9436a85866c7c5 filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
b/375ca224bf188af103815a995dd7ce74e7ba74a5c67f473a03600797db9af450 filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
b/3a0c90e6fe3baa82e5bf20681a902182c69cbb56d850fce5fa86eb86e863afe8 filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
b/3e8be10158d7e4a20f03d1bc7b59a04f97aab20ccbc37b3b599abec0d3681b9f filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
b/4c9124649152e87d2b41d053579997189860bfffccc8adfc14e86aaccb2471af filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
b/6224c8e805b0f7e2d478edcbd216c01af74150bde7e433f05c8016a6b4dd03f9 filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
b/7930427c195c0a738a3d19652b9eeab2012637f8f5e678a41163a89c3b1b78b8 filter=lfs diff=lfs merge=lfs -text
|
| 53 |
+
b/7aa800d6da2f38a86d41f9de4eab32d9418eb453d1ce59d56d8a8c151ec54d23 filter=lfs diff=lfs merge=lfs -text
|
| 54 |
+
b/8bda1d7bee91aac65e1a1db701ea902fba0116e30175c88e245740a9e9a7a33a filter=lfs diff=lfs merge=lfs -text
|
| 55 |
+
b/9ca150f76026a6841d2043a38f1a9ef512a53f6c7651f56723364011e322af08 filter=lfs diff=lfs merge=lfs -text
|
| 56 |
+
b/b826e5ac4bfe8bfe3c92c2d6e64c45a1bf1aff9fa829342a2e60e1f942186f26 filter=lfs diff=lfs merge=lfs -text
|
| 57 |
+
b/b85f748153b9ca262b04d5839efe26babd283515e59d2f34e621dba5ab2d571d filter=lfs diff=lfs merge=lfs -text
|
| 58 |
+
b/b932a39c7f64f0ebbe544307060dd2a9836a4b1999b01903f38898d00fdee690 filter=lfs diff=lfs merge=lfs -text
|
| 59 |
+
b/bb421b0cf44a37b1dfbea14fa58cabf69939a21483be3a828c4733e17c5bebb5 filter=lfs diff=lfs merge=lfs -text
|
| 60 |
+
b/c071203c3e57ad416021f57783f3994b0269bb26e8ec94734460dbcb6611152c filter=lfs diff=lfs merge=lfs -text
|
| 61 |
+
b/c4b894de1e276ccb732d9e17ab407022a1fcfb3d415ce7ac5e48c0c22464ef4d filter=lfs diff=lfs merge=lfs -text
|
| 62 |
+
b/cd4d521be3e2bdbc82e815d8411b0e92c31f935584d4bb5f4b9ec911434f90de filter=lfs diff=lfs merge=lfs -text
|
| 63 |
+
b/d63c7e2117883cf9ec8551a013d48695c4fc8183c81c1c704a1920060fcfe570 filter=lfs diff=lfs merge=lfs -text
|
| 64 |
+
b/ed23baff6c0015f9a92afba85a8e1043afe1ca57733c08ad8ff79273270ef861 filter=lfs diff=lfs merge=lfs -text
|
b/022a97533ed03d64fab9788eedfd3051f5d4ae1e12a61e3347b5df8cdcb5f97f
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2b4bd66bcb19588faf959e7d86b55b25a6d7ce3a7a886f276a16307a22aacc26
|
| 3 |
+
size 113768
|
b/0320a1879f663fad96719cede89e482a44e515a17c8504f8de0cb21071e716e2
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:183cd57f9676937fba04fcad21f86f94c6ad7eaf589035d27d09b268af27a4ca
|
| 3 |
+
size 111316
|
b/0ab773261c3d252351f1be8aea9e182111853ef6a5e9b45c8ac91f2e91a6bac8
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ce330c543c8e85a5b973bdffb3a67c05baf4960aa95a2903f0e7d0f9203fd11f
|
| 3 |
+
size 126017
|
b/12823c8a1a3c9e0084c34565bb5cd88938e4311c1f57f03236a2f9b6da24de05
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:11080ce6e915418409df45ead1d9fb76aa3ad17941242ee0ef85db8470fa2b90
|
| 3 |
+
size 267178
|
b/15bb754526268bf9e2149a23c6e39e99fc811a31a2119f68569ac1f14c5bb954
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
.vjs-svg-icon{display:inline-block;background-repeat:no-repeat;background-position:center;fill:currentColor;height:1.8em;width:1.8em}.vjs-svg-icon:before{content:none!important}.vjs-control:focus .vjs-svg-icon,.vjs-svg-icon:hover{filter:drop-shadow(0 0 .25em #fff)}.video-js .vjs-big-play-button .vjs-icon-placeholder:before,.video-js .vjs-modal-dialog,.vjs-button>.vjs-icon-placeholder:before,.vjs-modal-dialog .vjs-modal-dialog-content{position:absolute;top:0;left:0;width:100%;height:100%}.video-js .vjs-big-play-button .vjs-icon-placeholder:before,.vjs-button>.vjs-icon-placeholder:before{text-align:center}@font-face{font-family:VideoJS;src:url(data:application/font-woff;charset=utf-8;base64,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) format("woff");font-weight:400;font-style:normal}.video-js .vjs-big-play-button .vjs-icon-placeholder:before,.video-js .vjs-play-control .vjs-icon-placeholder,.vjs-icon-play{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-big-play-button .vjs-icon-placeholder:before,.video-js .vjs-play-control .vjs-icon-placeholder:before,.vjs-icon-play:before{content:"\f101"}.vjs-icon-play-circle{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-play-circle:before{content:"\f102"}.video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder,.vjs-icon-pause{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder:before,.vjs-icon-pause:before{content:"\f103"}.video-js .vjs-mute-control.vjs-vol-0 .vjs-icon-placeholder,.vjs-icon-volume-mute{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control.vjs-vol-0 .vjs-icon-placeholder:before,.vjs-icon-volume-mute:before{content:"\f104"}.video-js .vjs-mute-control.vjs-vol-1 .vjs-icon-placeholder,.vjs-icon-volume-low{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control.vjs-vol-1 .vjs-icon-placeholder:before,.vjs-icon-volume-low:before{content:"\f105"}.video-js .vjs-mute-control.vjs-vol-2 .vjs-icon-placeholder,.vjs-icon-volume-mid{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control.vjs-vol-2 .vjs-icon-placeholder:before,.vjs-icon-volume-mid:before{content:"\f106"}.video-js .vjs-mute-control .vjs-icon-placeholder,.vjs-icon-volume-high{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control .vjs-icon-placeholder:before,.vjs-icon-volume-high:before{content:"\f107"}.video-js .vjs-fullscreen-control .vjs-icon-placeholder,.vjs-icon-fullscreen-enter{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-fullscreen-control .vjs-icon-placeholder:before,.vjs-icon-fullscreen-enter:before{content:"\f108"}.video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder,.vjs-icon-fullscreen-exit{font-family:VideoJS;font-weight:400;font-style:normal}.video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder:before,.vjs-icon-fullscreen-exit:before{content:"\f109"}.vjs-icon-spinner{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-spinner:before{content:"\f10a"}.video-js .vjs-subs-caps-button .vjs-icon-placeholder,.video-js .vjs-subtitles-button .vjs-icon-placeholder,.video-js.video-js:lang(en-AU) .vjs-subs-caps-button .vjs-icon-placeholder,.video-js.video-js:lang(en-GB) .vjs-subs-caps-button .vjs-icon-placeholder,.video-js.video-js:lang(en-IE) .vjs-subs-caps-button .vjs-icon-placeholder,.video-js.video-js:lang(en-NZ) .vjs-subs-caps-button .vjs-icon-placeholder,.vjs-icon-subtitles{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js .vjs-subtitles-button .vjs-icon-placeholder:before,.video-js.video-js:lang(en-AU) .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js.video-js:lang(en-GB) .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js.video-js:lang(en-IE) .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js.video-js:lang(en-NZ) .vjs-subs-caps-button .vjs-icon-placeholder:before,.vjs-icon-subtitles:before{content:"\f10b"}.video-js .vjs-captions-button .vjs-icon-placeholder,.video-js:lang(en) .vjs-subs-caps-button .vjs-icon-placeholder,.video-js:lang(fr-CA) .vjs-subs-caps-button .vjs-icon-placeholder,.vjs-icon-captions{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-captions-button .vjs-icon-placeholder:before,.video-js:lang(en) .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js:lang(fr-CA) .vjs-subs-caps-button .vjs-icon-placeholder:before,.vjs-icon-captions:before{content:"\f10c"}.vjs-icon-hd{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-hd:before{content:"\f10d"}.video-js .vjs-chapters-button .vjs-icon-placeholder,.vjs-icon-chapters{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-chapters-button .vjs-icon-placeholder:before,.vjs-icon-chapters:before{content:"\f10e"}.vjs-icon-downloading{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-downloading:before{content:"\f10f"}.vjs-icon-file-download{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-file-download:before{content:"\f110"}.vjs-icon-file-download-done{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-file-download-done:before{content:"\f111"}.vjs-icon-file-download-off{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-file-download-off:before{content:"\f112"}.vjs-icon-share{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-share:before{content:"\f113"}.vjs-icon-cog{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-cog:before{content:"\f114"}.vjs-icon-square{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-square:before{content:"\f115"}.video-js .vjs-play-progress,.video-js .vjs-volume-level,.vjs-icon-circle,.vjs-seek-to-live-control .vjs-icon-placeholder{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-play-progress:before,.video-js .vjs-volume-level:before,.vjs-icon-circle:before,.vjs-seek-to-live-control .vjs-icon-placeholder:before{content:"\f116"}.vjs-icon-circle-outline{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-circle-outline:before{content:"\f117"}.vjs-icon-circle-inner-circle{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-circle-inner-circle:before{content:"\f118"}.video-js .vjs-control.vjs-close-button .vjs-icon-placeholder,.vjs-icon-cancel{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-control.vjs-close-button .vjs-icon-placeholder:before,.vjs-icon-cancel:before{content:"\f119"}.vjs-icon-repeat{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-repeat:before{content:"\f11a"}.video-js .vjs-play-control.vjs-ended .vjs-icon-placeholder,.vjs-icon-replay{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-play-control.vjs-ended .vjs-icon-placeholder:before,.vjs-icon-replay:before{content:"\f11b"}.video-js .vjs-skip-backward-5 .vjs-icon-placeholder,.vjs-icon-replay-5{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-skip-backward-5 .vjs-icon-placeholder:before,.vjs-icon-replay-5:before{content:"\f11c"}.video-js .vjs-skip-backward-10 .vjs-icon-placeholder,.vjs-icon-replay-10{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-skip-backward-10 .vjs-icon-placeholder:before,.vjs-icon-replay-10:before{content:"\f11d"}.video-js .vjs-skip-backward-30 .vjs-icon-placeholder,.vjs-icon-replay-30{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-skip-backward-30 .vjs-icon-placeholder:before,.vjs-icon-replay-30:before{content:"\f11e"}.video-js .vjs-skip-forward-5 .vjs-icon-placeholder,.vjs-icon-forward-5{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-skip-forward-5 .vjs-icon-placeholder:before,.vjs-icon-forward-5:before{content:"\f11f"}.video-js .vjs-skip-forward-10 .vjs-icon-placeholder,.vjs-icon-forward-10{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-skip-forward-10 .vjs-icon-placeholder:before,.vjs-icon-forward-10:before{content:"\f120"}.video-js .vjs-skip-forward-30 .vjs-icon-placeholder,.vjs-icon-forward-30{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-skip-forward-30 .vjs-icon-placeholder:before,.vjs-icon-forward-30:before{content:"\f121"}.video-js .vjs-audio-button .vjs-icon-placeholder,.vjs-icon-audio{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-audio-button .vjs-icon-placeholder:before,.vjs-icon-audio:before{content:"\f122"}.vjs-icon-next-item{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-next-item:before{content:"\f123"}.vjs-icon-previous-item{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-previous-item:before{content:"\f124"}.vjs-icon-shuffle{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-shuffle:before{content:"\f125"}.vjs-icon-cast{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-cast:before{content:"\f126"}.video-js .vjs-picture-in-picture-control .vjs-icon-placeholder,.vjs-icon-picture-in-picture-enter{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-picture-in-picture-control .vjs-icon-placeholder:before,.vjs-icon-picture-in-picture-enter:before{content:"\f127"}.video-js.vjs-picture-in-picture .vjs-picture-in-picture-control .vjs-icon-placeholder,.vjs-icon-picture-in-picture-exit{font-family:VideoJS;font-weight:400;font-style:normal}.video-js.vjs-picture-in-picture .vjs-picture-in-picture-control .vjs-icon-placeholder:before,.vjs-icon-picture-in-picture-exit:before{content:"\f128"}.vjs-icon-facebook{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-facebook:before{content:"\f129"}.vjs-icon-linkedin{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-linkedin:before{content:"\f12a"}.vjs-icon-twitter{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-twitter:before{content:"\f12b"}.vjs-icon-tumblr{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-tumblr:before{content:"\f12c"}.vjs-icon-pinterest{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-pinterest:before{content:"\f12d"}.video-js .vjs-descriptions-button .vjs-icon-placeholder,.vjs-icon-audio-description{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-descriptions-button .vjs-icon-placeholder:before,.vjs-icon-audio-description:before{content:"\f12e"}.video-js{display:inline-block;vertical-align:top;box-sizing:border-box;color:#fff;background-color:#000;position:relative;padding:0;font-size:10px;line-height:1;font-weight:400;font-style:normal;font-family:Arial,Helvetica,sans-serif;word-break:initial}.video-js:-moz-full-screen{position:absolute}.video-js:-webkit-full-screen{width:100%!important;height:100%!important}.video-js[tabindex="-1"]{outline:0}.video-js *,.video-js :after,.video-js :before{box-sizing:inherit}.video-js ul{font-family:inherit;font-size:inherit;line-height:inherit;list-style-position:outside;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0}.video-js.vjs-1-1,.video-js.vjs-16-9,.video-js.vjs-4-3,.video-js.vjs-9-16,.video-js.vjs-fluid{width:100%;max-width:100%}.video-js.vjs-1-1:not(.vjs-audio-only-mode),.video-js.vjs-16-9:not(.vjs-audio-only-mode),.video-js.vjs-4-3:not(.vjs-audio-only-mode),.video-js.vjs-9-16:not(.vjs-audio-only-mode),.video-js.vjs-fluid:not(.vjs-audio-only-mode){height:0}.video-js.vjs-16-9:not(.vjs-audio-only-mode){padding-top:56.25%}.video-js.vjs-4-3:not(.vjs-audio-only-mode){padding-top:75%}.video-js.vjs-9-16:not(.vjs-audio-only-mode){padding-top:177.7777777778%}.video-js.vjs-1-1:not(.vjs-audio-only-mode){padding-top:100%}.video-js.vjs-fill:not(.vjs-audio-only-mode){width:100%;height:100%}.video-js .vjs-tech{position:absolute;top:0;left:0;width:100%;height:100%}.video-js.vjs-audio-only-mode .vjs-tech{display:none}body.vjs-full-window,body.vjs-pip-window{padding:0;margin:0;height:100%}.vjs-full-window .video-js.vjs-fullscreen,body.vjs-pip-window .video-js{position:fixed;overflow:hidden;z-index:1000;left:0;top:0;bottom:0;right:0}.video-js.vjs-fullscreen:not(.vjs-ios-native-fs),body.vjs-pip-window .video-js{width:100%!important;height:100%!important;padding-top:0!important;display:block}.video-js.vjs-fullscreen.vjs-user-inactive{cursor:none}.vjs-pip-container .vjs-pip-text{position:absolute;bottom:10%;font-size:2em;background-color:rgba(0,0,0,.7);padding:.5em;text-align:center;width:100%}.vjs-layout-small.vjs-pip-container .vjs-pip-text,.vjs-layout-tiny.vjs-pip-container .vjs-pip-text,.vjs-layout-x-small.vjs-pip-container .vjs-pip-text{bottom:0;font-size:1.4em}.vjs-hidden{display:none!important}.vjs-disabled{opacity:.5;cursor:default}.video-js .vjs-offscreen{height:1px;left:-9999px;position:absolute;top:0;width:1px}.vjs-lock-showing{display:block!important;opacity:1!important;visibility:visible!important}.vjs-no-js{padding:20px;color:#fff;background-color:#000;font-size:18px;font-family:Arial,Helvetica,sans-serif;text-align:center;width:300px;height:150px;margin:0 auto}.vjs-no-js a,.vjs-no-js a:visited{color:#66a8cc}.video-js .vjs-big-play-button{font-size:3em;line-height:1.5em;height:1.63332em;width:3em;display:block;position:absolute;top:50%;left:50%;padding:0;margin-top:-.81666em;margin-left:-1.5em;cursor:pointer;opacity:1;border:.06666em solid #fff;background-color:#2b333f;background-color:rgba(43,51,63,.7);border-radius:.3em;transition:all .4s}.vjs-big-play-button .vjs-svg-icon{width:1em;height:1em;position:absolute;top:50%;left:50%;line-height:1;transform:translate(-50%,-50%)}.video-js .vjs-big-play-button:focus,.video-js:hover .vjs-big-play-button{border-color:#fff;background-color:rgb(114.9141509434,132.7028301887,159.3858490566);background-color:rgba(114.9141509434,132.7028301887,159.3858490566,.5);transition:all 0s}.vjs-controls-disabled .vjs-big-play-button,.vjs-error .vjs-big-play-button,.vjs-has-started .vjs-big-play-button,.vjs-using-native-controls .vjs-big-play-button{display:none}.vjs-has-started.vjs-paused.vjs-show-big-play-button-on-pause:not(.vjs-seeking,.vjs-scrubbing,.vjs-error) .vjs-big-play-button{display:block}.video-js button{background:0 0;border:none;color:inherit;display:inline-block;font-size:inherit;line-height:inherit;text-transform:none;text-decoration:none;transition:none;-webkit-appearance:none;-moz-appearance:none;appearance:none}.video-js.vjs-spatial-navigation-enabled .vjs-button:focus{outline:.0625em solid #fff;box-shadow:none}.vjs-control .vjs-button{width:100%;height:100%}.video-js .vjs-control.vjs-close-button{cursor:pointer;height:3em;position:absolute;right:0;top:.5em;z-index:2}.video-js .vjs-modal-dialog{background:rgba(0,0,0,.8);background:linear-gradient(180deg,rgba(0,0,0,.8),rgba(255,255,255,0));overflow:auto}.video-js .vjs-modal-dialog>*{box-sizing:border-box}.vjs-modal-dialog .vjs-modal-dialog-content{font-size:1.2em;line-height:1.5;padding:20px 24px;z-index:1}.vjs-menu-button{cursor:pointer}.vjs-menu-button.vjs-disabled{cursor:default}.vjs-workinghover .vjs-menu-button.vjs-disabled:hover .vjs-menu{display:none}.vjs-menu .vjs-menu-content{display:block;padding:0;margin:0;font-family:Arial,Helvetica,sans-serif;overflow:auto}.vjs-menu .vjs-menu-content>*{box-sizing:border-box}.vjs-scrubbing .vjs-control.vjs-menu-button:hover .vjs-menu{display:none}.vjs-menu li{display:flex;justify-content:center;list-style:none;margin:0;padding:.2em 0;line-height:1.4em;font-size:1.2em;text-align:center;text-transform:lowercase}.js-focus-visible .vjs-menu li.vjs-menu-item:hover,.vjs-menu li.vjs-menu-item:focus,.vjs-menu li.vjs-menu-item:hover{background-color:rgb(114.9141509434,132.7028301887,159.3858490566);background-color:rgba(114.9141509434,132.7028301887,159.3858490566,.5)}.js-focus-visible .vjs-menu li.vjs-selected:hover,.vjs-menu li.vjs-selected,.vjs-menu li.vjs-selected:focus,.vjs-menu li.vjs-selected:hover{background-color:#fff;color:#2b333f}.js-focus-visible .vjs-menu li.vjs-selected:hover .vjs-svg-icon,.vjs-menu li.vjs-selected .vjs-svg-icon,.vjs-menu li.vjs-selected:focus .vjs-svg-icon,.vjs-menu li.vjs-selected:hover .vjs-svg-icon{fill:#000}.js-focus-visible .vjs-menu :not(.vjs-selected):focus:not(.focus-visible),.video-js .vjs-menu :not(.vjs-selected):focus:not(:focus-visible){background:0 0}.vjs-menu li.vjs-menu-title{text-align:center;text-transform:uppercase;font-size:1em;line-height:2em;padding:0;margin:0 0 .3em 0;font-weight:700;cursor:default}.vjs-menu-button-popup .vjs-menu{display:none;position:absolute;bottom:0;width:10em;left:-3em;height:0;margin-bottom:1.5em;border-top-color:rgba(43,51,63,.7)}.vjs-pip-window .vjs-menu-button-popup .vjs-menu{left:unset;right:1em}.vjs-menu-button-popup .vjs-menu .vjs-menu-content{background-color:#2b333f;background-color:rgba(43,51,63,.7);position:absolute;width:100%;bottom:1.5em;max-height:15em}.vjs-layout-tiny .vjs-menu-button-popup .vjs-menu .vjs-menu-content,.vjs-layout-x-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content{max-height:5em}.vjs-layout-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content{max-height:10em}.vjs-layout-medium .vjs-menu-button-popup .vjs-menu .vjs-menu-content{max-height:14em}.vjs-layout-huge .vjs-menu-button-popup .vjs-menu .vjs-menu-content,.vjs-layout-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content,.vjs-layout-x-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content{max-height:25em}.vjs-menu-button-popup .vjs-menu.vjs-lock-showing,.vjs-workinghover .vjs-menu-button-popup.vjs-hover .vjs-menu{display:block}.video-js .vjs-menu-button-inline{transition:all .4s;overflow:hidden}.video-js .vjs-menu-button-inline:before{width:2.222222222em}.video-js .vjs-menu-button-inline.vjs-slider-active,.video-js .vjs-menu-button-inline:focus,.video-js .vjs-menu-button-inline:hover{width:12em}.vjs-menu-button-inline .vjs-menu{opacity:0;height:100%;width:auto;position:absolute;left:4em;top:0;padding:0;margin:0;transition:all .4s}.vjs-menu-button-inline.vjs-slider-active .vjs-menu,.vjs-menu-button-inline:focus .vjs-menu,.vjs-menu-button-inline:hover .vjs-menu{display:block;opacity:1}.vjs-menu-button-inline .vjs-menu-content{width:auto;height:100%;margin:0;overflow:hidden}.video-js .vjs-control-bar{display:none;width:100%;position:absolute;bottom:0;left:0;right:0;height:3em;background-color:#2b333f;background-color:rgba(43,51,63,.7)}.video-js.vjs-spatial-navigation-enabled .vjs-control-bar{gap:1px}.video-js:not(.vjs-controls-disabled,.vjs-using-native-controls,.vjs-error) .vjs-control-bar.vjs-lock-showing{display:flex!important}.vjs-audio-only-mode .vjs-control-bar,.vjs-has-started .vjs-control-bar{display:flex;visibility:visible;opacity:1;transition:visibility .1s,opacity .1s}.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar{visibility:visible;opacity:0;pointer-events:none;transition:visibility 1s,opacity 1s}.vjs-controls-disabled .vjs-control-bar,.vjs-error .vjs-control-bar,.vjs-using-native-controls .vjs-control-bar{display:none!important}.vjs-audio-only-mode.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar,.vjs-audio.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar{opacity:1;visibility:visible;pointer-events:auto}.video-js .vjs-control{position:relative;text-align:center;margin:0;padding:0;height:100%;width:4em;flex:none}.video-js .vjs-control.vjs-visible-text{width:auto;padding-left:1em;padding-right:1em}.vjs-button>.vjs-icon-placeholder:before{font-size:1.8em;line-height:1.67}.vjs-button>.vjs-icon-placeholder{display:block}.vjs-button>.vjs-svg-icon{display:inline-block}.video-js .vjs-control:focus,.video-js .vjs-control:focus:before,.video-js .vjs-control:hover:before{text-shadow:0 0 1em #fff}.video-js :not(.vjs-visible-text)>.vjs-control-text{border:0;clip:rect(0 0 0 0);height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.video-js .vjs-custom-control-spacer{display:none}.video-js .vjs-progress-control{cursor:pointer;flex:auto;display:flex;align-items:center;min-width:4em;touch-action:none}.video-js .vjs-progress-control.disabled{cursor:default}.vjs-live .vjs-progress-control{display:none}.vjs-liveui .vjs-progress-control{display:flex;align-items:center}.video-js .vjs-progress-holder{flex:auto;transition:all .2s;height:.3em}.video-js .vjs-progress-control .vjs-progress-holder{margin:0 10px}.video-js .vjs-progress-control:hover .vjs-progress-holder,.video-js.vjs-scrubbing.vjs-touch-enabled .vjs-progress-control .vjs-progress-holder{font-size:1.6666666667em}.video-js .vjs-progress-control:hover .vjs-progress-holder.disabled{font-size:1em}.video-js .vjs-progress-holder .vjs-load-progress,.video-js .vjs-progress-holder .vjs-load-progress div,.video-js .vjs-progress-holder .vjs-play-progress{position:absolute;display:block;height:100%;margin:0;padding:0;width:0}.video-js .vjs-play-progress{background-color:#fff}.video-js .vjs-play-progress:before{font-size:.9em;position:absolute;right:-.5em;line-height:.35em;z-index:1}.vjs-svg-icons-enabled .vjs-play-progress:before{content:none!important}.vjs-play-progress .vjs-svg-icon{position:absolute;top:-.35em;right:-.4em;width:.9em;height:.9em;pointer-events:none;line-height:.15em;z-index:1}.video-js .vjs-load-progress{background:rgba(114.9141509434,132.7028301887,159.3858490566,.5)}.video-js .vjs-load-progress div{background:rgba(114.9141509434,132.7028301887,159.3858490566,.75)}.video-js .vjs-time-tooltip{background-color:#fff;background-color:rgba(255,255,255,.8);border-radius:.3em;color:#000;float:right;font-family:Arial,Helvetica,sans-serif;font-size:1em;padding:6px 8px 8px 8px;pointer-events:none;position:absolute;top:-3.4em;visibility:hidden;z-index:1}.video-js .vjs-progress-holder:focus .vjs-time-tooltip{display:none}.video-js .vjs-progress-control:hover .vjs-progress-holder:focus .vjs-time-tooltip,.video-js .vjs-progress-control:hover .vjs-time-tooltip,.video-js.vjs-scrubbing.vjs-touch-enabled .vjs-progress-control .vjs-time-tooltip{display:block;font-size:.6em;visibility:visible}.video-js .vjs-progress-control.disabled:hover .vjs-time-tooltip{font-size:1em}.video-js .vjs-progress-control .vjs-mouse-display{display:none;position:absolute;width:1px;height:100%;background-color:#000;z-index:1}.video-js .vjs-progress-control:hover .vjs-mouse-display{display:block}.video-js.vjs-scrubbing.vjs-touch-enabled .vjs-progress-control .vjs-mouse-display{display:block}.video-js.vjs-touch-enabled:not(.vjs-scrubbing) .vjs-progress-control .vjs-mouse-display,.video-js.vjs-user-inactive .vjs-progress-control .vjs-mouse-display{visibility:hidden;opacity:0;transition:visibility 1s,opacity 1s}.vjs-mouse-display .vjs-time-tooltip{color:#fff;background-color:#000;background-color:rgba(0,0,0,.8)}.video-js .vjs-slider{position:relative;cursor:pointer;padding:0;margin:0 .45em 0 .45em;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:rgb(114.9141509434,132.7028301887,159.3858490566);background-color:rgba(114.9141509434,132.7028301887,159.3858490566,.5)}.video-js .vjs-slider.disabled{cursor:default}.video-js .vjs-slider:focus{text-shadow:0 0 1em #fff;box-shadow:0 0 1em #fff}.video-js.vjs-spatial-navigation-enabled .vjs-slider:focus{outline:.0625em solid #fff}.video-js .vjs-mute-control{cursor:pointer;flex:none}.video-js .vjs-volume-control{cursor:pointer;margin-right:1em;display:flex}.video-js .vjs-volume-control.vjs-volume-horizontal{width:5em}.video-js .vjs-volume-panel .vjs-volume-control{visibility:visible;opacity:0;width:1px;height:1px;margin-left:-1px}.video-js .vjs-volume-panel{transition:width 1s}.video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active,.video-js .vjs-volume-panel .vjs-volume-control:active,.video-js .vjs-volume-panel.vjs-hover .vjs-mute-control~.vjs-volume-control,.video-js .vjs-volume-panel.vjs-hover .vjs-volume-control,.video-js .vjs-volume-panel:active .vjs-volume-control,.video-js .vjs-volume-panel:focus .vjs-volume-control{visibility:visible;opacity:1;position:relative;transition:visibility .1s,opacity .1s,height .1s,width .1s,left 0s,top 0s}.video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active.vjs-volume-horizontal,.video-js .vjs-volume-panel .vjs-volume-control:active.vjs-volume-horizontal,.video-js .vjs-volume-panel.vjs-hover .vjs-mute-control~.vjs-volume-control.vjs-volume-horizontal,.video-js .vjs-volume-panel.vjs-hover .vjs-volume-control.vjs-volume-horizontal,.video-js .vjs-volume-panel:active .vjs-volume-control.vjs-volume-horizontal,.video-js .vjs-volume-panel:focus .vjs-volume-control.vjs-volume-horizontal{width:5em;height:3em;margin-right:0}.video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active.vjs-volume-vertical,.video-js .vjs-volume-panel .vjs-volume-control:active.vjs-volume-vertical,.video-js .vjs-volume-panel.vjs-hover .vjs-mute-control~.vjs-volume-control.vjs-volume-vertical,.video-js .vjs-volume-panel.vjs-hover .vjs-volume-control.vjs-volume-vertical,.video-js .vjs-volume-panel:active .vjs-volume-control.vjs-volume-vertical,.video-js .vjs-volume-panel:focus .vjs-volume-control.vjs-volume-vertical{left:-3.5em;transition:left 0s}.video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-hover,.video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active,.video-js .vjs-volume-panel.vjs-volume-panel-horizontal:active{width:10em;transition:width .1s}.video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-mute-toggle-only{width:4em}.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical{height:8em;width:3em;left:-3000em;transition:visibility 1s,opacity 1s,height 1s 1s,width 1s 1s,left 1s 1s,top 1s 1s}.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal{transition:visibility 1s,opacity 1s,height 1s 1s,width 1s,left 1s 1s,top 1s 1s}.video-js .vjs-volume-panel{display:flex}.video-js .vjs-volume-bar{margin:1.35em .45em}.vjs-volume-bar.vjs-slider-horizontal{width:5em;height:.3em}.vjs-volume-bar.vjs-slider-vertical{width:.3em;height:5em;margin:1.35em auto}.video-js .vjs-volume-level{position:absolute;bottom:0;left:0;background-color:#fff}.video-js .vjs-volume-level:before{position:absolute;font-size:.9em;z-index:1}.vjs-slider-vertical .vjs-volume-level{width:.3em}.vjs-slider-vertical .vjs-volume-level:before{top:-.5em;left:-.3em;z-index:1}.vjs-svg-icons-enabled .vjs-volume-level:before{content:none}.vjs-volume-level .vjs-svg-icon{position:absolute;width:.9em;height:.9em;pointer-events:none;z-index:1}.vjs-slider-horizontal .vjs-volume-level{height:.3em}.vjs-slider-horizontal .vjs-volume-level:before{line-height:.35em;right:-.5em}.vjs-slider-horizontal .vjs-volume-level .vjs-svg-icon{right:-.3em;transform:translateY(-50%)}.vjs-slider-vertical .vjs-volume-level .vjs-svg-icon{top:-.55em;transform:translateX(-50%)}.video-js .vjs-volume-panel.vjs-volume-panel-vertical{width:4em}.vjs-volume-bar.vjs-slider-vertical .vjs-volume-level{height:100%}.vjs-volume-bar.vjs-slider-horizontal .vjs-volume-level{width:100%}.video-js .vjs-volume-vertical{width:3em;height:8em;bottom:8em;background-color:#2b333f;background-color:rgba(43,51,63,.7)}.video-js .vjs-volume-horizontal .vjs-menu{left:-2em}.video-js .vjs-volume-tooltip{background-color:#fff;background-color:rgba(255,255,255,.8);border-radius:.3em;color:#000;float:right;font-family:Arial,Helvetica,sans-serif;font-size:1em;padding:6px 8px 8px 8px;pointer-events:none;position:absolute;top:-3.4em;visibility:hidden;z-index:1}.video-js .vjs-volume-control:hover .vjs-progress-holder:focus .vjs-volume-tooltip,.video-js .vjs-volume-control:hover .vjs-volume-tooltip{display:block;font-size:1em;visibility:visible}.video-js .vjs-volume-vertical:hover .vjs-progress-holder:focus .vjs-volume-tooltip,.video-js .vjs-volume-vertical:hover .vjs-volume-tooltip{left:1em;top:-12px}.video-js .vjs-volume-control.disabled:hover .vjs-volume-tooltip{font-size:1em}.video-js .vjs-volume-control .vjs-mouse-display{display:none;position:absolute;width:100%;height:1px;background-color:#000;z-index:1}.video-js .vjs-volume-horizontal .vjs-mouse-display{width:1px;height:100%}.video-js .vjs-volume-control:hover .vjs-mouse-display{display:block}.video-js.vjs-user-inactive .vjs-volume-control .vjs-mouse-display{visibility:hidden;opacity:0;transition:visibility 1s,opacity 1s}.vjs-mouse-display .vjs-volume-tooltip{color:#fff;background-color:#000;background-color:rgba(0,0,0,.8)}.vjs-poster{display:inline-block;vertical-align:middle;cursor:pointer;margin:0;padding:0;position:absolute;top:0;right:0;bottom:0;left:0;height:100%}.vjs-has-started .vjs-poster,.vjs-using-native-controls .vjs-poster{display:none}.vjs-audio.vjs-has-started .vjs-poster,.vjs-has-started.vjs-audio-poster-mode .vjs-poster,.vjs-pip-container.vjs-has-started .vjs-poster{display:block}.vjs-poster img{width:100%;height:100%;object-fit:contain}.video-js .vjs-live-control{display:flex;align-items:flex-start;flex:auto;font-size:1em;line-height:3em}.video-js.vjs-liveui .vjs-live-control,.video-js:not(.vjs-live) .vjs-live-control{display:none}.video-js .vjs-seek-to-live-control{align-items:center;cursor:pointer;flex:none;display:inline-flex;height:100%;padding-left:.5em;padding-right:.5em;font-size:1em;line-height:3em;width:auto;min-width:4em}.video-js.vjs-live:not(.vjs-liveui) .vjs-seek-to-live-control,.video-js:not(.vjs-live) .vjs-seek-to-live-control{display:none}.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge{cursor:auto}.vjs-seek-to-live-control .vjs-icon-placeholder{margin-right:.5em;color:#888}.vjs-svg-icons-enabled .vjs-seek-to-live-control{line-height:0}.vjs-seek-to-live-control .vjs-svg-icon{width:1em;height:1em;pointer-events:none;fill:#888}.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge .vjs-icon-placeholder{color:red}.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge .vjs-svg-icon{fill:red}.video-js .vjs-time-control{flex:none;font-size:1em;line-height:3em;min-width:2em;width:auto;padding-left:1em;padding-right:1em}.video-js .vjs-current-time,.video-js .vjs-duration,.vjs-live .vjs-time-control,.vjs-live .vjs-time-divider{display:none}.vjs-time-divider{display:none;line-height:3em}.vjs-normalise-time-controls:not(.vjs-live) .vjs-time-control{display:flex}.video-js .vjs-play-control{cursor:pointer}.video-js .vjs-play-control .vjs-icon-placeholder{flex:none}.vjs-text-track-display{position:absolute;bottom:3em;left:0;right:0;top:0;pointer-events:none}.vjs-error .vjs-text-track-display{display:none}.video-js.vjs-controls-disabled .vjs-text-track-display,.video-js.vjs-user-inactive.vjs-playing .vjs-text-track-display{bottom:1em}.video-js .vjs-text-track{font-size:1.4em;text-align:center;margin-bottom:.1em}.vjs-subtitles{color:#fff}.vjs-captions{color:#fc6}.vjs-tt-cue{display:block}video::-webkit-media-text-track-display{transform:translateY(-3em)}.video-js.vjs-controls-disabled video::-webkit-media-text-track-display,.video-js.vjs-user-inactive.vjs-playing video::-webkit-media-text-track-display{transform:translateY(-1.5em)}.video-js.vjs-force-center-align-cues .vjs-text-track-cue{text-align:center!important;width:80%!important}@supports not (inset:10px){.video-js .vjs-text-track-display>div{top:0;right:0;bottom:0;left:0}}.video-js .vjs-picture-in-picture-control{cursor:pointer;flex:none}.video-js.vjs-audio-only-mode .vjs-picture-in-picture-control,.vjs-pip-window .vjs-picture-in-picture-control{display:none}.video-js .vjs-fullscreen-control{cursor:pointer;flex:none}.video-js.vjs-audio-only-mode .vjs-fullscreen-control,.vjs-pip-window .vjs-fullscreen-control{display:none}.vjs-playback-rate .vjs-playback-rate-value,.vjs-playback-rate>.vjs-menu-button{position:absolute;top:0;left:0;width:100%;height:100%}.vjs-playback-rate .vjs-playback-rate-value{pointer-events:none;font-size:1.5em;line-height:2;text-align:center}.vjs-playback-rate .vjs-menu{width:4em;left:0}.vjs-error .vjs-error-display .vjs-modal-dialog-content{font-size:1.4em;text-align:center}.vjs-loading-spinner{display:none;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:.85;text-align:left;border:.6em solid rgba(43,51,63,.7);box-sizing:border-box;background-clip:padding-box;width:5em;height:5em;border-radius:50%;visibility:hidden}.vjs-seeking .vjs-loading-spinner,.vjs-waiting .vjs-loading-spinner{display:flex;justify-content:center;align-items:center;animation:vjs-spinner-show 0s linear .3s forwards}.vjs-error .vjs-loading-spinner{display:none}.vjs-loading-spinner:after,.vjs-loading-spinner:before{content:"";position:absolute;box-sizing:inherit;width:inherit;height:inherit;border-radius:inherit;opacity:1;border:inherit;border-color:transparent;border-top-color:#fff}.vjs-seeking .vjs-loading-spinner:after,.vjs-seeking .vjs-loading-spinner:before,.vjs-waiting .vjs-loading-spinner:after,.vjs-waiting .vjs-loading-spinner:before{animation:vjs-spinner-spin 1.1s cubic-bezier(.6,.2,0,.8) infinite,vjs-spinner-fade 1.1s linear infinite}.vjs-seeking .vjs-loading-spinner:before,.vjs-waiting .vjs-loading-spinner:before{border-top-color:#fff}.vjs-seeking .vjs-loading-spinner:after,.vjs-waiting .vjs-loading-spinner:after{border-top-color:#fff;animation-delay:.44s}@keyframes vjs-spinner-show{to{visibility:visible}}@keyframes vjs-spinner-spin{100%{transform:rotate(360deg)}}@keyframes vjs-spinner-fade{0%{border-top-color:rgb(114.9141509434,132.7028301887,159.3858490566)}20%{border-top-color:rgb(114.9141509434,132.7028301887,159.3858490566)}35%{border-top-color:#fff}60%{border-top-color:rgb(114.9141509434,132.7028301887,159.3858490566)}100%{border-top-color:rgb(114.9141509434,132.7028301887,159.3858490566)}}.video-js.vjs-audio-only-mode .vjs-captions-button{display:none}.vjs-chapters-button .vjs-menu ul{width:24em}.video-js.vjs-audio-only-mode .vjs-descriptions-button{display:none}.vjs-subs-caps-button+.vjs-menu .vjs-captions-menu-item .vjs-svg-icon{width:1.5em;height:1.5em}.video-js .vjs-subs-caps-button+.vjs-menu .vjs-captions-menu-item .vjs-menu-item-text .vjs-icon-placeholder{vertical-align:middle;display:inline-block;margin-bottom:-.1em}.video-js .vjs-subs-caps-button+.vjs-menu .vjs-captions-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before{font-family:VideoJS;content:"\f10c";font-size:1.5em;line-height:inherit}.video-js.vjs-audio-only-mode .vjs-subs-caps-button{display:none}.video-js .vjs-audio-button+.vjs-menu .vjs-descriptions-menu-item .vjs-menu-item-text .vjs-icon-placeholder,.video-js .vjs-audio-button+.vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder{vertical-align:middle;display:inline-block;margin-bottom:-.1em}.video-js .vjs-audio-button+.vjs-menu .vjs-descriptions-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before,.video-js .vjs-audio-button+.vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before{font-family:VideoJS;content:" \f12e";font-size:1.5em;line-height:inherit}.video-js.vjs-layout-small .vjs-current-time,.video-js.vjs-layout-small .vjs-duration,.video-js.vjs-layout-small .vjs-playback-rate,.video-js.vjs-layout-small .vjs-remaining-time,.video-js.vjs-layout-small .vjs-time-divider,.video-js.vjs-layout-small .vjs-volume-control,.video-js.vjs-layout-tiny .vjs-current-time,.video-js.vjs-layout-tiny .vjs-duration,.video-js.vjs-layout-tiny .vjs-playback-rate,.video-js.vjs-layout-tiny .vjs-remaining-time,.video-js.vjs-layout-tiny .vjs-time-divider,.video-js.vjs-layout-tiny .vjs-volume-control,.video-js.vjs-layout-x-small .vjs-current-time,.video-js.vjs-layout-x-small .vjs-duration,.video-js.vjs-layout-x-small .vjs-playback-rate,.video-js.vjs-layout-x-small .vjs-remaining-time,.video-js.vjs-layout-x-small .vjs-time-divider,.video-js.vjs-layout-x-small .vjs-volume-control{display:none}.video-js.vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-hover,.video-js.vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active,.video-js.vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal:active,.video-js.vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal:hover,.video-js.vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-hover,.video-js.vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active,.video-js.vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal:active,.video-js.vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal:hover,.video-js.vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-hover,.video-js.vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active,.video-js.vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal:active,.video-js.vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal:hover{width:auto;width:initial}.video-js.vjs-layout-tiny .vjs-progress-control,.video-js.vjs-layout-x-small .vjs-progress-control{display:none}.video-js.vjs-layout-x-small .vjs-custom-control-spacer{flex:auto;display:block}.vjs-modal-dialog.vjs-text-track-settings{background-color:#2b333f;background-color:rgba(43,51,63,.75);color:#fff;height:70%}.vjs-spatial-navigation-enabled .vjs-modal-dialog.vjs-text-track-settings{height:80%}.vjs-error .vjs-text-track-settings{display:none}.vjs-text-track-settings .vjs-modal-dialog-content{display:table}.vjs-text-track-settings .vjs-track-settings-colors,.vjs-text-track-settings .vjs-track-settings-controls,.vjs-text-track-settings .vjs-track-settings-font{display:table-cell}.vjs-text-track-settings .vjs-track-settings-controls{text-align:right;vertical-align:bottom}@supports (display:grid){.vjs-text-track-settings .vjs-modal-dialog-content{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr;padding:20px 24px 0 24px}.vjs-track-settings-controls .vjs-default-button{margin-bottom:20px}.vjs-text-track-settings .vjs-track-settings-controls{grid-column:1/-1}.vjs-layout-small .vjs-text-track-settings .vjs-modal-dialog-content,.vjs-layout-tiny .vjs-text-track-settings .vjs-modal-dialog-content,.vjs-layout-x-small .vjs-text-track-settings .vjs-modal-dialog-content{grid-template-columns:1fr}}.vjs-text-track-settings select{font-size:inherit}.vjs-track-setting>select{margin-right:1em;margin-bottom:.5em}.vjs-text-track-settings fieldset{margin:10px;border:none}.vjs-text-track-settings fieldset span{display:inline-block;padding:0 .6em .8em}.vjs-text-track-settings fieldset span>select{max-width:7.3em}.vjs-text-track-settings legend{color:#fff;font-weight:700;font-size:1.2em}.vjs-text-track-settings .vjs-label{margin:0 .5em .5em 0}.vjs-track-settings-controls button:active,.vjs-track-settings-controls button:focus{outline-style:solid;outline-width:medium;background-image:linear-gradient(0deg,#fff 88%,rgb(114.9141509434,132.7028301887,159.3858490566) 100%)}.vjs-track-settings-controls button:hover{color:rgba(43,51,63,.75)}.vjs-track-settings-controls button{background-color:#fff;background-image:linear-gradient(-180deg,#fff 88%,rgb(114.9141509434,132.7028301887,159.3858490566) 100%);color:#2b333f;cursor:pointer;border-radius:2px}.vjs-track-settings-controls .vjs-default-button{margin-right:1em}.vjs-title-bar{background:rgba(0,0,0,.9);background:linear-gradient(180deg,rgba(0,0,0,.9) 0,rgba(0,0,0,.7) 60%,rgba(0,0,0,0) 100%);font-size:1.2em;line-height:1.5;transition:opacity .1s;padding:.666em 1.333em 4em;pointer-events:none;position:absolute;top:0;width:100%}.vjs-error .vjs-title-bar{display:none}.vjs-title-bar-description,.vjs-title-bar-title{margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vjs-title-bar-title{font-weight:700;margin-bottom:.333em}.vjs-playing.vjs-user-inactive .vjs-title-bar{opacity:0;transition:opacity 1s}.video-js .vjs-skip-forward-5{cursor:pointer}.video-js .vjs-skip-forward-10{cursor:pointer}.video-js .vjs-skip-forward-30{cursor:pointer}.video-js .vjs-skip-backward-5{cursor:pointer}.video-js .vjs-skip-backward-10{cursor:pointer}.video-js .vjs-skip-backward-30{cursor:pointer}.video-js .vjs-transient-button{position:absolute;height:3em;display:flex;align-items:center;justify-content:center;background-color:rgba(50,50,50,.5);cursor:pointer;opacity:1;transition:opacity 1s}.video-js:not(.vjs-has-started) .vjs-transient-button{display:none}.video-js.not-hover .vjs-transient-button:not(.force-display),.video-js.vjs-user-inactive .vjs-transient-button:not(.force-display){opacity:0}.video-js .vjs-transient-button span{padding:0 .5em}.video-js .vjs-transient-button.vjs-left{left:1em}.video-js .vjs-transient-button.vjs-right{right:1em}.video-js .vjs-transient-button.vjs-top{top:1em}.video-js .vjs-transient-button.vjs-near-top{top:4em}.video-js .vjs-transient-button.vjs-bottom{bottom:4em}.video-js .vjs-transient-button:hover{background-color:rgba(50,50,50,.9)}@media print{.video-js>:not(.vjs-tech):not(.vjs-poster){visibility:hidden}}.vjs-resize-manager{position:absolute;top:0;left:0;width:100%;height:100%;border:none;z-index:-1000}.js-focus-visible .video-js :focus:not(.focus-visible){outline:0}.video-js :focus:not(:focus-visible){outline:0}
|
b/172b2e7bdc5d7b3c9be55b46a36744187755f1671e0382e75d3ba5da8baa1e45
ADDED
|
Binary file (835 Bytes). View file
|
|
|
b/1a9a0e5d2a4b2deefd9c02e2c03534c665aa3268751faa0b0d0f65d5db46ba97
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bfb4ab26e354f2b26c72dff51d262e40e5adf335a3300b8a53c29137549fa950
|
| 3 |
+
size 336000
|
b/1cbf6a00a9ab9358f4ccb53cc434ec3d8c180abbd4a94e89c7447770ee007fbf
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"big-buck-bunny-360p.mp4": "sha256:2a338a2bc796086be42f2b9c85a6efdd2198f5e88c92f5ce0332bf141b8f4edd",
|
| 3 |
+
"cmaf/chunk-stream0-00001.m4s": "sha256:21035f0f735177094c3546b1ea1eaeb3c70f1b41d8189cff1996064a99b68472",
|
| 4 |
+
"cmaf/chunk-stream0-00002.m4s": "sha256:99cbcd159f04cbb04a085ecdb5ee682c3974ea27615653c231a3364532953fe2",
|
| 5 |
+
"cmaf/chunk-stream0-00003.m4s": "sha256:cc83f1fa1fdaf49481e0fc85af64cf8eec921a0c57c889fd10d5cac92fee266d",
|
| 6 |
+
"cmaf/chunk-stream1-00001.m4s": "sha256:31178cbca16267ad92d135296941a9c4a8bf2d84dbac0a61185a4b145c793c7d",
|
| 7 |
+
"cmaf/chunk-stream1-00002.m4s": "sha256:ce330c543c8e85a5b973bdffb3a67c05baf4960aa95a2903f0e7d0f9203fd11f",
|
| 8 |
+
"cmaf/chunk-stream1-00003.m4s": "sha256:489c38565a90a6a583da4315108ef09bf844804f11647cd8c4323cfec1dd804b",
|
| 9 |
+
"cmaf/chunk-stream2-00001.m4s": "sha256:5d19e31a70457d9f8a8432c332d0da8fb6196b94e647b1e35bc3f0f8223c0842",
|
| 10 |
+
"cmaf/chunk-stream2-00002.m4s": "sha256:e48de2ca952ba10b84fb3e60c3e2860969d5a55690fc72bf69ad55891cbe4a90",
|
| 11 |
+
"cmaf/chunk-stream2-00003.m4s": "sha256:220dbaedd6133dd272173a4aa9c101661276890bc488a289f140efc9166b0caa",
|
| 12 |
+
"cmaf/chunk-stream2-00004.m4s": "sha256:0af08e8a87229393c215ca2b278d7292901ad01f075684e7fff0236b3ada6066",
|
| 13 |
+
"cmaf/chunk-stream2-00005.m4s": "sha256:fef85c91bdf64d436aaef4f6c8d1f4d5b3922dbfb1d1eb863eb5f5e436daeb8d",
|
| 14 |
+
"cmaf/chunk-stream2-00006.m4s": "sha256:29009d7ff025a00cb777fee190f434e6b09db12a2dbad96dcdfffcf913d06dd9",
|
| 15 |
+
"cmaf/init-stream0.m4s": "sha256:d42c89f9fe60e901b820966b58c5329a5252f306da9dc469ce98610179553374",
|
| 16 |
+
"cmaf/init-stream1.m4s": "sha256:57846cd2e6ad258aef532034d9e76c911c2cb059fb02f1402fe5ba4a0ac6113d",
|
| 17 |
+
"cmaf/init-stream2.m4s": "sha256:2848238fc77e1e270fbb3e899456e6a6137bc549e27ec9733e1b7e0bc8effafe",
|
| 18 |
+
"cmaf/master.m3u8": "sha256:e77a1243dfed42350a47432a67d21136be6cf7b6858a62dff7b6f5cebe2a5783",
|
| 19 |
+
"cmaf/media_0.m3u8": "sha256:b1d7ea689373a29cf8a6a489eba899d1158c2ae0927597a1f5621cf308cde2a7",
|
| 20 |
+
"cmaf/media_1.m3u8": "sha256:30a190cae16904da28d877bf97d1ffdb81f0d4e5ab3383935a444cb46aefe3e4",
|
| 21 |
+
"cmaf/media_2.m3u8": "sha256:48fbaa4a062d4d9f87eabe69e35c902bb4d10bf1d9e34b194e518a4887fe37b8",
|
| 22 |
+
"cmaf/stream.mpd": "sha256:20683114ef22a87799324ecc34a49f34e86c1bd6f9531715121e6fb1ffd8c706"
|
| 23 |
+
}
|
b/1e75d59c8b442c3b550d09de7425df69f7d73775812de981af07a4efe35e0def
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"type": "audio",
|
| 3 |
+
"format": "flac",
|
| 4 |
+
"mime": "audio/flac",
|
| 5 |
+
"sampleRate": 48000,
|
| 6 |
+
"channels": 2,
|
| 7 |
+
"chunks": [
|
| 8 |
+
{
|
| 9 |
+
"file": "chunk-000.bin",
|
| 10 |
+
"kappa": "sha256:f82b887001d82355c2799c917110e6a2b57abdcc814ca80684e6ebe604375cff",
|
| 11 |
+
"len": 267178
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
"file": "chunk-001.bin",
|
| 15 |
+
"kappa": "sha256:6d2bec1dd4b59b61cd5ea8d9e7c99451d9ef27813fe5c6c82df50e4932f36d03",
|
| 16 |
+
"len": 267178
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"file": "chunk-002.bin",
|
| 20 |
+
"kappa": "sha256:815189cd257fd585996e988d3ce93a88d4e08e780e9982aa3cb4f1a2ea0b571e",
|
| 21 |
+
"len": 267178
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"file": "chunk-003.bin",
|
| 25 |
+
"kappa": "sha256:11080ce6e915418409df45ead1d9fb76aa3ad17941242ee0ef85db8470fa2b90",
|
| 26 |
+
"len": 267178
|
| 27 |
+
},
|
| 28 |
+
{
|
| 29 |
+
"file": "chunk-004.bin",
|
| 30 |
+
"kappa": "sha256:8ef61cd8a64ef6f8555e4b2d12540dbe54ad9309e3b2cea82cae1b539772853f",
|
| 31 |
+
"len": 267178
|
| 32 |
+
},
|
| 33 |
+
{
|
| 34 |
+
"file": "chunk-005.bin",
|
| 35 |
+
"kappa": "sha256:8d2bd316a74f45be4b90036d2e64ed8ea6d9ce84a30742e094685974fa459a64",
|
| 36 |
+
"len": 267178
|
| 37 |
+
}
|
| 38 |
+
],
|
| 39 |
+
"note": "BBB audio as a lossless FLAC κ-object — each chunk re-derives to its declared sha256 (L5) before decode. (Lossless FLAC; source master is AAC, so lossless-of-a-lossy-source.)"
|
| 40 |
+
}
|
b/1ff06debd7f34106c40c7920612f382759d6080adac51e98f2c0e1c3a80e85b3
ADDED
|
@@ -0,0 +1,785 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
| 6 |
+
<title>Holo Video Hologram OS</title>
|
| 7 |
+
<meta name="description" content="Holo Video the hologram-native video player: content-addressed, adaptive (HLS/DASH), low-latency, WebGPU present. Plays any holospace video in any browser." />
|
| 8 |
+
<!-- The native video engine: video.js 8 (HTML5 <video> + Media Source Extensions),
|
| 9 |
+
pinned + sha256-verified in _shared/videojs/ (PROVENANCE.txt, Law L5). The
|
| 10 |
+
HLS/DASH adaptive source handler (@videojs/http-streaming, "VHS") is bundled
|
| 11 |
+
INTO video.min.js — one engine file, no CDN — so 4K/8K ABR + Low-Latency HLS
|
| 12 |
+
work offline. The icon font is embedded in the CSS (no external font). -->
|
| 13 |
+
<link rel="stylesheet" href="./videojs/video-js.min.css" data-holo-shared="videojs/video-js.min.css" />
|
| 14 |
+
<!-- The shared mobile conformance layer (WCAG 2.2 reflow / 48px targets / safe-area /
|
| 15 |
+
no iOS auto-zoom) every shipped holospace adopts (enforce-witnesses gate). -->
|
| 16 |
+
<link rel="stylesheet" href="../../_shared/holo-mobile.css" data-holo-shared="holo-mobile.css" />
|
| 17 |
+
<style>
|
| 18 |
+
:root { color-scheme: dark; --bg: var(--holo-bg); --dim: var(--holo-ink-dim); }
|
| 19 |
+
* { box-sizing: border-box; }
|
| 20 |
+
html, body { height: 100%; margin: 0; }
|
| 21 |
+
body { background: var(--bg); color: #d4d4d4; display: flex; flex-direction: column;
|
| 22 |
+
font: var(--holo-text-sm, 1rem)/1.4 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
|
| 23 |
+
|
| 24 |
+
/* Thin holospace chrome — identity · κ-address · lifecycle. Same vocabulary as
|
| 25 |
+
os.html so a video holospace reads like any other (ADR-026). */
|
| 26 |
+
/* The chrome floats OVER the video and auto-hides while playing — cinematic, edge-to-edge, YouTube-clean. */
|
| 27 |
+
#bar { position: absolute; top: 0; left: 0; right: 0; z-index: 8; display: flex; align-items: center; gap: 0.7rem;
|
| 28 |
+
padding: 0.55rem 0.9rem 1.4rem; background: linear-gradient(180deg, rgba(8,10,14,0.82), rgba(8,10,14,0));
|
| 29 |
+
font: var(--holo-text-sm, 1rem) ui-monospace, "Cascadia Code", Menlo, Consolas, monospace; user-select: none;
|
| 30 |
+
transition: opacity 0.3s ease, transform 0.3s ease; }
|
| 31 |
+
body.chrome-hidden #bar { opacity: 0; transform: translateY(-110%); pointer-events: none; }
|
| 32 |
+
body.chrome-hidden { cursor: none; }
|
| 33 |
+
/* Bare preview mode — no chrome, no overlay, no controls: only the streaming video fills the box. */
|
| 34 |
+
body.bare #overlay, body.bare .vjs-control-bar, body.bare .vjs-big-play-button { display: none !important; }
|
| 35 |
+
body.bare .vjs-tech, body.bare #gpu { object-fit: cover; }
|
| 36 |
+
#bar .name { color: #e6edf3; font-weight: 600; letter-spacing: 0.02em; }
|
| 37 |
+
#bar .tag { color: var(--dim); }
|
| 38 |
+
#bar .k { color: #58a6ff; word-break: break-all; max-width: 46vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
| 39 |
+
#bar .sp { margin-left: auto; }
|
| 40 |
+
#bar .stat { color: var(--dim); font-variant-numeric: tabular-nums; }
|
| 41 |
+
#bar .ver { color: var(--dim); } #bar .ver.ok { color: #3fb950; } #bar .ver.bad { color: #f85149; }
|
| 42 |
+
#bar .dot { color: #d29922; } #bar .dot.on { color: #3fb950; } #bar .dot.off { color: #f85149; }
|
| 43 |
+
#bar button { font: inherit; color: var(--dim); background: transparent;
|
| 44 |
+
border: 1px solid #1b2128; border-radius: 5px; padding: 0.15rem 0.5rem; cursor: pointer; }
|
| 45 |
+
#bar button:hover { color: #e6edf3; border-color: #30363d; }
|
| 46 |
+
|
| 47 |
+
/* The player fills the rest, full-bleed and letterboxed (object-fit), so a 4K
|
| 48 |
+
source presents at the device's real pixels on PC and mobile alike. */
|
| 49 |
+
#stage { position: absolute; inset: 0; background: #000; }
|
| 50 |
+
.video-js { position: absolute; inset: 0; width: 100%; height: 100%; }
|
| 51 |
+
.video-js .vjs-tech { object-fit: contain; }
|
| 52 |
+
.vjs-default-skin { --vjs-theme-primary: #58a6ff; }
|
| 53 |
+
/* Hologram OS WebGPU present surface: the decoded frame, rendered zero-copy
|
| 54 |
+
through the GPU pipeline. JS sizes it to the video's contained rect (correct
|
| 55 |
+
aspect, stage = letterbox). Sits above the source <video>, below the controls;
|
| 56 |
+
pointer-events pass straight through to video.js's controls. */
|
| 57 |
+
#gpu { position: absolute; display: none; z-index: 2; pointer-events: none; image-rendering: auto; }
|
| 58 |
+
body.gpu-on #gpu { display: block; }
|
| 59 |
+
body.gpu-on .vjs-tech { opacity: 0; } /* keep decoding + audio; present via GPU */
|
| 60 |
+
.video-js .vjs-control-bar, .video-js .vjs-big-play-button { z-index: 3; }
|
| 61 |
+
#bar button.on { color: #d2a8ff; border-color: #8957e5; }
|
| 62 |
+
|
| 63 |
+
/* Pre-play status / error panel over the player. */
|
| 64 |
+
#overlay { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
|
| 65 |
+
background: radial-gradient(60% 60% at 50% 45%, rgba(8,10,14,0.5), rgba(2,4,8,0.78)); transition: opacity 0.3s; z-index: 5; pointer-events: none; }
|
| 66 |
+
#overlay.hidden { opacity: 0; }
|
| 67 |
+
#panel { max-width: 40rem; padding: 1.5rem 1.75rem; text-align: center; }
|
| 68 |
+
#panel .spin { width: 2.6rem; height: 2.6rem; margin: 0 auto 0.9rem; border: 3px solid rgba(255,255,255,0.16); border-top-color: #58a6ff; border-radius: 50%; animation: holoSpin 0.8s linear infinite; }
|
| 69 |
+
#panel.err .spin, #overlay.hidden .spin { display: none; }
|
| 70 |
+
@keyframes holoSpin { to { transform: rotate(360deg); } }
|
| 71 |
+
#panel .ttl { color: #e6edf3; font-size: 1.05rem; font-weight: 600; margin-bottom: 0.2rem; }
|
| 72 |
+
#panel .sub { color: var(--dim); }
|
| 73 |
+
#panel.err .ttl { color: #f85149; }
|
| 74 |
+
/* video.js controls fade with the chrome for a clean, immersive frame. */
|
| 75 |
+
.video-js .vjs-control-bar { transition: opacity 0.3s ease; }
|
| 76 |
+
body.chrome-hidden .video-js.vjs-has-started .vjs-control-bar { opacity: 0; pointer-events: none; }
|
| 77 |
+
#toast { position: fixed; bottom: 1rem; left: 50%; transform: translateX(-50%); background: #161b22;
|
| 78 |
+
border: 1px solid #30363d; border-radius: var(--holo-radius-sm, 8px); padding: 0.45rem 0.8rem; font-size: var(--holo-text-sm, 1rem); opacity: 0;
|
| 79 |
+
transition: opacity 0.2s; z-index: 9; pointer-events: none; }
|
| 80 |
+
#toast.show { opacity: 1; }
|
| 81 |
+
|
| 82 |
+
/* Dev loop (Deliverable 0): A/B split (\) = reconstructed | raw; HUD (F8) = cost. Default off. */
|
| 83 |
+
body.ab .vjs-tech { opacity: 1 !important; } /* reveal the raw bilinear video under the canvas */
|
| 84 |
+
#abdiv { position: absolute; width: 2px; background: rgba(255,255,255,.9); z-index: 4; display: none;
|
| 85 |
+
cursor: ew-resize; box-shadow: 0 0 10px rgba(0,0,0,.7); pointer-events: auto; }
|
| 86 |
+
body.ab #abdiv { display: block; }
|
| 87 |
+
#abdiv .grip { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); width: 22px; height: 22px;
|
| 88 |
+
border-radius: 50%; background: #fff; box-shadow: 0 2px 8px rgba(0,0,0,.6); }
|
| 89 |
+
#abdiv::before { content: "HOLOGRAM"; position: absolute; top: 8px; right: 7px; font: 700 11px ui-monospace,monospace; color: #fff; text-shadow: 0 1px 3px #000; }
|
| 90 |
+
#abdiv::after { content: "raw"; position: absolute; top: 8px; left: 7px; font: 700 11px ui-monospace,monospace; color: #fff; text-shadow: 0 1px 3px #000; }
|
| 91 |
+
#hud { position: absolute; top: 10px; left: 10px; z-index: 6; display: none; pointer-events: none; white-space: pre;
|
| 92 |
+
font: 600 11px/1.55 ui-monospace,"Cascadia Code",monospace; color: #7ee787; background: rgba(8,10,14,.8);
|
| 93 |
+
border: 1px solid #30363d; border-radius: var(--holo-radius-sm, 8px); padding: 8px 11px; backdrop-filter: blur(6px); }
|
| 94 |
+
body.hud #hud { display: block; }
|
| 95 |
+
</style>
|
| 96 |
+
<link rel="icon" href="./icon.svg">
|
| 97 |
+
<script>try{if("serviceWorker" in navigator)navigator.serviceWorker.register(new URL("../../root-sw.js",location.href),{type:"module"}).catch(function(){navigator.serviceWorker.register(new URL("../../root-sw-classic.js",location.href)).catch(function(){})})}catch(e){}</script>
|
| 98 |
+
</head>
|
| 99 |
+
<body data-no-teleport>
|
| 100 |
+
|
| 101 |
+
<div id="bar">
|
| 102 |
+
<span class="name">Holo Video</span>
|
| 103 |
+
<span class="k" id="kaddr" title="content address" style="display:none">holo://local</span>
|
| 104 |
+
<span class="ver" id="ver" title="Checked safe in your browser"></span>
|
| 105 |
+
<span class="sp"></span>
|
| 106 |
+
<span class="stat" id="res"></span>
|
| 107 |
+
<button id="audioBtn" title="Mute / unmute">🔊</button>
|
| 108 |
+
<button id="gearBtn" title="Settings" aria-label="Settings" onclick="var a=document.getElementById('adv');a.hidden=!a.hidden;this.classList.toggle('on',!a.hidden)">⚙</button>
|
| 109 |
+
<span id="adv" hidden>
|
| 110 |
+
<button id="qualBtn" title="Quality. Auto adapts to your connection; Max picks the highest">Quality: Auto</button>
|
| 111 |
+
<button id="protoBtn" style="display:none">HLS</button>
|
| 112 |
+
<button id="gpuBtn" title="Improve the picture">Enhance</button>
|
| 113 |
+
<button id="srBtn" title="Sharpen to your screen's full detail">Sharpen</button>
|
| 114 |
+
</span>
|
| 115 |
+
<span class="dot" id="dot" style="display:none">●</span>
|
| 116 |
+
<span class="tag" id="state" style="display:none">idle</span>
|
| 117 |
+
<button id="fsBtn" title="Fullscreen (f)">⛶</button>
|
| 118 |
+
</div>
|
| 119 |
+
<div id="stage">
|
| 120 |
+
<!-- video.js upgrades this <video>. playsinline = no iOS takeover; the engine
|
| 121 |
+
hands 4K to the platform's hardware decoder. -->
|
| 122 |
+
<video id="player" class="video-js vjs-default-skin vjs-big-play-centered"
|
| 123 |
+
playsinline controls preload="auto"></video>
|
| 124 |
+
<canvas id="gpu" aria-hidden="true"></canvas>
|
| 125 |
+
<div id="overlay"><div id="panel">
|
| 126 |
+
<div class="spin" id="pspin"></div>
|
| 127 |
+
<div class="ttl" id="ptitle">Holo Video</div>
|
| 128 |
+
<div class="sub" id="pstatus">loading engine…</div>
|
| 129 |
+
</div></div>
|
| 130 |
+
</div>
|
| 131 |
+
<div id="toast"></div>
|
| 132 |
+
|
| 133 |
+
<!-- The pinned native video engine (video.js 8 UMD global `videojs`). -->
|
| 134 |
+
<script src="./videojs/video.min.js" data-holo-shared="videojs/video.min.js"></script>
|
| 135 |
+
<!-- Hologram OS WebGPU present layer: zero-copy importExternalTexture + an O(1)
|
| 136 |
+
content-addressed LUT grade. Returns null where WebGPU is absent → plain video. -->
|
| 137 |
+
<script src="../../_shared/holo-gpu.js" data-holo-shared="holo-gpu.js"></script>
|
| 138 |
+
<!-- Share affordance + content-address provenance (#k=) + Law-L5 re-derivation. -->
|
| 139 |
+
<script src="../../_shared/teleport.js" data-holo-shared="teleport.js" defer></script>
|
| 140 |
+
<script type="module">
|
| 141 |
+
const $ = (id) => document.getElementById(id);
|
| 142 |
+
const params = new URLSearchParams(location.search);
|
| 143 |
+
const bool = (v, d) => (v == null ? d : v !== "0" && v !== "false");
|
| 144 |
+
|
| 145 |
+
let toastT;
|
| 146 |
+
const toast = (m) => { const el = $("toast"); el.textContent = m; el.classList.add("show");
|
| 147 |
+
clearTimeout(toastT); toastT = setTimeout(() => el.classList.remove("show"), 1800); };
|
| 148 |
+
const setState = (t, cls) => { $("state").textContent = t; $("dot").className = "dot" + (cls ? " " + cls : ""); };
|
| 149 |
+
const showOverlay = (ttl, sub, err) => { const o = $("overlay"); o.classList.remove("hidden");
|
| 150 |
+
$("ptitle").textContent = ttl; $("pstatus").textContent = sub; $("panel").classList.toggle("err", !!err); };
|
| 151 |
+
const hideOverlay = () => $("overlay").classList.add("hidden");
|
| 152 |
+
const extType = (u) => /\.m3u8(\?|$)/i.test(u) ? "application/x-mpegURL"
|
| 153 |
+
: /\.mpd(\?|$)/i.test(u) ? "application/dash+xml"
|
| 154 |
+
: /\.webm(\?|$)/i.test(u) ? "video/webm" : "video/mp4";
|
| 155 |
+
|
| 156 |
+
// ── what to play ─────────────────────────────────────────────────────────
|
| 157 |
+
// Interop contract (so ANY holospace can hand this player a video):
|
| 158 |
+
// ?src=<url|holo://κ/path> the resolvable media (HLS/DASH/MP4/WebM)
|
| 159 |
+
// ?k=/?id=/?addr=/?v=<κ> the content address to DISPLAY + VERIFY (Law L5)
|
| 160 |
+
// ?type= ?poster= ?autoplay= ?muted= ?loop= ?live=
|
| 161 |
+
// No params → the bundled CMAF ladder (one content-addressed fMP4 segment set
|
| 162 |
+
// served as BOTH HLS and DASH; ?proto=dash plays the SAME segments via the MPD).
|
| 163 |
+
const kparam = params.get("k") || params.get("id") || params.get("addr") || params.get("v") || "";
|
| 164 |
+
// Bundled CMAF: identical κ-segments, two open-standard manifests.
|
| 165 |
+
const CMAF = { hls: { src: "video/cmaf/master.m3u8", key: "cmaf/master.m3u8" },
|
| 166 |
+
dash: { src: "video/cmaf/stream.mpd", key: "cmaf/stream.mpd" } };
|
| 167 |
+
const DEFAULT = CMAF[params.get("proto") === "dash" ? "dash" : "hls"];
|
| 168 |
+
let source = {
|
| 169 |
+
src: params.get("src") || DEFAULT.src,
|
| 170 |
+
type: params.get("type") || extType(params.get("src") || DEFAULT.src),
|
| 171 |
+
kappa: kparam || "",
|
| 172 |
+
key: params.get("src") ? "" : DEFAULT.key,
|
| 173 |
+
};
|
| 174 |
+
|
| 175 |
+
const showKaddr = (k) => { $("kaddr").textContent = k ? (k.startsWith("holo://") ? k : "holo://" + k) : "holo://local"; };
|
| 176 |
+
|
| 177 |
+
// ── the engine ───────────────────────────────────────────────────────────
|
| 178 |
+
// VHS tuned for fast start + adaptive quality up to the source maximum (4K/8K):
|
| 179 |
+
// enableLowInitialPlaylist → start on the lowest rendition = first frame ASAP
|
| 180 |
+
// limitRenditionByPlayerDimensions / useDevicePixelRatio → never fetch more
|
| 181 |
+
// pixels than the device shows (efficiency), but allow up to native on 4K
|
| 182 |
+
// useNetworkInformationApi / useBandwidthFromLocalStorage → smarter ABR
|
| 183 |
+
// liveui + handlePartialData → Low-Latency HLS (partial segments) for live
|
| 184 |
+
// SEAMLESS FALLBACK: video.js ships as a κ-pinned shared bundle (/.holo/sha256/…). If it isn't served
|
| 185 |
+
// (a κ not in this host's pin set), `videojs` is undefined — instead of leaving a dead <video>, drive
|
| 186 |
+
// the NATIVE <video> through a minimal videojs-shaped shim, so progressive media (mp4/webm/ogg) plays
|
| 187 |
+
// anywhere with zero dependency. HLS/DASH (MSE) still want the real videojs; native HLS works on Safari.
|
| 188 |
+
function nativePlayer(el) {
|
| 189 |
+
el.controls = true; el.playsInline = true; el.setAttribute("playsinline", "");
|
| 190 |
+
const p = {
|
| 191 |
+
ready(cb) { Promise.resolve().then(cb); return p; },
|
| 192 |
+
src(o) { const s = typeof o === "string" ? o : (o && o.src) || ""; if (s) el.src = s; },
|
| 193 |
+
on(ev, cb) { el.addEventListener(ev, cb); }, one(ev, cb) { el.addEventListener(ev, cb, { once: true }); },
|
| 194 |
+
off(ev, cb) { try { el.removeEventListener(ev, cb); } catch {} },
|
| 195 |
+
play() { return el.play(); }, pause() { el.pause(); }, paused() { return el.paused; },
|
| 196 |
+
muted(v) { if (v === undefined) return el.muted; el.muted = v; },
|
| 197 |
+
volume(v) { if (v === undefined) return el.volume; el.volume = v; },
|
| 198 |
+
loop(v) { if (v === undefined) return el.loop; el.loop = !!v; },
|
| 199 |
+
poster(v) { if (v === undefined) return el.poster; if (v) el.poster = v; },
|
| 200 |
+
currentTime(v) { if (v === undefined) return el.currentTime; el.currentTime = v; },
|
| 201 |
+
duration() { return el.duration || 0; },
|
| 202 |
+
crossOrigin(v) { if (v === undefined) return el.crossOrigin; if (v) el.crossOrigin = v; else el.removeAttribute("crossorigin"); },
|
| 203 |
+
videoWidth() { return el.videoWidth; }, videoHeight() { return el.videoHeight; },
|
| 204 |
+
error() { return el.error; },
|
| 205 |
+
audioTracks() { return { length: 0, addEventListener() {}, on() {} }; },
|
| 206 |
+
tech() { return { el() { return el; }, el_: el }; },
|
| 207 |
+
requestFullscreen() { return el.requestFullscreen && el.requestFullscreen(); },
|
| 208 |
+
exitFullscreen() { return document.exitFullscreen && document.exitFullscreen(); },
|
| 209 |
+
isFullscreen() { return !!document.fullscreenElement; },
|
| 210 |
+
el() { return el; }, dispose() {},
|
| 211 |
+
};
|
| 212 |
+
return p;
|
| 213 |
+
}
|
| 214 |
+
const VJS = typeof videojs === "function";
|
| 215 |
+
// video.js/VHS is built for HLS/DASH; on a raw holo:// progressive webm/mp4 it can fail SRC_NOT_SUPPORTED
|
| 216 |
+
// (MediaError code 4). Play a κ/holo:// progressive source through the native <video> shim directly — it's
|
| 217 |
+
// reliable, and the WebGPU super-res present still drives the SAME <video> element. HLS/DASH + http(s) keep video.js.
|
| 218 |
+
const initManifest = /m3u8|mpd|mpegurl|dash/i.test((source.src || "") + " " + (source.type || ""));
|
| 219 |
+
const initHoloProgressive = /^holo:\/\//i.test(source.src || "") && !initManifest;
|
| 220 |
+
const player = (VJS && !initHoloProgressive) ? videojs($("player"), {
|
| 221 |
+
controls: true, preload: "auto", playsinline: true, fill: true, responsive: true,
|
| 222 |
+
liveui: true,
|
| 223 |
+
playbackRates: [0.5, 1, 1.25, 1.5, 2],
|
| 224 |
+
html5: { vhs: {
|
| 225 |
+
overrideNative: true,
|
| 226 |
+
enableLowInitialPlaylist: true,
|
| 227 |
+
limitRenditionByPlayerDimensions: true,
|
| 228 |
+
useDevicePixelRatio: true,
|
| 229 |
+
useNetworkInformationApi: true,
|
| 230 |
+
useBandwidthFromLocalStorage: true,
|
| 231 |
+
handlePartialData: true,
|
| 232 |
+
}, nativeAudioTracks: false, nativeVideoTracks: false },
|
| 233 |
+
}) : nativePlayer($("player"));
|
| 234 |
+
if (!VJS) console.warn("Holo Video: video.js bundle unavailable — native <video> fallback (progressive plays; HLS/DASH need the engine).");
|
| 235 |
+
else if (initHoloProgressive) console.warn("Holo Video: holo:// progressive source — using native <video> (bypass video.js/VHS).");
|
| 236 |
+
window.player = player; // witness / automation handle (repo idiom)
|
| 237 |
+
|
| 238 |
+
// A single content-addressed object (mp4/webm) may be streamed through the holo-pull swarm:
|
| 239 |
+
// deadline-ordered, per-block L5-verified κ-pieces, reassembled, then played. Opt-in via ?swarm=1
|
| 240 |
+
// and only for a κ-addressed single object (HLS/DASH manifests already verify per-segment via the
|
| 241 |
+
// holo:// engine, so they take the direct path). Fully non-breaking: no ?swarm → byte-identical.
|
| 242 |
+
const isManifest = (s) => /m3u8|mpd|x-mpegURL|dash\+xml/i.test((s.type || "") + " " + (s.src || ""));
|
| 243 |
+
// Autoplay policy: unmuted autoplay requires a user gesture. Rather than stall on a dead "press play"
|
| 244 |
+
// when there is no gesture (opened programmatically, embedded, or a strict autoplay policy), fall back
|
| 245 |
+
// to MUTED autoplay — which is always permitted — so the stream actually starts, and invite the viewer
|
| 246 |
+
// to unmute. This is the standard streaming behaviour (YouTube/Netflix): a click carries a gesture →
|
| 247 |
+
// plays with sound; no gesture → plays muted, one tap to unmute. The "playing" handler clears the overlay.
|
| 248 |
+
function forceMutedPlay() {
|
| 249 |
+
try {
|
| 250 |
+
player.muted(true);
|
| 251 |
+
player.play().then(() => toast("Playing. Tap 🔊 for sound"))
|
| 252 |
+
.catch(() => { setState("paused", ""); hideOverlay(); }); // clean: the centred play affordance shows; tap anywhere to start
|
| 253 |
+
} catch { setState("paused", ""); hideOverlay(); }
|
| 254 |
+
}
|
| 255 |
+
function tryAutoplay() {
|
| 256 |
+
// First try as-is (with sound when a gesture is present). play() may REJECT (NotAllowedError) or, on
|
| 257 |
+
// some policies, resolve while the element stays paused — handle BOTH: if still paused shortly after,
|
| 258 |
+
// fall back to muted autoplay so the stream always starts.
|
| 259 |
+
try { Promise.resolve(player.play()).catch(() => {}); } catch {}
|
| 260 |
+
setTimeout(() => { try { if (player.paused && player.paused()) forceMutedPlay(); } catch { forceMutedPlay(); } }, 600);
|
| 261 |
+
}
|
| 262 |
+
// ── SponsorBlock: skip in-video sponsor segments (the ad class that lives IN the bytes) ──────
|
| 263 |
+
// Projecting the raw stream removes PLATFORM ads by construction; this removes the creator's own
|
| 264 |
+
// "this video is sponsored by…", self-promo, and like/subscribe nags — client-side auto-skip, fast
|
| 265 |
+
// and progressive-compatible (no re-mux). YouTube /sc/vstream sources only; fail-soft throughout
|
| 266 |
+
// (a missing module or a failed fetch never breaks playback). ?sponsorblock=0 disables.
|
| 267 |
+
let _sb = null, _sbSegs = [], _sbTriedImport = false;
|
| 268 |
+
async function sbLoad(src) {
|
| 269 |
+
_sbSegs = [];
|
| 270 |
+
if (!bool(params.get("sponsorblock"), true)) return;
|
| 271 |
+
if (!_sbTriedImport) { _sbTriedImport = true; try { _sb = await import("../../usr/lib/holo/holo-sponsorblock.mjs"); } catch { _sb = null; } }
|
| 272 |
+
if (!_sb) return;
|
| 273 |
+
let vid = null; try { vid = _sb.videoIdFromVstream(src); } catch {}
|
| 274 |
+
if (!vid) return;
|
| 275 |
+
try { _sbSegs = await _sb.fetchSegments(vid, {}); } catch { _sbSegs = []; }
|
| 276 |
+
if (_sbSegs.length) toast("ad-free · " + _sbSegs.length + " sponsor segment" + (_sbSegs.length > 1 ? "s" : "") + " auto-skipped");
|
| 277 |
+
}
|
| 278 |
+
function sbMaybeSkip() {
|
| 279 |
+
if (!_sb || !_sbSegs.length) return;
|
| 280 |
+
try { const t = player.currentTime(); const to = _sb.skipTarget(t, _sbSegs);
|
| 281 |
+
if (to != null && to > t + 0.05) { player.currentTime(to); toast("⏭ skipped sponsor"); } } catch {}
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
function load(s, opts = {}) {
|
| 285 |
+
source = { ...source, ...s };
|
| 286 |
+
showKaddr(source.kappa);
|
| 287 |
+
$("ver").textContent = ""; $("ver").className = "ver";
|
| 288 |
+
if (!opts._noSwarm && bool(params.get("swarm"), false) && source.kappa && !isManifest(source)) { loadViaSwarm(source, opts); return; }
|
| 289 |
+
// Cross-origin streams + the native host's COEP=credentialless (kappa_scheme.cc): credentialless ADMITS
|
| 290 |
+
// a cross-origin <video src> WITHOUT credentials — so PROGRESSIVE media plays plain (no crossorigin).
|
| 291 |
+
// But MSE (HLS/DASH via VHS) fetches segments as XHR and needs READABLE bytes → CORS, so a cross-origin
|
| 292 |
+
// MANIFEST must be crossorigin=anonymous (an opaque no-cors response is useless to MSE). Hence: CORS for
|
| 293 |
+
// cross-origin manifests, plain for everything else — correct under credentialless AND require-corp.
|
| 294 |
+
try { player.crossOrigin((/^https?:\/\//i.test(source.src) && isManifest(source)) ? "anonymous" : null); } catch {}
|
| 295 |
+
// Instant-start: a holo:// /sc/vstream source plays through MSE (appends the growing transcode) so the
|
| 296 |
+
// first frame shows in ~1s instead of after the full download. Fail-open: any MSE error restores <video src>.
|
| 297 |
+
player.src({ src: source.src, type: source.type });
|
| 298 |
+
if (params.get("poster") || opts.poster) player.poster(params.get("poster") || opts.poster);
|
| 299 |
+
player.muted(bool(params.get("muted"), false)); // SOUND ON BY DEFAULT (host allows unmuted autoplay); ?muted=1 to start muted
|
| 300 |
+
player.loop(bool(params.get("loop"), false));
|
| 301 |
+
setState("loading", "");
|
| 302 |
+
showOverlay("", source.live ? "Going live…" : "Streaming…");
|
| 303 |
+
if (bool(params.get("autoplay") ?? opts.autoplay, true)) tryAutoplay();
|
| 304 |
+
verify(); // Law L5, in parallel
|
| 305 |
+
maybeDeriveKappa(); // opt-in κ discovery for an unaddressed stream (native + size-capped + fail-open)
|
| 306 |
+
sbLoad(source.src); // fetch sponsor segments to auto-skip (YouTube /sc/vstream only), in parallel
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
+
// ── derive a κ for an UNADDRESSED stream (opt-in: ?kderive=1) so verified O(1) replay can cover it ──────
|
| 310 |
+
// Safe by construction: NATIVE host only (the 2nd fetch hits the network-layer κ-cache → cheap, not a
|
| 311 |
+
// re-download), SIZE-CAPPED (multi-GB movies are left to that host cache — never buffered here), runs in
|
| 312 |
+
// the BACKGROUND, and is FULLY FAIL-OPEN (any error → no κ, playback untouched). It has no `want`, so it
|
| 313 |
+
// can never trigger the L5 refuse path. On success it sets source.kappa + re-posts status; the parent
|
| 314 |
+
// (Holo Player) then learns key→κ and the NEXT play is swarm-verified, O(1), offline.
|
| 315 |
+
async function maybeDeriveKappa() {
|
| 316 |
+
try {
|
| 317 |
+
if (source.kappa || !bool(params.get("kderive"), false)) return; // already addressed, or not asked
|
| 318 |
+
if (isManifest(source) || !/^https?:\/\//i.test(source.src)) return; // manifests verify per-segment already
|
| 319 |
+
if (!(window.HoloNative || window.HoloCDP)) return; // native only (κ-cache makes the fetch free)
|
| 320 |
+
const CAP = 512 * 1024 * 1024; // 512 MB ceiling; bigger → host κ-cache's job
|
| 321 |
+
const res = await fetch(source.src, { cache: "force-cache" }); if (!res.ok) return;
|
| 322 |
+
if (+(res.headers.get("content-length") || 0) > CAP) return;
|
| 323 |
+
const buf = new Uint8Array(await res.arrayBuffer()); if (buf.byteLength > CAP) return;
|
| 324 |
+
const k = window.HoloTeleport ? await HoloTeleport.kappaOf(buf) : await sha256(buf);
|
| 325 |
+
if (k && !source.kappa) { source.kappa = k; showKaddr(k); postStatus(player.paused?.() ? "paused" : "playing"); }
|
| 326 |
+
} catch {}
|
| 327 |
+
}
|
| 328 |
+
|
| 329 |
+
// ── Law L5: the played bytes re-derive to their pinned κ ───────────────────
|
| 330 |
+
// For bundled content we know the pin (video-manifest.json); re-derive the
|
| 331 |
+
// top-level object and show ✓. Each HLS SEGMENT is additionally verified by the
|
| 332 |
+
// holo:// engine at resolve time (path→κ→bytes), so the whole stream is covered.
|
| 333 |
+
async function verify() {
|
| 334 |
+
try {
|
| 335 |
+
let want = source.kappa;
|
| 336 |
+
if (!want && source.key) {
|
| 337 |
+
const man = await (await fetch("video/video-manifest.json", { cache: "no-store" })).json();
|
| 338 |
+
want = man[source.key]; source.kappa = want; showKaddr(want);
|
| 339 |
+
}
|
| 340 |
+
if (!want) { $("ver").textContent = ""; return; }
|
| 341 |
+
const buf = new Uint8Array(await (await fetch(source.src, { cache: "no-store" })).arrayBuffer());
|
| 342 |
+
if (window.HoloTeleport) { HoloTeleport.stamp(want); }
|
| 343 |
+
// Verify with the SAME algorithm the κ declares: a sha256-pinned object must
|
| 344 |
+
// be checked with sha256, never the substrate wasm's blake3 (which would
|
| 345 |
+
// falsely refuse it). Only fall back to kappaOf for non-sha256 addresses.
|
| 346 |
+
const want2 = String(want);
|
| 347 |
+
const got = want2.startsWith("sha256:") ? await sha256(buf) : await (window.HoloTeleport ? HoloTeleport.kappaOf(buf) : sha256(buf));
|
| 348 |
+
const ok = !!got && got.split(":").pop() === want2.split(":").pop();
|
| 349 |
+
$("ver").textContent = ok ? "✓ κ verified" : "✗ κ mismatch refused";
|
| 350 |
+
$("ver").className = "ver " + (ok ? "ok" : "bad");
|
| 351 |
+
if (!ok) { player.pause(); showOverlay("Refused", "content does not match its hash (Law L5)", true); setState("refused", "off"); }
|
| 352 |
+
} catch { $("ver").textContent = ""; }
|
| 353 |
+
}
|
| 354 |
+
async function sha256(u8) { const d = await crypto.subtle.digest("SHA-256", u8);
|
| 355 |
+
return "sha256:" + Array.from(new Uint8Array(d), (b) => b.toString(16).padStart(2, "0")).join(""); }
|
| 356 |
+
|
| 357 |
+
// ── swarm path: holo-pull streams the object as per-block L5-verified κ-pieces ──────────────
|
| 358 |
+
// The bridge (holo-pull-media.mjs) pulls every piece through the consume loop over a verify-on-
|
| 359 |
+
// receipt source (production: the mesh/swarm; the served object seeds it), reassembles, and hands
|
| 360 |
+
// back a playable Blob URL — or refuses (url:null) if any piece fails L5 / the declared κ mismatches.
|
| 361 |
+
// The object is verified BEFORE it reaches the decoder (no mismatched bytes ever play). The bridge
|
| 362 |
+
// is the same module witnessed in Node + the browser proof. If unavailable, fall back to direct.
|
| 363 |
+
async function loadViaSwarm(s, opts = {}) {
|
| 364 |
+
setState("loading", ""); showOverlay("Holo Video", "streaming + verifying κ-blocks…");
|
| 365 |
+
try {
|
| 366 |
+
const M = await import("../../_shared/holo-pull-media.mjs"); // absolute virtual FHS alias → usr/lib/holo (depth-independent; matches control/hub/qvac)
|
| 367 |
+
const res = await M.playableFromSwarm(
|
| 368 |
+
async () => new Uint8Array(await (await fetch(s.src, { cache: "no-store" })).arrayBuffer()),
|
| 369 |
+
{ type: s.type || "video/mp4", want: s.kappa || "" });
|
| 370 |
+
if (window.HoloTeleport && s.kappa) HoloTeleport.stamp(s.kappa);
|
| 371 |
+
if (!res.url) { // a piece failed L5, or the whole object ≠ its κ
|
| 372 |
+
$("ver").textContent = "Couldn't verify this stream"; $("ver").className = "ver bad";
|
| 373 |
+
showOverlay("Refused", "content does not match its hash (Law L5)", true); setState("refused", "off");
|
| 374 |
+
return;
|
| 375 |
+
}
|
| 376 |
+
$("ver").textContent = "Verified"; $("ver").className = "ver ok";
|
| 377 |
+
try { player.crossOrigin(null); } catch {}
|
| 378 |
+
player.src({ src: res.url, type: s.type || "video/mp4" });
|
| 379 |
+
if (params.get("poster") || opts.poster) player.poster(params.get("poster") || opts.poster);
|
| 380 |
+
player.muted(bool(params.get("muted"), false));
|
| 381 |
+
player.loop(bool(params.get("loop"), false));
|
| 382 |
+
if (bool(params.get("autoplay") ?? opts.autoplay, true)) tryAutoplay();
|
| 383 |
+
} catch (e) { // bridge missing / fetch failed → direct path
|
| 384 |
+
toast("Playing directly");
|
| 385 |
+
load(s, { ...opts, _noSwarm: true });
|
| 386 |
+
}
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
// ── chrome wiring ──────────────────────────────────────────────────────────
|
| 390 |
+
const fmtRes = () => { const w = player.videoWidth?.() || 0, h = player.videoHeight?.() || 0;
|
| 391 |
+
const tier = h >= 4320 ? "8K" : h >= 2160 ? "4K" : h >= 1440 ? "1440p" : h >= 1080 ? "1080p" : h >= 720 ? "720p" : h ? h + "p" : "";
|
| 392 |
+
$("res").textContent = w && h ? `${w}×${h}` + (tier ? " · " + tier : "") : ""; };
|
| 393 |
+
// Quality control: VHS exposes the renditions; "max" pins the highest (forces
|
| 394 |
+
// 4K/8K regardless of window size), "auto" restores adaptive ABR. ?max=1 boots
|
| 395 |
+
// in max so a remote 4K ladder streams at full resolution on first play.
|
| 396 |
+
// ?height=<N> pins the rendition nearest N (e.g. height=1080 to feed the GPU
|
| 397 |
+
// super-resolution a 1080p source it reconstructs toward the 4K display).
|
| 398 |
+
let qualMode = bool(params.get("max"), false) ? "max" : "auto";
|
| 399 |
+
let heightPin = params.get("height") ? +params.get("height") : 0;
|
| 400 |
+
const reps = () => { try { return player.tech(true).vhs.representations() || []; } catch { return []; } };
|
| 401 |
+
function applyQuality() {
|
| 402 |
+
const rs = reps(); if (!rs.length) { $("qualBtn").textContent = "Quality: Auto"; return; }
|
| 403 |
+
if (heightPin) {
|
| 404 |
+
const tgt = rs.reduce((a, b) => (Math.abs((b.height || 0) - heightPin) < Math.abs((a.height || 0) - heightPin) ? b : a));
|
| 405 |
+
rs.forEach((r) => r.enabled(r === tgt));
|
| 406 |
+
$("qualBtn").textContent = "Quality: " + (tgt.height || "") + "p";
|
| 407 |
+
return;
|
| 408 |
+
}
|
| 409 |
+
const top = rs.reduce((a, b) => ((b.height || 0) > (a.height || 0) ? b : a));
|
| 410 |
+
rs.forEach((r) => r.enabled(qualMode === "auto" ? true : r === top));
|
| 411 |
+
$("qualBtn").textContent = qualMode === "max"
|
| 412 |
+
? "Quality: " + (top.height >= 4320 ? "8K" : top.height >= 2160 ? "4K" : (top.height || "") + "p")
|
| 413 |
+
: "Quality: Auto";
|
| 414 |
+
}
|
| 415 |
+
player.on("loadedmetadata", () => { fmtRes(); applyQuality(); });
|
| 416 |
+
|
| 417 |
+
// ── streaming standard: HLS ⇄ MPEG-DASH over ONE content-addressed CMAF set ──
|
| 418 |
+
// For the bundled content the SAME fMP4 κ-segments are described by both a .m3u8
|
| 419 |
+
// and a .mpd; this swaps the manifest, not the bytes — a live proof of the
|
| 420 |
+
// CMAF unification. Disabled for a remote ?src (it just shows the protocol).
|
| 421 |
+
const curProto = () => (/\.mpd(\?|$)/i.test(source.src) || source.type === "application/dash+xml") ? "DASH" : "HLS";
|
| 422 |
+
const isBundledCmaf = () => source.key === CMAF.hls.key || source.key === CMAF.dash.key;
|
| 423 |
+
function updateProto() {
|
| 424 |
+
const p = curProto(), bundled = isBundledCmaf();
|
| 425 |
+
$("protoBtn").textContent = p; $("protoBtn").classList.toggle("on", bundled); $("protoBtn").disabled = !bundled;
|
| 426 |
+
$("protoBtn").title = bundled ? `${p} click to play the SAME content-addressed CMAF segments via ${p === "HLS" ? "MPEG-DASH" : "HLS"}` : `${p} stream`;
|
| 427 |
+
}
|
| 428 |
+
$("protoBtn").addEventListener("click", () => {
|
| 429 |
+
if (!isBundledCmaf()) return;
|
| 430 |
+
const toDash = curProto() === "HLS", c = toDash ? CMAF.dash : CMAF.hls;
|
| 431 |
+
load({ src: c.src, type: extType(c.src), kappa: "", key: c.key });
|
| 432 |
+
updateProto(); toast("streaming via " + (toDash ? "MPEG-DASH" : "HLS") + " · same κ-segments");
|
| 433 |
+
});
|
| 434 |
+
player.on("loadedmetadata", updateProto);
|
| 435 |
+
$("qualBtn").addEventListener("click", () => { heightPin = 0; qualMode = qualMode === "auto" ? "max" : "auto"; applyQuality(); toast("quality: " + qualMode); });
|
| 436 |
+
player.on("playing", () => { hideOverlay(); setState("playing", "on"); fmtRes(); postStatus("playing"); });
|
| 437 |
+
player.on("pause", () => { setState("paused", ""); postStatus("paused"); });
|
| 438 |
+
player.on("waiting", () => setState("buffering", ""));
|
| 439 |
+
player.on("ended", () => { setState("ended", ""); postStatus("ended"); });
|
| 440 |
+
player.on("error", () => { const e = player.error();
|
| 441 |
+
showOverlay("Playback error", e ? e.message : "could not play this source", true);
|
| 442 |
+
setState("error", "off"); postStatus("error"); });
|
| 443 |
+
player.on("timeupdate", () => { sbMaybeSkip(); postStatus("playing"); });
|
| 444 |
+
|
| 445 |
+
// ── Cinematic auto-hide: the chrome floats over the video and melts away after a few idle seconds while
|
| 446 |
+
// playing; any pointer move / key brings it back. Always shown when paused. Clean, immersive, effortless. ──
|
| 447 |
+
let _chromeT;
|
| 448 |
+
function bumpChrome() {
|
| 449 |
+
document.body.classList.remove("chrome-hidden"); clearTimeout(_chromeT);
|
| 450 |
+
try { if (player.paused && player.paused()) return; } catch {}
|
| 451 |
+
_chromeT = setTimeout(() => { try { if (!player.paused()) document.body.classList.add("chrome-hidden"); } catch {} }, 2800);
|
| 452 |
+
}
|
| 453 |
+
["pointermove", "pointerdown", "keydown", "touchstart"].forEach((ev) => addEventListener(ev, bumpChrome, { passive: true }));
|
| 454 |
+
player.on("playing", bumpChrome);
|
| 455 |
+
player.on("pause", () => { clearTimeout(_chromeT); document.body.classList.remove("chrome-hidden"); });
|
| 456 |
+
|
| 457 |
+
// Bare mode (?bare=1) — a clean, chrome-free surface for in-card hover previews: just the streaming video.
|
| 458 |
+
if (bool(params.get("bare"), false)) { try { $("bar").style.display = "none"; player.controls(false); document.body.classList.add("bare"); } catch {} }
|
| 459 |
+
|
| 460 |
+
$("fsBtn").addEventListener("click", () => { player.isFullscreen() ? player.exitFullscreen() : player.requestFullscreen(); });
|
| 461 |
+
addEventListener("keydown", (e) => {
|
| 462 |
+
if (e.target.tagName === "INPUT") return;
|
| 463 |
+
if (e.key === "f") $("fsBtn").click();
|
| 464 |
+
else if (e.key === " ") { e.preventDefault(); player.paused() ? player.play() : player.pause(); }
|
| 465 |
+
});
|
| 466 |
+
|
| 467 |
+
// ── nestability: a parent holospace can hand us a video over postMessage ────
|
| 468 |
+
// { type:"holo-video", src, kappa?, mime?, autoplay? } → load + play.
|
| 469 |
+
// We answer with { type:"holo-video-status", state, currentTime, duration,
|
| 470 |
+
// width, height, kappa } on every change, and "holo-video-ready" once up — so
|
| 471 |
+
// the player composes into ANY holospace as a drop-in media surface.
|
| 472 |
+
function postStatus(state) {
|
| 473 |
+
const msg = { type: "holo-video-status", state, kappa: source.kappa,
|
| 474 |
+
currentTime: player.currentTime?.() || 0, duration: player.duration?.() || 0,
|
| 475 |
+
width: player.videoWidth?.() || 0, height: player.videoHeight?.() || 0 };
|
| 476 |
+
try { parent !== window && parent.postMessage(msg, "*"); } catch {}
|
| 477 |
+
window.holoVideo.last = msg;
|
| 478 |
+
}
|
| 479 |
+
addEventListener("message", (e) => {
|
| 480 |
+
const m = e.data; if (!m || typeof m !== "object") return;
|
| 481 |
+
if (m.type === "holo-video" && (m.src || m.kappa)) {
|
| 482 |
+
load({ src: m.src || ("holo://" + m.kappa), type: m.mime || extType(m.src || ""), kappa: m.kappa || "", key: "" },
|
| 483 |
+
{ autoplay: m.autoplay !== false });
|
| 484 |
+
toast("playing handoff from parent holospace");
|
| 485 |
+
} else if (m.type === "holo-video-control" && m.action) {
|
| 486 |
+
({ play: () => player.play(), pause: () => player.pause(),
|
| 487 |
+
mute: () => player.muted(true), unmute: () => player.muted(false),
|
| 488 |
+
seek: () => player.currentTime(m.time || 0) }[m.action] || (() => {}))();
|
| 489 |
+
}
|
| 490 |
+
});
|
| 491 |
+
|
| 492 |
+
// ── Hologram OS WebGPU present (zero-copy frames + O(1) content-addressed LUT) ─
|
| 493 |
+
// Renders the decoded <video> through holo-gpu.js. Three modes cycle on the
|
| 494 |
+
// button: native (pristine present + the precomputed LUT grade) → holographic
|
| 495 |
+
// (adds chromatic shimmer / scanlines / bloom / grain) → off (plain <video>).
|
| 496 |
+
// Params: ?gpu=0 disables · ?fx=<0..1> boots holographic at that intensity ·
|
| 497 |
+
// ?grade=<0..1> sets the LUT mix. Absent WebGPU → the button says n/a, plain
|
| 498 |
+
// video plays (works in any browser).
|
| 499 |
+
const gpuCanvas = $("gpu");
|
| 500 |
+
let gpu = null, gpuMode = "off";
|
| 501 |
+
const _fx = params.get("fx");
|
| 502 |
+
const FX = _fx != null ? Math.max(0, Math.min(1, +_fx || 0)) : 0.65; // holographic intensity
|
| 503 |
+
const GRADE = params.get("grade") != null ? Math.max(0, Math.min(1, +params.get("grade"))) : 0.4;
|
| 504 |
+
const bootHolo = _fx != null && +_fx > 0;
|
| 505 |
+
const techVideo = () => (player.tech && player.tech(true) && player.tech(true).el_) || document.querySelector("video.vjs-tech") || document.querySelector("video");
|
| 506 |
+
|
| 507 |
+
function fitGpu() {
|
| 508 |
+
if (!document.body.classList.contains("gpu-on")) return;
|
| 509 |
+
const stage = $("stage"), a = (player.videoWidth?.() || 16) / (player.videoHeight?.() || 9);
|
| 510 |
+
const sw = stage.clientWidth, sh = stage.clientHeight;
|
| 511 |
+
let w = sw, h = Math.round(sw / a); if (h > sh) { h = sh; w = Math.round(sh * a); }
|
| 512 |
+
gpuCanvas.style.width = w + "px"; gpuCanvas.style.height = h + "px";
|
| 513 |
+
gpuCanvas.style.left = Math.round((sw - w) / 2) + "px"; gpuCanvas.style.top = Math.round((sh - h) / 2) + "px";
|
| 514 |
+
}
|
| 515 |
+
new ResizeObserver(fitGpu).observe($("stage"));
|
| 516 |
+
addEventListener("resize", fitGpu);
|
| 517 |
+
player.on("loadedmetadata", fitGpu);
|
| 518 |
+
|
| 519 |
+
function setGpuMode(mode) { // "off" | "native" | "holo"
|
| 520 |
+
gpuMode = mode;
|
| 521 |
+
document.body.classList.toggle("gpu-on", mode !== "off");
|
| 522 |
+
$("gpuBtn").classList.toggle("on", mode !== "off");
|
| 523 |
+
if (gpu) { gpu.setGrade(mode === "off" ? 0 : GRADE); gpu.setFx(mode === "holo" ? FX : 0); }
|
| 524 |
+
$("gpuBtn").textContent = mode === "holo" ? "Enhance: Max" : mode === "native" ? "Enhance: On" : "Enhance: Off";
|
| 525 |
+
fitGpu();
|
| 526 |
+
}
|
| 527 |
+
$("gpuBtn").addEventListener("click", () => {
|
| 528 |
+
if (!gpu) return;
|
| 529 |
+
setGpuMode(gpuMode === "off" ? "native" : gpuMode === "native" ? "holo" : "off");
|
| 530 |
+
toast("Picture: " + gpuMode);
|
| 531 |
+
});
|
| 532 |
+
|
| 533 |
+
// ── GPU super-resolution (Lanczos reconstruction toward native pixels) ───────
|
| 534 |
+
// Needs the GPU present; turning SR on also flips present to native if it was off.
|
| 535 |
+
// Per-GPU super-res safety gate. sr=1 (Lanczos) can HARD-crash the Dawn/D3D12 backend on some GPUs (a
|
| 536 |
+
// renderer-process death, NOT a catchable device-lost). Optimistic + self-healing: try sr=1, but if a prior
|
| 537 |
+
// attempt died mid-render — its "trying" marker survived the crash AND the host's self-heal reload — blocklist
|
| 538 |
+
// super-res for THIS machine and fall back to sr=0. localStorage persists across the renderer crash.
|
| 539 |
+
let srOn = bool(params.get("sr"), false);
|
| 540 |
+
try {
|
| 541 |
+
const LS = window.localStorage;
|
| 542 |
+
if (srOn && LS.getItem("holo.sr.bad")) { srOn = false; } // GPU known-bad (crashed) → no super-res
|
| 543 |
+
else if (srOn && LS.getItem("holo.sr.laggy")) { srOn = false; } // GPU couldn't sustain it before → start smooth
|
| 544 |
+
else if (srOn) {
|
| 545 |
+
const trying = +LS.getItem("holo.sr.trying") || 0;
|
| 546 |
+
if (trying && (Date.now() - trying) < 8000) { // recent attempt that never cleared = it crashed
|
| 547 |
+
LS.setItem("holo.sr.bad", "1"); LS.removeItem("holo.sr.trying"); srOn = false;
|
| 548 |
+
}
|
| 549 |
+
}
|
| 550 |
+
} catch {}
|
| 551 |
+
function setSR(on) {
|
| 552 |
+
srOn = !!on;
|
| 553 |
+
if (gpu) gpu.setSR(srOn);
|
| 554 |
+
$("srBtn").classList.toggle("on", srOn);
|
| 555 |
+
$("srBtn").textContent = srOn ? "Sharpen: On" : "Sharpen";
|
| 556 |
+
if (srOn && gpuMode === "off") setGpuMode("native");
|
| 557 |
+
}
|
| 558 |
+
$("srBtn").addEventListener("click", () => {
|
| 559 |
+
if (!gpu) return;
|
| 560 |
+
setSR(!srOn);
|
| 561 |
+
const s = gpu.srcSize;
|
| 562 |
+
toast(srOn ? `super-resolution ON · Lanczos ${s[0]}×${s[1]} → display` : "super-resolution off");
|
| 563 |
+
});
|
| 564 |
+
|
| 565 |
+
// ── audio — verify every video's track plays ────────────────────────────────
|
| 566 |
+
// Non-destructive: webkitAudioDecodedByteCount proves an AAC track is decoding
|
| 567 |
+
// (it advances even while muted); the button toggles mute. Default muted=FALSE — the host allows
|
| 568 |
+
// unmuted autoplay (autoplay-policy=no-user-gesture-required), so video plays with SOUND; ?muted=1 to start muted.
|
| 569 |
+
let _lastAB = 0, audioRate = 0;
|
| 570 |
+
const audioPresent = (el) => !!(el && ((el.webkitAudioDecodedByteCount || 0) > 0 || (el.audioTracks && el.audioTracks.length) || el.mozHasAudio)) || (player.audioTracks && player.audioTracks().length > 0);
|
| 571 |
+
function updateAudio() {
|
| 572 |
+
const el = techVideo(), present = audioPresent(el), muted = player.muted() || player.volume() === 0;
|
| 573 |
+
$("audioBtn").textContent = !present ? "🔇 no audio" : muted ? "🔈 muted" : (audioRate > 0 ? "🔊 audio" : "🔊 audio");
|
| 574 |
+
$("audioBtn").classList.toggle("on", present && !muted);
|
| 575 |
+
$("audioBtn").title = present
|
| 576 |
+
? `AAC audio track decoding (${audioRate.toFixed(0)} KB/s) click to ${muted ? "unmute" : "mute"}`
|
| 577 |
+
: "no audio track in this source";
|
| 578 |
+
}
|
| 579 |
+
$("audioBtn").addEventListener("click", () => {
|
| 580 |
+
const m = !player.muted(); player.muted(m); if (!m && player.volume() === 0) player.volume(1);
|
| 581 |
+
updateAudio(); toast(m ? "muted" : "unmuted");
|
| 582 |
+
});
|
| 583 |
+
player.on("loadedmetadata", updateAudio); player.on("volumechange", updateAudio); player.on("playing", updateAudio);
|
| 584 |
+
setInterval(() => { const el = techVideo(); const ab = el ? (el.webkitAudioDecodedByteCount || 0) : 0; audioRate = Math.max(0, ab - _lastAB) / 1024; _lastAB = ab; updateAudio(); }, 1000);
|
| 585 |
+
|
| 586 |
+
// ── Immersive spatial audio (WebAudio M/S stereo widening + gentle cinematic glue) ─────────────────────
|
| 587 |
+
// Default-on (?spatial=0 to disable). A MediaElementAudioSourceNode is ONE-SHOT per element, so we guard
|
| 588 |
+
// with a flag and wire src->destination FIRST (audio guaranteed), build the processed graph, then drop the
|
| 589 |
+
// direct wire — if any step throws, the direct wire remains so audio is NEVER lost. Same-origin holo://
|
| 590 |
+
// media is untainted, so WebAudio may process it. Mute/volume (video.js sets el.muted/volume) still apply
|
| 591 |
+
// since the tap is post-element. Width is live-tunable via window.holoSpatial.width.
|
| 592 |
+
let _spatialOn = bool(params.get("spatial"), true), _sideL = null, _sideR = null, _spW = 1.7;
|
| 593 |
+
function initSpatial() {
|
| 594 |
+
if (!_spatialOn) return;
|
| 595 |
+
const el = techVideo();
|
| 596 |
+
if (!el || el.__holoSpatial || !audioPresent(el)) return; // wait until audio actually decodes
|
| 597 |
+
const AC = window.AudioContext || window.webkitAudioContext; if (!AC) return;
|
| 598 |
+
el.__holoSpatial = true; // claim it (source can be made only once)
|
| 599 |
+
try {
|
| 600 |
+
const ctx = new AC();
|
| 601 |
+
const src = ctx.createMediaElementSource(el);
|
| 602 |
+
src.connect(ctx.destination); // (A) GUARANTEE audio before anything else
|
| 603 |
+
const g = (v) => { const n = ctx.createGain(); n.gain.value = v; return n; };
|
| 604 |
+
const splitter = ctx.createChannelSplitter(2), merger = ctx.createChannelMerger(2);
|
| 605 |
+
const mid = g(1), side = g(1), lMid = g(0.5), rMid = g(0.5), lSide = g(0.5), rSide = g(-0.5);
|
| 606 |
+
const midL = g(1), midR = g(1); _sideL = g(_spW); _sideR = g(-_spW);
|
| 607 |
+
const comp = ctx.createDynamicsCompressor();
|
| 608 |
+
comp.threshold.value = -18; comp.knee.value = 24; comp.ratio.value = 2; comp.attack.value = 0.006; comp.release.value = 0.25;
|
| 609 |
+
const makeup = g(1.12);
|
| 610 |
+
src.connect(splitter);
|
| 611 |
+
splitter.connect(lMid, 0); splitter.connect(lSide, 0); splitter.connect(rMid, 1); splitter.connect(rSide, 1);
|
| 612 |
+
lMid.connect(mid); rMid.connect(mid); lSide.connect(side); rSide.connect(side);
|
| 613 |
+
mid.connect(midL); mid.connect(midR); side.connect(_sideL); side.connect(_sideR);
|
| 614 |
+
midL.connect(merger, 0, 0); _sideL.connect(merger, 0, 0); midR.connect(merger, 0, 1); _sideR.connect(merger, 0, 1);
|
| 615 |
+
merger.connect(comp); comp.connect(makeup); makeup.connect(ctx.destination);
|
| 616 |
+
src.disconnect(ctx.destination); // (B) drop the direct wire → only processed
|
| 617 |
+
const resume = () => { if (ctx.state === "suspended") ctx.resume().catch(() => {}); };
|
| 618 |
+
el.addEventListener("play", resume); resume();
|
| 619 |
+
window.holoSpatial = { on: true, set width(w) { _spW = Math.max(1, Math.min(3, +w || 1.7)); if (_sideL) _sideL.gain.value = _spW; if (_sideR) _sideR.gain.value = -_spW; }, get width() { return _spW; } };
|
| 620 |
+
toast("Spatial audio on");
|
| 621 |
+
} catch (e) { /* direct wire (A) — if it was made — keeps audio alive; else element plays untouched */ }
|
| 622 |
+
}
|
| 623 |
+
player.on("playing", initSpatial);
|
| 624 |
+
|
| 625 |
+
async function initGpu() {
|
| 626 |
+
if (!bool(params.get("gpu"), true)) { $("gpuBtn").textContent = "Enhance: Off"; $("gpuBtn").disabled = true; $("srBtn").disabled = true; return; }
|
| 627 |
+
if (!window.HoloGPU || !HoloGPU.available()) { $("gpuBtn").textContent = "Enhance: n/a"; $("gpuBtn").disabled = true; $("srBtn").disabled = true;
|
| 628 |
+
$("gpuBtn").title = "WebGPU unavailable here playing plain <video> (runs in any browser)"; return; }
|
| 629 |
+
// onLost: if the GPU device is lost (e.g. a heavy super-res frame trips a TDR), DEGRADE cleanly —
|
| 630 |
+
// setGpuMode("off") drops the gpu-on class so the plain <video> (still decoding + audio) shows
|
| 631 |
+
// through instead of a black canvas. Never a black screen.
|
| 632 |
+
const onLost = () => { try { setSR(false); setGpuMode("off"); } catch {} toast("Reset. Playing standard video"); };
|
| 633 |
+
const onSrEased = () => { try { setSR(false); } catch {} toast("Smooth mode. Eased off for this device"); };
|
| 634 |
+
try { gpu = await HoloGPU.attach(techVideo(), gpuCanvas, { fx: 0, grade: GRADE, sr: srOn, maxDpr: Math.max(1, Math.min(4, +params.get("maxdpr") || 3)), srMaxPx: +params.get("srmaxpx") || 0, onLost, onSrEased }); } catch { gpu = null; }
|
| 635 |
+
if (!gpu) { $("gpuBtn").textContent = "Enhance: n/a"; $("gpuBtn").disabled = true; $("srBtn").disabled = true; return; }
|
| 636 |
+
setGpuMode(bootHolo ? "holo" : "native"); // GPU present is the default experience
|
| 637 |
+
setSR(srOn); // honor ?sr=1
|
| 638 |
+
// Super-res engaged on a working GPU: arm the "trying" marker NOW; a 3s survival (this timer firing = the
|
| 639 |
+
// renderer didn't die) clears it + records the GPU as good. If a Lanczos frame hard-crashes the GPU within
|
| 640 |
+
// 3s, the timer dies with the renderer, the marker survives, and the next load (after the host self-heal
|
| 641 |
+
// reload) reads it and blocklists sr=1 — exactly one crash, then permanent sr=0 for this machine.
|
| 642 |
+
if (srOn) {
|
| 643 |
+
try { localStorage.setItem("holo.sr.trying", String(Date.now())); } catch {}
|
| 644 |
+
setTimeout(() => { try { localStorage.removeItem("holo.sr.trying"); localStorage.setItem("holo.sr.ok", "1"); } catch {} }, 3000);
|
| 645 |
+
}
|
| 646 |
+
toast("Picture enhanced for your screen");
|
| 647 |
+
}
|
| 648 |
+
|
| 649 |
+
// Public handle for witnesses / parents loaded in the same context.
|
| 650 |
+
window.holoVideo = { player, load, verify, get source() { return source; }, get gpu() { return gpu; }, get gpuMode() { return gpuMode; },
|
| 651 |
+
get sr() { return srOn; },
|
| 652 |
+
get audio() { const el = techVideo(); return { present: audioPresent(el), rate: audioRate, muted: player.muted(), decodedBytes: el ? (el.webkitAudioDecodedByteCount || 0) : 0 }; },
|
| 653 |
+
// SponsorBlock state (observability + a test seam): the ad segments being auto-skipped.
|
| 654 |
+
get sbSegments() { return _sbSegs; }, set sbSegments(v) { _sbSegs = Array.isArray(v) ? v : []; },
|
| 655 |
+
async sbArm(segs) { try { if (!_sb) _sb = await import("../../usr/lib/holo/holo-sponsorblock.mjs"); } catch {} _sbSegs = Array.isArray(segs) ? segs : []; return !!_sb; },
|
| 656 |
+
last: null };
|
| 657 |
+
|
| 658 |
+
// ── Dev loop (Deliverable 0): SEE the reconstruction. "\" toggles an A/B split (Lanczos-reconstructed
|
| 659 |
+
// canvas clipped to the left of a draggable divider, raw bilinear <video> revealed on the right); "F8"
|
| 660 |
+
// toggles a quality/cost HUD (fps, dropped frames, source→output upscale, SR path, GPU, gate). Default
|
| 661 |
+
// off → zero effect on normal playback. This is the instrument the GPU phases are tuned against (the
|
| 662 |
+
// automated harness has no WebGPU adapter, so all visual validation happens here, live, on the real GPU).
|
| 663 |
+
(function () {
|
| 664 |
+
const stage = $("stage"), cv = $("gpu");
|
| 665 |
+
const div = document.createElement("div"); div.id = "abdiv"; div.innerHTML = '<div class="grip"></div>'; stage.appendChild(div);
|
| 666 |
+
const hud = document.createElement("div"); hud.id = "hud"; stage.appendChild(hud);
|
| 667 |
+
let abOn = false, abFrac = 0.5, hudOn = false;
|
| 668 |
+
function placeAB() {
|
| 669 |
+
if (!abOn) { cv.style.clipPath = ""; return; }
|
| 670 |
+
cv.style.clipPath = "inset(0 " + ((1 - abFrac) * 100).toFixed(2) + "% 0 0)"; // canvas visible only left of the divider
|
| 671 |
+
div.style.left = (cv.offsetLeft + abFrac * cv.offsetWidth) + "px";
|
| 672 |
+
div.style.top = cv.offsetTop + "px"; div.style.height = cv.offsetHeight + "px";
|
| 673 |
+
}
|
| 674 |
+
function toggleAB() {
|
| 675 |
+
abOn = !abOn; document.body.classList.toggle("ab", abOn);
|
| 676 |
+
if (abOn && !document.body.classList.contains("gpu-on")) toast("A/B: turn on Enhance to compare");
|
| 677 |
+
if (abOn) placeAB(); else cv.style.clipPath = "";
|
| 678 |
+
toast(abOn ? "A/B split on — drag the divider" : "A/B off");
|
| 679 |
+
}
|
| 680 |
+
let dragging = false;
|
| 681 |
+
div.addEventListener("pointerdown", (e) => { dragging = true; try { div.setPointerCapture(e.pointerId); } catch (_) {} e.preventDefault(); });
|
| 682 |
+
div.addEventListener("pointermove", (e) => { if (!dragging) return; const r = cv.getBoundingClientRect(); if (r.width) { abFrac = Math.max(0.05, Math.min(0.95, (e.clientX - r.left) / r.width)); placeAB(); } });
|
| 683 |
+
const endDrag = (e) => { dragging = false; try { div.releasePointerCapture(e.pointerId); } catch (_) {} };
|
| 684 |
+
div.addEventListener("pointerup", endDrag); div.addEventListener("pointercancel", endDrag);
|
| 685 |
+
window.addEventListener("resize", () => { if (abOn) placeAB(); });
|
| 686 |
+
const dq = () => { try { const v = techVideo(); const q = v.getVideoPlaybackQuality ? v.getVideoPlaybackQuality() : {}; return (q.droppedVideoFrames || 0) + "/" + (q.totalVideoFrames || 0); } catch (e) { return "-"; } };
|
| 687 |
+
const gate = () => { try { if (localStorage.getItem("holo.sr.bad")) return "BAD (blocklisted)"; if (localStorage.getItem("holo.sr.laggy")) return "laggy (sr eased)"; if (localStorage.getItem("holo.sr.ok")) return "ok"; return "untested"; } catch (e) { return "-"; } };
|
| 688 |
+
function updateHUD() {
|
| 689 |
+
if (!hudOn) return;
|
| 690 |
+
const g = (window.holoVideo && window.holoVideo.gpu) || null;
|
| 691 |
+
if (!g) { hud.textContent = "HOLO VIDEO · dev (F8)\nGPU: not attached, standard video"; return; }
|
| 692 |
+
const ss = g.srcSize || [0, 0], ts = g.target || [0, 0];
|
| 693 |
+
const up = ss[0] > 0 ? (ts[0] / ss[0]).toFixed(2) : "-";
|
| 694 |
+
hud.textContent =
|
| 695 |
+
"HOLO VIDEO · dev (F8)\n" +
|
| 696 |
+
"fps " + g.winFps + " scale " + g.scale + " drop " + dq() + "\n" +
|
| 697 |
+
"src " + ss[0] + "x" + ss[1] + " -> out " + ts[0] + "x" + ts[1] + " (" + up + "x)\n" +
|
| 698 |
+
"SR " + (g.sr ? "lanczos" : "off") + " mode " + (window.holoVideo ? window.holoVideo.gpuMode : "?") + "\n" +
|
| 699 |
+
"gpu " + ((g.adapter && (g.adapter.description || g.adapter.vendor)) || "?") + "\n" +
|
| 700 |
+
"gate " + gate();
|
| 701 |
+
}
|
| 702 |
+
setInterval(updateHUD, 500);
|
| 703 |
+
function toggleHUD() { hudOn = !hudOn; document.body.classList.toggle("hud", hudOn); updateHUD(); toast(hudOn ? "HUD on" : "HUD off"); }
|
| 704 |
+
window.addEventListener("keydown", (e) => {
|
| 705 |
+
if (e.key === "\\" || e.code === "Backslash") { e.preventDefault(); toggleAB(); }
|
| 706 |
+
else if (e.key === "F8") { e.preventDefault(); toggleHUD(); }
|
| 707 |
+
});
|
| 708 |
+
// automation/parent seam (the YouTube app can forward keys / drive these)
|
| 709 |
+
window.holoVideoDev = { toggleAB, toggleHUD, setSplit: (f) => { abFrac = Math.max(0.05, Math.min(0.95, +f || 0.5)); if (abOn) placeAB(); }, get ab() { return abOn; }, get hud() { return hudOn; } };
|
| 710 |
+
})();
|
| 711 |
+
|
| 712 |
+
// ── boot ───────────────────────────────────────────────────────────────────
|
| 713 |
+
// ── DASH-direct: INSTANT start, no transcode ────────────────────────────────────────────────────────
|
| 714 |
+
// ?dash=<watch url> → fetch /sc/dash for the SEPARATE VP9 video + Opus audio URLs, stream each through the
|
| 715 |
+
// same-origin /sc/proxy into two MSE SourceBuffers (the standard path — proven to play on this engine).
|
| 716 |
+
// First frame ~1s (header + first cluster), length-independent (only fetch what plays; ~30s read-ahead).
|
| 717 |
+
// Fail-open: any error falls back to the /sc/vstream complete-file path (the ?src= we were also given).
|
| 718 |
+
let _dashStop = null;
|
| 719 |
+
function holoDashPlay(watchUrl, h, onFail) {
|
| 720 |
+
const el = $("player");
|
| 721 |
+
const VM = 'video/webm; codecs="vp9"', AM = 'audio/webm; codecs="opus"';
|
| 722 |
+
let failed = false; const fail = (why) => { if (failed) return; failed = true; console.warn("Holo Video: dash →", why, "→ /sc/vstream"); try { if (_dashStop) _dashStop(); } catch {} if (onFail) onFail(); };
|
| 723 |
+
if (!("MediaSource" in window) || !MediaSource.isTypeSupported(VM) || !MediaSource.isTypeSupported(AM)) { fail("no-mse"); return; }
|
| 724 |
+
showOverlay("Holo Video", "starting…");
|
| 725 |
+
const _t0 = performance.now(); window.__dash = { resolveMs: 0, firstByteMs: 0, firstFrameMs: 0 };
|
| 726 |
+
el.addEventListener("loadeddata", () => { if (!window.__dash.firstFrameMs) window.__dash.firstFrameMs = Math.round(performance.now() - _t0); }, { once: true });
|
| 727 |
+
(async () => {
|
| 728 |
+
let j;
|
| 729 |
+
try { const r = await fetch(`holo://os/sc/dash?url=${encodeURIComponent(watchUrl)}&h=${h}`, { cache: "no-store" }); j = await r.json(); } catch (e) { return fail("resolve:" + e); }
|
| 730 |
+
window.__dash.resolveMs = Math.round(performance.now() - _t0);
|
| 731 |
+
if (!j || !j.video || !j.audio) return fail("no-streams");
|
| 732 |
+
let aborted = false; _dashStop = () => { aborted = true; };
|
| 733 |
+
const ms = new MediaSource(); el.src = URL.createObjectURL(ms);
|
| 734 |
+
let vsb, asb;
|
| 735 |
+
try { await new Promise((res, rej) => { ms.addEventListener("sourceopen", res, { once: true }); setTimeout(() => rej(new Error("sourceopen-timeout")), 6000); }); } catch (e) { return fail(String(e)); }
|
| 736 |
+
try { URL.revokeObjectURL(el.src); vsb = ms.addSourceBuffer(VM); asb = ms.addSourceBuffer(AM); vsb.mode = "sequence"; asb.mode = "sequence"; } catch (e) { return fail("addSB:" + e); }
|
| 737 |
+
const proxy = (u, mime) => `holo://os/sc/proxy?u=${encodeURIComponent(u)}&mime=${encodeURIComponent(mime)}`;
|
| 738 |
+
const sleep = (ms2) => new Promise((r) => setTimeout(r, ms2));
|
| 739 |
+
const appendBuf = (sb, chunk) => new Promise((res, rej) => {
|
| 740 |
+
const done = () => { sb.removeEventListener("updateend", done); sb.removeEventListener("error", err); res(); };
|
| 741 |
+
const err = () => { sb.removeEventListener("updateend", done); sb.removeEventListener("error", err); rej(new Error("sbErr")); };
|
| 742 |
+
sb.addEventListener("updateend", done); sb.addEventListener("error", err);
|
| 743 |
+
try { sb.appendBuffer(chunk); }
|
| 744 |
+
catch (e) { sb.removeEventListener("updateend", done); sb.removeEventListener("error", err);
|
| 745 |
+
if (e && e.name === "QuotaExceededError") { try { const c = el.currentTime; if (sb.buffered.length && c > 12) sb.remove(0, c - 8); } catch {}
|
| 746 |
+
sb.addEventListener("updateend", function r2() { sb.removeEventListener("updateend", r2); try { sb.appendBuffer(chunk); sb.addEventListener("updateend", done, { once: true }); sb.addEventListener("error", err, { once: true }); } catch (e2) { rej(e2); } }, { once: true });
|
| 747 |
+
} else rej(e); }
|
| 748 |
+
});
|
| 749 |
+
const ahead = (sb) => { try { return sb.buffered.length ? sb.buffered.end(sb.buffered.length - 1) - el.currentTime : 0; } catch { return 0; } };
|
| 750 |
+
async function pump(sb, url, mime, isVideo) {
|
| 751 |
+
const resp = await fetch(proxy(url, mime), { cache: "no-store" });
|
| 752 |
+
if (!resp.ok || !resp.body) throw new Error("proxy:" + resp.status);
|
| 753 |
+
const reader = resp.body.getReader();
|
| 754 |
+
for (;;) {
|
| 755 |
+
while (!aborted && ahead(sb) > 30) await sleep(200); // read-ahead cap → length-independent memory
|
| 756 |
+
if (aborted) { try { reader.cancel(); } catch {} return; }
|
| 757 |
+
const { done, value } = await reader.read();
|
| 758 |
+
if (done) return;
|
| 759 |
+
if (value && value.length) { if (isVideo && !window.__dash.firstByteMs) window.__dash.firstByteMs = Math.round(performance.now() - _t0); await appendBuf(sb, value); }
|
| 760 |
+
}
|
| 761 |
+
}
|
| 762 |
+
Promise.all([pump(vsb, j.video, "video/webm", true), pump(asb, j.audio, "audio/webm", false)])
|
| 763 |
+
.then(() => { try { if (ms.readyState === "open") ms.endOfStream(); } catch {} })
|
| 764 |
+
.catch((e) => fail("pump:" + e));
|
| 765 |
+
el.muted = bool(params.get("muted"), false);
|
| 766 |
+
try { Promise.resolve(el.play()).catch(() => { el.muted = true; el.play().catch(() => {}); }); } catch {}
|
| 767 |
+
el.addEventListener("playing", hideOverlay, { once: true });
|
| 768 |
+
})();
|
| 769 |
+
}
|
| 770 |
+
|
| 771 |
+
player.ready(() => {
|
| 772 |
+
const dash = params.get("dash");
|
| 773 |
+
if (dash) {
|
| 774 |
+
holoDashPlay(decodeURIComponent(dash), +params.get("h") || 1080, () => load(source, { autoplay: bool(params.get("autoplay"), true) }));
|
| 775 |
+
} else {
|
| 776 |
+
load(source, { autoplay: bool(params.get("autoplay"), true) });
|
| 777 |
+
}
|
| 778 |
+
initGpu();
|
| 779 |
+
try { parent !== window && parent.postMessage({ type: "holo-video-ready" }, "*"); } catch {}
|
| 780 |
+
});
|
| 781 |
+
</script>
|
| 782 |
+
<script src="../../_shared/holo-theme.js" data-holo-shared="holo-theme.js"></script>
|
| 783 |
+
<script src="../../_shared/holo-manage.js" data-holo-shared="holo-manage.js" defer></script>
|
| 784 |
+
</body>
|
| 785 |
+
</html>
|
b/26cd85a5de850229aae046e87490e4a32c7bfae6c01fa83ca0a6593241f5e54f
ADDED
|
Binary file (765 Bytes). View file
|
|
|
b/29fcd622a2dfc0cccfece8a717e5f8f09061dd0497bd0921717ffd5c104cf71d
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8ef61cd8a64ef6f8555e4b2d12540dbe54ad9309e3b2cea82cae1b539772853f
|
| 3 |
+
size 267178
|
b/2e485cef50d0c92a82dc47e998dc692fb064bb04080a3ce42f9436a85866c7c5
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4daf3f0ab5ac4ed166152d76c15fa88e72114fe5f5b2af021116337926308395
|
| 3 |
+
size 1070023
|
b/3237bdea9058724a3dedc441e487a3c477489a1b71df668ff6d9ad9e838a3f1d
ADDED
|
Binary file (66.2 kB). View file
|
|
|
b/3444f1c9211901b5676d5a44777fab07ff67fed01deda3c5aaa3c1717442af39
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"$comment": "Generated from the hub catalog by tools/gen-app-defs.mjs edit the catalog, not here.",
|
| 3 |
+
"id": "org.hologram.HoloVideo",
|
| 4 |
+
"name": "Holo Video",
|
| 5 |
+
"type": [
|
| 6 |
+
"schema:SoftwareApplication",
|
| 7 |
+
"schema:WebApplication"
|
| 8 |
+
],
|
| 9 |
+
"summary": "A verified video player with adaptive streaming, tamper-proof and serverless.",
|
| 10 |
+
"entry": "index.html",
|
| 11 |
+
"icon": "icon.svg",
|
| 12 |
+
"applicationCategory": "MultimediaApplication",
|
| 13 |
+
"conforms": {
|
| 14 |
+
"specs": [
|
| 15 |
+
"video-streaming",
|
| 16 |
+
"video-holospace",
|
| 17 |
+
"webgpu-present",
|
| 18 |
+
"gpu-super-resolution",
|
| 19 |
+
"video-audio",
|
| 20 |
+
"cmaf-dash"
|
| 21 |
+
]
|
| 22 |
+
},
|
| 23 |
+
"capabilities": {
|
| 24 |
+
"storage": [
|
| 25 |
+
"org.hologram.HoloVideo"
|
| 26 |
+
]
|
| 27 |
+
},
|
| 28 |
+
"shared": [
|
| 29 |
+
"holo-gpu.js",
|
| 30 |
+
"holo-manage.js",
|
| 31 |
+
"holo-mobile.css",
|
| 32 |
+
"holo-theme.js",
|
| 33 |
+
"teleport.js",
|
| 34 |
+
"videojs/video-js.min.css",
|
| 35 |
+
"videojs/video.min.js"
|
| 36 |
+
],
|
| 37 |
+
"engines": [],
|
| 38 |
+
"loader": "video.html",
|
| 39 |
+
"description": [
|
| 40 |
+
{
|
| 41 |
+
"p": "Video you play should be verifiable and embeddable anywhere, with no server."
|
| 42 |
+
},
|
| 43 |
+
{
|
| 44 |
+
"p": "Adaptive HLS streams where every chunk is content-addressed and verified, nestable into any holospace."
|
| 45 |
+
},
|
| 46 |
+
{
|
| 47 |
+
"ul": [
|
| 48 |
+
"Adaptive HLS, every chunk verified",
|
| 49 |
+
"Nestable into any holospace",
|
| 50 |
+
"Full transport and quality selector",
|
| 51 |
+
"Serverless"
|
| 52 |
+
]
|
| 53 |
+
}
|
| 54 |
+
],
|
| 55 |
+
"categories": [
|
| 56 |
+
"AudioVideo",
|
| 57 |
+
"Video",
|
| 58 |
+
"Player"
|
| 59 |
+
],
|
| 60 |
+
"keywords": [
|
| 61 |
+
"video",
|
| 62 |
+
"player",
|
| 63 |
+
"hls",
|
| 64 |
+
"streaming",
|
| 65 |
+
"media",
|
| 66 |
+
"videojs"
|
| 67 |
+
],
|
| 68 |
+
"developer": {
|
| 69 |
+
"id": "org.hologram",
|
| 70 |
+
"name": "Hologram Technologies"
|
| 71 |
+
},
|
| 72 |
+
"license": "Apache-2.0",
|
| 73 |
+
"homepage": "https://hologram.os/apps/video",
|
| 74 |
+
"bugtracker": "https://github.com/Hologram-Technologies/hologram-os/issues",
|
| 75 |
+
"releases": [
|
| 76 |
+
{
|
| 77 |
+
"version": "8.0",
|
| 78 |
+
"date": "2026-06-08",
|
| 79 |
+
"description": "Content-addressed HLS playback, κ-verified chunks, nestable player."
|
| 80 |
+
}
|
| 81 |
+
],
|
| 82 |
+
"accent": "#7b68ee"
|
| 83 |
+
}
|
b/374cc5daa86b2dddbceb83552d561d4856eb60ef631b30916dc92b57275f9eca
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#EXTM3U
|
| 2 |
+
#EXT-X-VERSION:7
|
| 3 |
+
#EXT-X-TARGETDURATION:40
|
| 4 |
+
#EXT-X-MEDIA-SEQUENCE:0
|
| 5 |
+
#EXT-X-MAP:URI="init.mp4"
|
| 6 |
+
#EXTINF:40.000000,
|
| 7 |
+
seg_000.m4s
|
| 8 |
+
#EXT-X-ENDLIST
|
b/375ca224bf188af103815a995dd7ce74e7ba74a5c67f473a03600797db9af450
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f50b4506c938345dc7730895c8b4d83a172cf43c84659fb8889c407a4f452e96
|
| 3 |
+
size 336000
|
b/3a0c90e6fe3baa82e5bf20681a902182c69cbb56d850fce5fa86eb86e863afe8
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a1817e8be47b12b93c0b3862ffe325311b198c985a37b53f98c513ca7ed5e5ff
|
| 3 |
+
size 203560
|
b/3e8be10158d7e4a20f03d1bc7b59a04f97aab20ccbc37b3b599abec0d3681b9f
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4bd8fcf2dbd675115b54e768858642d8b3f0b61794577a04f73103da3c195fc7
|
| 3 |
+
size 336000
|
b/3fd9ff18cd3872dc3f5596a9aa9a02b094ce2b357d2de893ad70dfea23006f24
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"init.mp4": "sha256:5cb42ebbb9090e5d64eda59f265b756d30d81f3f5888caf76e9026c0a4e36614",
|
| 3 |
+
"seg_000.m4s": "sha256:a88ccad597c16cbb165df1d8dda70ea6f9b85681a3792f9c741b19945f7a05a5",
|
| 4 |
+
"codec": "video/mp4; codecs=\"avc1.640033, mp4a.40.2\""
|
| 5 |
+
}
|
b/477220d636a4e6c65d61affa15c10a81de2dcf6df73b8aafb719db547e9074fc
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#EXTM3U
|
| 2 |
+
#EXT-X-VERSION:7
|
| 3 |
+
#EXT-X-TARGETDURATION:10
|
| 4 |
+
#EXT-X-MEDIA-SEQUENCE:0
|
| 5 |
+
#EXT-X-MAP:URI="init.mp4"
|
| 6 |
+
#EXTINF:10.000000,
|
| 7 |
+
seg_000.m4s
|
| 8 |
+
#EXT-X-ENDLIST
|
b/4c9124649152e87d2b41d053579997189860bfffccc8adfc14e86aaccb2471af
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cc83f1fa1fdaf49481e0fc85af64cf8eec921a0c57c889fd10d5cac92fee266d
|
| 3 |
+
size 126429
|
b/5ca266f01ea019c2f4d8a4f146f91a262b05625807304653f92dbb97f261d95a
ADDED
|
Binary file (32.7 kB). View file
|
|
|
b/5d755801f6ad62cf150df6c035e45fb89144bbb163c9a58687a78425f8f609d5
ADDED
|
Binary file (1.67 kB). View file
|
|
|
b/61f5de63f35414694a789bcf05c03382b15ca209e36334ff4e83b4c466c306c0
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"init.mp4": "sha256:26a7d3621bacca0fe410434ac84ec6a226ab2aa003549ef8eb8bfb51559321ce",
|
| 3 |
+
"seg_000.m4s": "sha256:e6f8f48671cdb72f4154487391c75e2faac0fe2b15e581397088feeaee0b35d2",
|
| 4 |
+
"codec": "video/mp4; codecs=\"avc1.640033, mp4a.40.2\"",
|
| 5 |
+
"note": "native 1080p BBB upscaled 2x (lanczos+unsharp) to 4K, 22Mbps + AAC 194k — 9x the source detail of the 360p->4K original"
|
| 6 |
+
}
|
b/61fd98fa00653be79b0948475914d2522be9f83eafe4858180b2bd2e2b5668c4
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#EXTM3U
|
| 2 |
+
#EXT-X-VERSION:7
|
| 3 |
+
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="group_A1",NAME="audio_2",DEFAULT=YES,CHANNELS="2",URI="media_2.m3u8"
|
| 4 |
+
#EXT-X-STREAM-INF:BANDWIDTH=933019,RESOLUTION=640x360,CODECS="avc1.4d401e,mp4a.40.2",AUDIO="group_A1"
|
| 5 |
+
media_0.m3u8
|
| 6 |
+
|
| 7 |
+
#EXT-X-STREAM-INF:BANDWIDTH=530234,RESOLUTION=480x270,CODECS="avc1.42c015,mp4a.40.2",AUDIO="group_A1"
|
| 8 |
+
media_1.m3u8
|
| 9 |
+
|
b/6224c8e805b0f7e2d478edcbd216c01af74150bde7e433f05c8016a6b4dd03f9
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5ec1ff7e3c587680a6cc164ba8b27169eee976673285e348a1ea8ff47ee3a829
|
| 3 |
+
size 336000
|
b/6902f4b785750559a8993a4e55fcdf9c80a44c0d3049b10c1960450d37759a7f
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#EXTM3U
|
| 2 |
+
#EXT-X-VERSION:7
|
| 3 |
+
#EXT-X-STREAM-INF:BANDWIDTH=11026792,AVERAGE-BANDWIDTH=11026792,RESOLUTION=3840x2160,CODECS="avc1.640033,mp4a.40.2"
|
| 4 |
+
stream_0.m3u8
|
| 5 |
+
|
b/792fd7431ff360863e34a67d4190902e01022792eefe63ac2a35c965f5d97af0
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#EXTM3U
|
| 2 |
+
#EXT-X-VERSION:6
|
| 3 |
+
#EXT-X-TARGETDURATION:4
|
| 4 |
+
#EXT-X-MEDIA-SEQUENCE:1
|
| 5 |
+
#EXT-X-MAP:URI="init-stream1.m4s"
|
| 6 |
+
#EXTINF:3.840000,
|
| 7 |
+
#EXT-X-PROGRAM-DATE-TIME:2026-06-07T22:09:21.000+0100
|
| 8 |
+
chunk-stream1-00001.m4s
|
| 9 |
+
#EXTINF:3.840000,
|
| 10 |
+
#EXT-X-PROGRAM-DATE-TIME:2026-06-07T22:09:24.840+0100
|
| 11 |
+
chunk-stream1-00002.m4s
|
| 12 |
+
#EXTINF:2.320000,
|
| 13 |
+
#EXT-X-PROGRAM-DATE-TIME:2026-06-07T22:09:28.680+0100
|
| 14 |
+
chunk-stream1-00003.m4s
|
| 15 |
+
#EXT-X-ENDLIST
|
b/7930427c195c0a738a3d19652b9eeab2012637f8f5e678a41163a89c3b1b78b8
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:815189cd257fd585996e988d3ce93a88d4e08e780e9982aa3cb4f1a2ea0b571e
|
| 3 |
+
size 267178
|
b/7aa800d6da2f38a86d41f9de4eab32d9418eb453d1ce59d56d8a8c151ec54d23
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:99cbcd159f04cbb04a085ecdb5ee682c3974ea27615653c231a3364532953fe2
|
| 3 |
+
size 246425
|
b/7e6baad8d384f52a584e2432e8a12a78c92afbaa9be9ae9731d6a076a4a61ffc
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
| 3 |
+
xmlns="urn:mpeg:dash:schema:mpd:2011"
|
| 4 |
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
| 5 |
+
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
|
| 6 |
+
profiles="urn:mpeg:dash:profile:isoff-live:2011"
|
| 7 |
+
type="static"
|
| 8 |
+
mediaPresentationDuration="PT10.0S"
|
| 9 |
+
maxSegmentDuration="PT2.0S"
|
| 10 |
+
minBufferTime="PT7.6S">
|
| 11 |
+
<ProgramInformation>
|
| 12 |
+
</ProgramInformation>
|
| 13 |
+
<ServiceDescription id="0">
|
| 14 |
+
</ServiceDescription>
|
| 15 |
+
<Period id="0" start="PT0.0S">
|
| 16 |
+
<AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="25/1" maxWidth="640" maxHeight="360" par="16:9" lang="und">
|
| 17 |
+
<Representation id="0" mimeType="video/mp4" codecs="avc1.4d401e" bandwidth="800000" width="640" height="360" sar="1:1">
|
| 18 |
+
<SegmentTemplate timescale="12800" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
| 19 |
+
<SegmentTimeline>
|
| 20 |
+
<S t="0" d="49152" r="1" />
|
| 21 |
+
<S d="29696" />
|
| 22 |
+
</SegmentTimeline>
|
| 23 |
+
</SegmentTemplate>
|
| 24 |
+
</Representation>
|
| 25 |
+
<Representation id="1" mimeType="video/mp4" codecs="avc1.42c015" bandwidth="400000" width="480" height="270" sar="1:1">
|
| 26 |
+
<SegmentTemplate timescale="12800" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
| 27 |
+
<SegmentTimeline>
|
| 28 |
+
<S t="0" d="49152" r="1" />
|
| 29 |
+
<S d="29696" />
|
| 30 |
+
</SegmentTimeline>
|
| 31 |
+
</SegmentTemplate>
|
| 32 |
+
</Representation>
|
| 33 |
+
</AdaptationSet>
|
| 34 |
+
<AdaptationSet id="1" contentType="audio" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" lang="und">
|
| 35 |
+
<Representation id="2" mimeType="audio/mp4" codecs="mp4a.40.2" bandwidth="128000" audioSamplingRate="48000">
|
| 36 |
+
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" />
|
| 37 |
+
<SegmentTemplate timescale="48000" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
| 38 |
+
<SegmentTimeline>
|
| 39 |
+
<S t="0" d="95232" />
|
| 40 |
+
<S d="96256" r="3" />
|
| 41 |
+
<S d="1024" />
|
| 42 |
+
</SegmentTimeline>
|
| 43 |
+
</SegmentTemplate>
|
| 44 |
+
</Representation>
|
| 45 |
+
</AdaptationSet>
|
| 46 |
+
</Period>
|
| 47 |
+
</MPD>
|
b/84acaa85618686868e6b28091c81674894a194d27123b91ab0d4e86f2fd1b973
ADDED
|
Binary file (1.38 kB). View file
|
|
|
b/85ac280da66d2cba7a4ad1487e0b391b40a702b45e63bfae884859d0c50d390f
ADDED
|
Binary file (473 Bytes). View file
|
|
|
b/8bda1d7bee91aac65e1a1db701ea902fba0116e30175c88e245740a9e9a7a33a
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1f803b39d399bc41d651472e9dfc44fc480f4cbb68e6df925a66d3bd09724045
|
| 3 |
+
size 257659
|
b/9149515d6ce8bc6ef4d01509ae02d4cc072c0e04a98e9b411a1a807e5286f2f6
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"init.mp4": "sha256:9daa1b45bef8b6145b5c1ae70297b9f7f67920ed70a04c86b14a4a07197764fa",
|
| 3 |
+
"seg_000.m4s": "sha256:f902dd1a7bd87cb9a0b75eb57a61eeec51ff5f8c1fcd2cafeca852b3ffeafb5b",
|
| 4 |
+
"codec": "video/mp4; codecs=\"avc1.640033, mp4a.40.2\""
|
| 5 |
+
}
|
b/931fc03dc173d2b04f5f3e450414ff58e8d15064ca5e4abc0ed5f092fd6b3688
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
video.js — the native video player component (HTML5 <video> + MSE adaptive streaming).
|
| 2 |
+
|
| 3 |
+
Pinned, integrity-verified external authority (Law L5) — vendored dist builds, not
|
| 4 |
+
loaded from a CDN. video.html loads exactly these bytes; video-witness.mjs re-derives
|
| 5 |
+
their sha256 and refuses a drift. The HLS/DASH source handler (@videojs/http-streaming,
|
| 6 |
+
"VHS") is bundled INTO video.min.js — one engine file, no extra import — so Media Source
|
| 7 |
+
Extensions adaptive streaming (up to 4K/8K, ABR + Low-Latency HLS) works offline. The
|
| 8 |
+
icon font is embedded as a base64 woff data-URI inside the CSS, so there is NO external
|
| 9 |
+
font file: the player is exactly two content-addressed bytes-objects + a license.
|
| 10 |
+
|
| 11 |
+
Versions:
|
| 12 |
+
video.js 8.23.7 (Apache-2.0)
|
| 13 |
+
└─ @videojs/http-streaming (VHS) bundled in the dist build
|
| 14 |
+
|
| 15 |
+
sha256 (the bytes video.html loads):
|
| 16 |
+
efac16dea9821877702781ba25db9691722541e320a594647af4f810b823ead1 video.min.js
|
| 17 |
+
69933a9ee9f1c6c0152919103522e86222124964e2195d28eb2d0c043c26f59c video-js.min.css
|
| 18 |
+
|
| 19 |
+
Reproduce:
|
| 20 |
+
npm pack video.js@8.23.7 # or: curl the registry tarball
|
| 21 |
+
tar -xzf video.js-8.23.7.tgz
|
| 22 |
+
cp package/dist/video.min.js package/dist/video-js.min.css package/LICENSE .
|
b/967267c207182bbddd00f3783746889ba53739c566e4c9bfeb76bf52bf9a611e
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#EXTM3U
|
| 2 |
+
#EXT-X-VERSION:6
|
| 3 |
+
#EXT-X-TARGETDURATION:2
|
| 4 |
+
#EXT-X-MEDIA-SEQUENCE:1
|
| 5 |
+
#EXT-X-MAP:URI="init-stream2.m4s"
|
| 6 |
+
#EXTINF:1.984000,
|
| 7 |
+
#EXT-X-PROGRAM-DATE-TIME:2026-06-07T22:09:21.000+0100
|
| 8 |
+
chunk-stream2-00001.m4s
|
| 9 |
+
#EXTINF:2.005333,
|
| 10 |
+
#EXT-X-PROGRAM-DATE-TIME:2026-06-07T22:09:22.984+0100
|
| 11 |
+
chunk-stream2-00002.m4s
|
| 12 |
+
#EXTINF:2.005333,
|
| 13 |
+
#EXT-X-PROGRAM-DATE-TIME:2026-06-07T22:09:24.989+0100
|
| 14 |
+
chunk-stream2-00003.m4s
|
| 15 |
+
#EXTINF:2.005333,
|
| 16 |
+
#EXT-X-PROGRAM-DATE-TIME:2026-06-07T22:09:26.995+0100
|
| 17 |
+
chunk-stream2-00004.m4s
|
| 18 |
+
#EXTINF:2.005333,
|
| 19 |
+
#EXT-X-PROGRAM-DATE-TIME:2026-06-07T22:09:29.000+0100
|
| 20 |
+
chunk-stream2-00005.m4s
|
| 21 |
+
#EXTINF:0.021333,
|
| 22 |
+
#EXT-X-PROGRAM-DATE-TIME:2026-06-07T22:09:31.005+0100
|
| 23 |
+
chunk-stream2-00006.m4s
|
| 24 |
+
#EXT-X-ENDLIST
|
b/9a9c309bfdb2c4792eb598870d3eba8cd22d6704d6240a0993fab28c34aa0e79
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" fill="none" stroke="currentColor" stroke-width="6" stroke-linejoin="round" stroke-linecap="round">
|
| 2 |
+
<rect x="20" y="36" width="88" height="60" rx="8"/><path d="M56 52 L82 66 L56 80 Z" fill="currentColor" stroke="none"/><path d="M20 50 H108"/>
|
| 3 |
+
</svg>
|
b/9ca150f76026a6841d2043a38f1a9ef512a53f6c7651f56723364011e322af08
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2a338a2bc796086be42f2b9c85a6efdd2198f5e88c92f5ce0332bf141b8f4edd
|
| 3 |
+
size 807453
|
b/a29511197cf5065147b26ac3796fe45da8c057f4c115ab977d3f35920ecb54df
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Native video content — content-addressed media for the Video holospace (Law L5).
|
| 2 |
+
|
| 3 |
+
Every file here is pinned by sha256 in video-manifest.json; video.html re-derives the
|
| 4 |
+
default source before playing it, and video-witness.mjs fails on any drift. This is the
|
| 5 |
+
"100% UOR content-addressable" default content: the bytes the holospace plays ARE their
|
| 6 |
+
own address. The bundled clip and the HLS ladder carry an AUDIO track (AAC stereo), so
|
| 7 |
+
"audio works for every video" is testable offline. For remote 4K / Low-Latency-HLS
|
| 8 |
+
streaming, open video.html?src=<url> — the adaptive ladder is then addressed by its HLS
|
| 9 |
+
playlist, not bundled here (the verified 4K and 1080p remote streams also carry audio).
|
| 10 |
+
|
| 11 |
+
big-buck-bunny-360p.mp4 (+ hls/ ladder transcoded from it)
|
| 12 |
+
Source : https://www.w3schools.com/html/mov_bbb.mp4 (the short Big Buck Bunny clip, with audio)
|
| 13 |
+
Origin : "Big Buck Bunny" © Blender Foundation — Creative Commons Attribution 3.0
|
| 14 |
+
(https://peach.blender.org/about/) — free to use with attribution.
|
| 15 |
+
Codec : H.264 High + AAC-LC stereo 48 kHz, MP4 (ISO BMFF) · 640×360 · ~10s · 807453 bytes
|
| 16 |
+
sha256 : 2a338a2bc796086be42f2b9c85a6efdd2198f5e88c92f5ce0332bf141b8f4edd
|
| 17 |
+
|
| 18 |
+
cmaf/ — ONE content-addressed CMAF (fragmented-MP4) segment set, described by BOTH an
|
| 19 |
+
HLS playlist (master.m3u8, #EXT-X-VERSION:7) AND an MPEG-DASH manifest
|
| 20 |
+
(stream.mpd). Two open standards, identical κ-segments: init-stream{0,1,2}.m4s +
|
| 21 |
+
chunk-stream{0,1,2}-*.m4s (reps 0/1 = 640×360 + 480×270 video, rep 2 = AAC audio).
|
| 22 |
+
Built from the MP4 above with ffmpeg's DASH muxer (-hls_playlist). Each segment is
|
| 23 |
+
independently content-addressed; served through holo:// every .m4s is a verified
|
| 24 |
+
κ-object reused by HLS and DASH alike (max-UOR dedup across protocols).
|
| 25 |
+
HLS master κ: sha256:e77a1243dfed… · DASH MPD κ: sha256:20683114ef22…
|
b/a4545a22f6cab447274c9e14c5119f3b238b88c831a811af3ac3f06bf0519d59
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#EXTM3U
|
| 2 |
+
#EXT-X-VERSION:7
|
| 3 |
+
#EXT-X-TARGETDURATION:10
|
| 4 |
+
#EXT-X-MEDIA-SEQUENCE:0
|
| 5 |
+
#EXT-X-PLAYLIST-TYPE:VOD
|
| 6 |
+
#EXT-X-MAP:URI="init.mp4"
|
| 7 |
+
#EXTINF:10.000000,
|
| 8 |
+
seg_000.m4s
|
| 9 |
+
#EXT-X-ENDLIST
|
b/b2df281d2eed192aeca5c87c9f2347edf5f9297341ae1f742a21d488f97c3dee
ADDED
|
Binary file (33.1 kB). View file
|
|
|
b/b69a617ad775cda67c2e7f9a9189218c079c6e621b7b3f9e3b5d07f2d3dc8cb1
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"init.mp4": "sha256:4e81e18e179dd6096f1605a15b69d87696aa4ecba0f4c22e029c204a622d9fd0",
|
| 3 |
+
"master.m3u8": "sha256:3519d4bf65e9ab8b12959a890a4ba80adb21640ffe303ad3674871cc0ed411d2",
|
| 4 |
+
"seg_000.m4s": "sha256:79c8b7a3d5860d016ebdf44ef2297aaa10b74b60723795fb6d5babbbe550e009",
|
| 5 |
+
"stream_0.m3u8": "sha256:bca7d8c35cdd543a30fb584852e4dbce7c9b17e2d96086d8afa12cb368dc499e"
|
| 6 |
+
}
|
b/b826e5ac4bfe8bfe3c92c2d6e64c45a1bf1aff9fa829342a2e60e1f942186f26
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:31178cbca16267ad92d135296941a9c4a8bf2d84dbac0a61185a4b145c793c7d
|
| 3 |
+
size 198083
|
b/b85f748153b9ca262b04d5839efe26babd283515e59d2f34e621dba5ab2d571d
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:77a8952a97e3453d9623dc69a337243d64fbb3bb7eb59594f8c508e65c245c75
|
| 3 |
+
size 4454871
|
b/b932a39c7f64f0ebbe544307060dd2a9836a4b1999b01903f38898d00fdee690
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:baf6c3e0f642d105d3252739b91dad8016c0deea0738d11c9a6982dbb5fa8258
|
| 3 |
+
size 249053
|
b/bb421b0cf44a37b1dfbea14fa58cabf69939a21483be3a828c4733e17c5bebb5
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fc5ea5880ab48d73e6ae0cdf17c80454345ee66af6654cdc4de161830092156d
|
| 3 |
+
size 310656
|
b/bea568400b40279f4470f3ec72a96b952772946e09a878d26867fe2ba5133019
ADDED
|
Binary file (1.38 kB). View file
|
|
|