Humuhumu33 commited on
Commit
6ff545d
·
verified ·
1 Parent(s): d654229

Evict apps/video → κ-store (M3 E0 pilot): 64 blake3-addressed objects

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +25 -0
  2. b/022a97533ed03d64fab9788eedfd3051f5d4ae1e12a61e3347b5df8cdcb5f97f +3 -0
  3. b/0320a1879f663fad96719cede89e482a44e515a17c8504f8de0cb21071e716e2 +3 -0
  4. b/0ab773261c3d252351f1be8aea9e182111853ef6a5e9b45c8ac91f2e91a6bac8 +3 -0
  5. b/12823c8a1a3c9e0084c34565bb5cd88938e4311c1f57f03236a2f9b6da24de05 +3 -0
  6. b/15bb754526268bf9e2149a23c6e39e99fc811a31a2119f68569ac1f14c5bb954 +1 -0
  7. b/172b2e7bdc5d7b3c9be55b46a36744187755f1671e0382e75d3ba5da8baa1e45 +0 -0
  8. b/1a9a0e5d2a4b2deefd9c02e2c03534c665aa3268751faa0b0d0f65d5db46ba97 +3 -0
  9. b/1cbf6a00a9ab9358f4ccb53cc434ec3d8c180abbd4a94e89c7447770ee007fbf +23 -0
  10. b/1e75d59c8b442c3b550d09de7425df69f7d73775812de981af07a4efe35e0def +40 -0
  11. b/1ff06debd7f34106c40c7920612f382759d6080adac51e98f2c0e1c3a80e85b3 +785 -0
  12. b/26cd85a5de850229aae046e87490e4a32c7bfae6c01fa83ca0a6593241f5e54f +0 -0
  13. b/29fcd622a2dfc0cccfece8a717e5f8f09061dd0497bd0921717ffd5c104cf71d +3 -0
  14. b/2e485cef50d0c92a82dc47e998dc692fb064bb04080a3ce42f9436a85866c7c5 +3 -0
  15. b/3237bdea9058724a3dedc441e487a3c477489a1b71df668ff6d9ad9e838a3f1d +0 -0
  16. b/3444f1c9211901b5676d5a44777fab07ff67fed01deda3c5aaa3c1717442af39 +83 -0
  17. b/374cc5daa86b2dddbceb83552d561d4856eb60ef631b30916dc92b57275f9eca +8 -0
  18. b/375ca224bf188af103815a995dd7ce74e7ba74a5c67f473a03600797db9af450 +3 -0
  19. b/3a0c90e6fe3baa82e5bf20681a902182c69cbb56d850fce5fa86eb86e863afe8 +3 -0
  20. b/3e8be10158d7e4a20f03d1bc7b59a04f97aab20ccbc37b3b599abec0d3681b9f +3 -0
  21. b/3fd9ff18cd3872dc3f5596a9aa9a02b094ce2b357d2de893ad70dfea23006f24 +5 -0
  22. b/477220d636a4e6c65d61affa15c10a81de2dcf6df73b8aafb719db547e9074fc +8 -0
  23. b/4c9124649152e87d2b41d053579997189860bfffccc8adfc14e86aaccb2471af +3 -0
  24. b/5ca266f01ea019c2f4d8a4f146f91a262b05625807304653f92dbb97f261d95a +0 -0
  25. b/5d755801f6ad62cf150df6c035e45fb89144bbb163c9a58687a78425f8f609d5 +0 -0
  26. b/61f5de63f35414694a789bcf05c03382b15ca209e36334ff4e83b4c466c306c0 +6 -0
  27. b/61fd98fa00653be79b0948475914d2522be9f83eafe4858180b2bd2e2b5668c4 +9 -0
  28. b/6224c8e805b0f7e2d478edcbd216c01af74150bde7e433f05c8016a6b4dd03f9 +3 -0
  29. b/6902f4b785750559a8993a4e55fcdf9c80a44c0d3049b10c1960450d37759a7f +5 -0
  30. b/792fd7431ff360863e34a67d4190902e01022792eefe63ac2a35c965f5d97af0 +15 -0
  31. b/7930427c195c0a738a3d19652b9eeab2012637f8f5e678a41163a89c3b1b78b8 +3 -0
  32. b/7aa800d6da2f38a86d41f9de4eab32d9418eb453d1ce59d56d8a8c151ec54d23 +3 -0
  33. b/7e6baad8d384f52a584e2432e8a12a78c92afbaa9be9ae9731d6a076a4a61ffc +47 -0
  34. b/84acaa85618686868e6b28091c81674894a194d27123b91ab0d4e86f2fd1b973 +0 -0
  35. b/85ac280da66d2cba7a4ad1487e0b391b40a702b45e63bfae884859d0c50d390f +0 -0
  36. b/8bda1d7bee91aac65e1a1db701ea902fba0116e30175c88e245740a9e9a7a33a +3 -0
  37. b/9149515d6ce8bc6ef4d01509ae02d4cc072c0e04a98e9b411a1a807e5286f2f6 +5 -0
  38. b/931fc03dc173d2b04f5f3e450414ff58e8d15064ca5e4abc0ed5f092fd6b3688 +22 -0
  39. b/967267c207182bbddd00f3783746889ba53739c566e4c9bfeb76bf52bf9a611e +24 -0
  40. b/9a9c309bfdb2c4792eb598870d3eba8cd22d6704d6240a0993fab28c34aa0e79 +3 -0
  41. b/9ca150f76026a6841d2043a38f1a9ef512a53f6c7651f56723364011e322af08 +3 -0
  42. b/a29511197cf5065147b26ac3796fe45da8c057f4c115ab977d3f35920ecb54df +25 -0
  43. b/a4545a22f6cab447274c9e14c5119f3b238b88c831a811af3ac3f06bf0519d59 +9 -0
  44. b/b2df281d2eed192aeca5c87c9f2347edf5f9297341ae1f742a21d488f97c3dee +0 -0
  45. b/b69a617ad775cda67c2e7f9a9189218c079c6e621b7b3f9e3b5d07f2d3dc8cb1 +6 -0
  46. b/b826e5ac4bfe8bfe3c92c2d6e64c45a1bf1aff9fa829342a2e60e1f942186f26 +3 -0
  47. b/b85f748153b9ca262b04d5839efe26babd283515e59d2f34e621dba5ab2d571d +3 -0
  48. b/b932a39c7f64f0ebbe544307060dd2a9836a4b1999b01903f38898d00fdee690 +3 -0
  49. b/bb421b0cf44a37b1dfbea14fa58cabf69939a21483be3a828c4733e17c5bebb5 +3 -0
  50. 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