1
0

VideoPlayer.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <template>
  2. <div>
  3. <div v-if="status.sensitive == true" class="content-label-wrapper">
  4. <div class="text-light content-label">
  5. <p class="text-center">
  6. <i class="far fa-eye-slash fa-2x"></i>
  7. </p>
  8. <p class="h4 font-weight-bold text-center">
  9. Sensitive Content
  10. </p>
  11. <p class="text-center py-2 content-label-text">
  12. {{ status.spoiler_text ? status.spoiler_text : 'This post may contain sensitive content.'}}
  13. </p>
  14. <p class="mb-0">
  15. <button @click="status.sensitive = false" class="btn btn-outline-light btn-block btn-sm font-weight-bold">See Post</button>
  16. </p>
  17. </div>
  18. </div>
  19. <template v-else>
  20. <div v-if="!shouldPlay" class="content-label-wrapper" :style="{ background: `linear-gradient(rgba(0, 0, 0, 0.2),rgba(0, 0, 0, 0.8)),url(${getPoster(status)})`, backgroundSize: 'cover'}">
  21. <div class="text-light content-label">
  22. <p class="mb-0">
  23. <button @click.prevent="handleShouldPlay" class="btn btn-link btn-block btn-sm font-weight-bold">
  24. <i class="fas fa-play fa-5x text-white"></i>
  25. </button>
  26. </p>
  27. </div>
  28. </div>
  29. <template v-else>
  30. <video v-if="hasHls" ref="video" :class="{ fixedHeight: fixedHeight }" style="margin:0" playsinline webkit-playsinline controls autoplay="false" :poster="getPoster(status)">
  31. </video>
  32. <video v-else class="card-img-top shadow" :class="{ fixedHeight: fixedHeight }" style="border-radius:15px;object-fit: contain;background-color: #000;" autoplay="false" playsinline webkit-playsinline controls :poster="getPoster(status)">
  33. <source :src="status.media_attachments[0].url" :type="status.media_attachments[0].mime">
  34. </video>
  35. </template>
  36. </template>
  37. </div>
  38. </template>
  39. <script type="text/javascript">
  40. import Hls from 'hls.js';
  41. import "plyr/dist/plyr.css";
  42. import Plyr from 'plyr';
  43. export default {
  44. props: ['status', 'fixedHeight'],
  45. data() {
  46. return {
  47. shouldPlay: false,
  48. hasHls: undefined,
  49. hlsConfig: window.App.config.features.hls,
  50. liveSyncDurationCount: 7,
  51. isHlsSupported: false,
  52. isP2PSupported: false,
  53. engine: undefined,
  54. }
  55. },
  56. mounted() {
  57. this.$nextTick(() => {
  58. this.init();
  59. })
  60. },
  61. methods: {
  62. handleShouldPlay(){
  63. this.shouldPlay = true;
  64. this.isHlsSupported = false;
  65. this.isP2PSupported = false;
  66. this.$nextTick(() => {
  67. this.init();
  68. })
  69. },
  70. init() {
  71. if(!this.status.sensitive && this.status.media_attachments[0]?.hls_manifest && this.isHlsSupported) {
  72. this.hasHls = true;
  73. this.$nextTick(() => {
  74. this.initHls();
  75. })
  76. } else {
  77. this.hasHls = false;
  78. }
  79. },
  80. initHls() {
  81. let loader;
  82. if(this.isP2PSupported) {
  83. const config = {
  84. loader: {
  85. trackerAnnounce: [this.hlsConfig.tracker],
  86. rtcConfig: {
  87. iceServers: [
  88. {
  89. urls: [this.hlsConfig.ice]
  90. }
  91. ],
  92. }
  93. }
  94. };
  95. var engine = new Engine(config);
  96. if(this.hlsConfig.p2p_debug) {
  97. engine.on("peer_connect", peer => console.log("peer_connect", peer.id, peer.remoteAddress));
  98. engine.on("peer_close", peerId => console.log("peer_close", peerId));
  99. engine.on("segment_loaded", (segment, peerId) => console.log("segment_loaded from", peerId ? `peer ${peerId}` : "HTTP", segment.url));
  100. }
  101. loader = engine.createLoaderClass();
  102. } else {
  103. loader = Hls.DefaultConfig.loader;
  104. }
  105. const video = this.$refs.video;
  106. const source = this.status.media_attachments[0].hls_manifest;
  107. const player = new Plyr(video, {
  108. captions: {
  109. active: true,
  110. update: true,
  111. },
  112. });
  113. const hls = new Hls({
  114. liveSyncDurationCount: this.liveSyncDurationCount,
  115. loader: loader,
  116. });
  117. let self = this;
  118. initHlsJsPlayer(hls);
  119. hls.loadSource(source);
  120. hls.attachMedia(video);
  121. hls.on(Hls.Events.MANIFEST_PARSED, function(event, data) {
  122. if(this.hlsConfig.debug) {
  123. console.log(event);
  124. console.log(data);
  125. }
  126. const defaultOptions = {};
  127. const availableQualities = hls.levels.map((l) => l.height)
  128. if(this.hlsConfig.debug) {
  129. console.log(availableQualities);
  130. }
  131. availableQualities.unshift(0);
  132. defaultOptions.quality = {
  133. default: 0,
  134. options: availableQualities,
  135. forced: true,
  136. onChange: (e) => self.updateQuality(e),
  137. }
  138. defaultOptions.i18n = {
  139. qualityLabel: {
  140. 0: 'Auto',
  141. },
  142. }
  143. hls.on(Hls.Events.LEVEL_SWITCHED, function(event, data) {
  144. var span = document.querySelector(".plyr__menu__container [data-plyr='quality'][value='0'] span")
  145. if (hls.autoLevelEnabled) {
  146. span.innerHTML = `Auto (${hls.levels[data.level].height}p)`
  147. } else {
  148. span.innerHTML = `Auto`
  149. }
  150. })
  151. var player = new Plyr(video, defaultOptions);
  152. });
  153. },
  154. updateQuality(newQuality) {
  155. if (newQuality === 0) {
  156. window.hls.currentLevel = -1;
  157. } else {
  158. window.hls.levels.forEach((level, levelIndex) => {
  159. if (level.height === newQuality) {
  160. if(this.hlsConfig.debug) {
  161. console.log("Found quality match with " + newQuality);
  162. }
  163. window.hls.currentLevel = levelIndex;
  164. }
  165. });
  166. }
  167. },
  168. getPoster(status) {
  169. let url = status.media_attachments[0].preview_url;
  170. if(url.endsWith('no-preview.jpg') || url.endsWith('no-preview.png')) {
  171. return;
  172. }
  173. return url;
  174. }
  175. }
  176. }
  177. </script>