浏览代码

Add hls/p2p video player

Daniel Supernault 1 年之前
父节点
当前提交
e3f8cfb49e
共有 1 个文件被更改,包括 198 次插入0 次删除
  1. 198 0
      resources/assets/components/presenter/VideoPlayer.vue

+ 198 - 0
resources/assets/components/presenter/VideoPlayer.vue

@@ -0,0 +1,198 @@
+<template>
+    <div>
+        <div v-if="status.sensitive == true" class="content-label-wrapper">
+            <div class="text-light content-label">
+                <p class="text-center">
+                    <i class="far fa-eye-slash fa-2x"></i>
+                </p>
+                <p class="h4 font-weight-bold text-center">
+                    Sensitive Content
+                </p>
+                <p class="text-center py-2 content-label-text">
+                    {{ status.spoiler_text ? status.spoiler_text : 'This post may contain sensitive content.'}}
+                </p>
+                <p class="mb-0">
+                    <button @click="status.sensitive = false" class="btn btn-outline-light btn-block btn-sm font-weight-bold">See Post</button>
+                </p>
+            </div>
+        </div>
+
+        <template v-else>
+            <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'}">
+                <div class="text-light content-label">
+                    <p class="mb-0">
+                        <button @click.prevent="handleShouldPlay" class="btn btn-link btn-block btn-sm font-weight-bold">
+                            <i class="fas fa-play fa-5x text-white"></i>
+                        </button>
+                    </p>
+                </div>
+            </div>
+
+            <template v-else>
+                <video v-if="hasHls" ref="video" :class="{ fixedHeight: fixedHeight }" style="margin:0" playsinline controls autoplay="false" :poster="getPoster(status)">
+                </video>
+
+                <video v-else class="card-img-top shadow" :class="{ fixedHeight: fixedHeight }" style="border-radius:15px;object-fit: contain;background-color: #000;" autoplay="false" controls :poster="getPoster(status)">
+                    <source :src="status.media_attachments[0].url" :type="status.media_attachments[0].mime">
+                </video>
+            </template>
+        </template>
+
+    </div>
+</template>
+
+<script type="text/javascript">
+    import Hls from 'hls.js';
+    import "plyr/dist/plyr.css";
+    import Plyr from 'plyr';
+    import { p2pml } from '@peertube/p2p-media-loader-core'
+    import { Engine, initHlsJsPlayer } from '@peertube/p2p-media-loader-hlsjs'
+
+    export default {
+        props: ['status', 'fixedHeight'],
+
+        data() {
+            return {
+                shouldPlay: false,
+                hasHls: undefined,
+                hlsConfig: window.App.config.features.hls,
+                liveSyncDurationCount: 7,
+                isHlsSupported: false,
+                isP2PSupported: false,
+                engine: undefined,
+            }
+        },
+
+        mounted() {
+            this.$nextTick(() => {
+                this.init();
+            })
+        },
+
+        methods: {
+            handleShouldPlay(){
+                this.shouldPlay = true;
+                this.isHlsSupported = this.hlsConfig.enabled && Hls.isSupported();
+                this.isP2PSupported = this.hlsConfig.enabled && this.hlsConfig.p2p && Engine.isSupported();
+                this.$nextTick(() => {
+                    this.init();
+                })
+            },
+
+            init() {
+                if(!this.status.sensitive && this.status.media_attachments[0]?.hls_manifest && this.isHlsSupported) {
+                    this.hasHls = true;
+                    this.$nextTick(() => {
+                        this.initHls();
+                    })
+                } else {
+                    this.hasHls = false;
+                }
+            },
+
+            initHls() {
+                let loader;
+                if(this.isP2PSupported) {
+                    const config = {
+                        loader: {
+                            trackerAnnounce: [this.hlsConfig.tracker],
+                            rtcConfig: {
+                                iceServers: [
+                                    {
+                                        urls: [this.hlsConfig.ice]
+                                    }
+                                ],
+                            }
+                        }
+                    };
+                    var engine = new Engine(config);
+                    if(this.hlsConfig.p2p_debug) {
+                        engine.on("peer_connect", peer => console.log("peer_connect", peer.id, peer.remoteAddress));
+                        engine.on("peer_close", peerId => console.log("peer_close", peerId));
+                        engine.on("segment_loaded", (segment, peerId) => console.log("segment_loaded from", peerId ? `peer ${peerId}` : "HTTP", segment.url));
+                    }
+                    loader = engine.createLoaderClass();
+                } else {
+                    loader = Hls.DefaultConfig.loader;
+                }
+                const video = this.$refs.video;
+                const source = this.status.media_attachments[0].hls_manifest;
+                const player = new Plyr(video, {
+                    captions: {
+                        active: true,
+                        update: true,
+                    },
+                });
+
+                const hls = new Hls({
+                    liveSyncDurationCount: this.liveSyncDurationCount,
+                    loader: loader,
+                });
+                let self = this;
+                initHlsJsPlayer(hls);
+                hls.loadSource(source);
+                hls.attachMedia(video);
+
+                hls.on(Hls.Events.MANIFEST_PARSED, function(event, data) {
+                    if(this.hlsConfig.debug) {
+                        console.log(event);
+                        console.log(data);
+                    }
+                    const defaultOptions = {};
+
+                    const availableQualities = hls.levels.map((l) => l.height)
+                    if(this.hlsConfig.debug) {
+                        console.log(availableQualities);
+                    }
+                    availableQualities.unshift(0);
+
+                    defaultOptions.quality = {
+                        default: 0,
+                        options: availableQualities,
+                        forced: true,
+                        onChange: (e) => self.updateQuality(e),
+                    }
+                    defaultOptions.i18n = {
+                        qualityLabel: {
+                            0: 'Auto',
+                        },
+                    }
+
+                    hls.on(Hls.Events.LEVEL_SWITCHED, function(event, data) {
+                        var span = document.querySelector(".plyr__menu__container [data-plyr='quality'][value='0'] span")
+                        if (hls.autoLevelEnabled) {
+                            span.innerHTML = `Auto (${hls.levels[data.level].height}p)`
+                        } else {
+                            span.innerHTML = `Auto`
+                        }
+                    })
+
+                    var player = new Plyr(video, defaultOptions);
+                });
+            },
+
+             updateQuality(newQuality) {
+                if (newQuality === 0) {
+                    window.hls.currentLevel = -1;
+                } else {
+                    window.hls.levels.forEach((level, levelIndex) => {
+                        if (level.height === newQuality) {
+                            if(this.hlsConfig.debug) {
+                                console.log("Found quality match with " + newQuality);
+                            }
+                            window.hls.currentLevel = levelIndex;
+                        }
+                    });
+                }
+            },
+
+            getPoster(status) {
+                let url = status.media_attachments[0].preview_url;
+                if(url.endsWith('no-preview.jpg') || url.endsWith('no-preview.png')) {
+                    return;
+                }
+                return url;
+            }
+        }
+    }
+</script>