You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

114 satır
3.3KB

  1. const Toggle = {
  2. view({attrs: {label, value}}) {
  3. const onclick = () => {
  4. StreamContainer[value] = !StreamContainer[value]
  5. requestStream()
  6. }
  7. const checked = StreamContainer[value]
  8. return m('label.styled',
  9. m('input[type=checkbox]', {checked, onclick}),
  10. label,
  11. )
  12. }
  13. }
  14. const VideoConfig = {
  15. get video() {
  16. return StreamContainer.videoOn
  17. && State.online.length < 10
  18. && params.get('v') !== '0'
  19. && {width: {ideal: 320}, facingMode: 'user', frameRate: 26}
  20. },
  21. get audio() {
  22. return StreamContainer.audioOn
  23. && params.get('a') !== '0'
  24. },
  25. view() {
  26. return [
  27. m(Toggle, {label: 'video', value: 'videoOn'}),
  28. m(Toggle, {label: 'audio', value: 'audioOn'}),
  29. ]
  30. }
  31. }
  32. const requestStream = async () => {
  33. if(StreamContainer.videoOn || StreamContainer.audioOn) {
  34. await navigator.mediaDevices
  35. .getUserMedia(VideoConfig)
  36. .then(stream =>
  37. signal({kind: 'stream', value: {source: State.username, stream}})
  38. )
  39. .catch(e => console.log(e))
  40. }
  41. else {
  42. const stream = new MediaStream()
  43. signal({kind: 'stream', value: {source: State.username, stream}})
  44. }
  45. }
  46. const Video = {
  47. setUp: (username) => ({dom}) => {
  48. if(username === State.username) {
  49. dom.muted = true
  50. requestStream()
  51. }
  52. },
  53. view({attrs: {username}}) {
  54. const styleOuter = {
  55. position: 'relative',
  56. display: 'block',
  57. color: 'white',
  58. overflow: 'hidden',
  59. }
  60. const styleMeta = {
  61. position: 'absolute',
  62. display: 'flex',
  63. alignItems: 'center',
  64. justifyContent: 'center',
  65. height: '100%',
  66. width: '100%',
  67. fontFamily: 'monospace',
  68. fontSize: 'xxx-large',
  69. }
  70. const styleVideo = {
  71. objectFit: Settings.get('blackBars') ? 'contain' : 'cover',
  72. width: '100%',
  73. height: '100%',
  74. transform: username === State.username ? 'scaleX(-1)' : 'scaleX(1)',
  75. }
  76. return m('.video-container', {style: styleOuter},
  77. m('.video-info', {style: styleMeta},
  78. m('.username', username),
  79. ),
  80. m('video[playsinline][autoplay]', {
  81. style: styleVideo,
  82. srcObject: streams[username],
  83. oncreate: this.setUp(username),
  84. }),
  85. )
  86. },
  87. }
  88. const StreamContainer = {
  89. videoOn: true,
  90. audioOn: true,
  91. view() {
  92. const dims = [
  93. Math.floor((1 + Math.sqrt(4 * State.online.length - 3)) / 2),
  94. Math.ceil(Math.sqrt(State.online.length)),
  95. ].map(n => Array(n || 1).fill('1fr').join(' '))
  96. if(innerHeight > innerWidth) dims.reverse()
  97. const style = {
  98. backgroundColor: 'black',
  99. overflow: 'hidden',
  100. display: 'grid',
  101. gridTemplateRows: dims[0],
  102. gridTemplateColumns: dims[1],
  103. }
  104. return m('.videos', {style},
  105. State.online.map(username => m(Video, {key: username, username}))
  106. )
  107. },
  108. }
  109. addEventListener('load', () => {
  110. Headers.push([VideoConfig])
  111. Apps.push([StreamContainer, {key: 'stream-container'}])
  112. })