想在文章中插入视频,尝试 iframe 和 video 标签后发现 m3u8 流会触发下载无法播放,用 hls 该问题后,碰到了 403 forbbiden。联想到前些天新浪图床加 referrer 的解决方法,随手一试竟然可以。后来发现 Mixed Content 和 CORS 才是大坑,几经了解,这个问题只能靠视频源服务器解决,所以把视频放在自己的服务器或 CDN 是不会有问题的。最后,用 js 封装了一下神器 DPlayer,使用体验良好。

演示效果

{dplayer src="https://youku.cdn2-youku.com/20180410/9985_3bdd7f1e/index.m3u8"/}

引入 JS

对于 本主题,依次进入 控制台 - 外观 - 设置外观 - 主题自定义扩展,将代码加入到 自定义 HTML 元素拓展 - 标签: head 头部 (meta 元素后)。其他主题,加入到主题对应的 header.php 中的 标签前。

    <script>
        // DPlayer API
        document.addEventListener('DOMContentLoaded', initDplayer);
        function initDplayer() {
            const common = {
                loadResource: function (id, resource, type, callback) {
                    let loaded = document.head.querySelector('#' + id);
                    if (loaded) {
                        callback();
                        return;
                    }
                    const element = document.createElement(type);
                    element.onload = element.onreadystatechange = () => {
                        if (!loaded && (!element.readyState || /loaded|complete/.test(element.readyState))) {
                            element.onload = element.onreadystatechange = null;
                            loaded = true;
                            callback();
                        }
                    }
                    if (type === 'link') {
                        element.rel = 'stylesheet';
                        element.href = resource;
                    } else {
                        element.src = resource;
                    }
                    element.id = id;
                    document.getElementsByTagName('head')[0].appendChild(element);
                },
                loadResources: function (callback) {
                    const cdn = '//s0.pstatp.com/cdn/expire-1-M';
                    const resources = [
                        '/dplayer/1.25.0/DPlayer.min.css',
                        '/dplayer/1.25.0/DPlayer.min.js',
                        '/hls.js/0.12.4/hls.light.min.js',
                        '/flv.js/1.5.0/flv.min.js'
                    ];
                    let unloadedResourceCount = resources.length;
                    resources.forEach(resource => {
                        this.loadResource(btoa(resource).replace(/[=+\/]/g, ''), cdn + resource,
                            ({
                                'css': 'link',
                                'js': 'script'
                            })[resource.split('.').pop()],
                            () => --unloadedResourceCount ? null : callback()
                        );
                    });
                },
                createDplayers: function (sources, callback) {
                    for (let i = 0; i < sources.length; i++) {
                        const child = document.createElement('div');
                        const src = sources[i].getAttribute('src');
                        sources[i].parentNode.insertBefore(child, sources[i]);
                        sources[i].style.display = 'none';
                        const type = src.split('.').pop();
                        const option = { url: src };
                        type === 'flv' ? option.type = type : null;
                        const dplayer = new DPlayer({ container: child, preload: 'none', autoplay: false, screenshot: false, video: option });
                    }
                    if (typeof callback === 'function') callback();
                }
            };
            const mirages = {
                isMirages: function () { return Mirages || false },
                fixVideoSize: function (length) {
                    let outerTimer = false;
                    const outerInterval = setInterval(() => {
                        if (outerTimer) return;
                        const videos = document.getElementsByTagName('video');
                        if (videos.length === length) {
                            const dplayerWraps = document.querySelectorAll('.dplayer-video-wrap');
                            for (let i = 0; i < length; i++) {
                                const videoContainers = dplayerWraps[i].querySelectorAll('.video-container.video-4-3');
                                if (videoContainers.length) {
                                    videoContainers[0].style = 'position: initial;';
                                    videoContainers[0].className = 'video-container video-16-9';
                                    console.log('video-4-3 fixed.');
                                } else {
                                    const videoContainer = document.createElement('div');
                                    videoContainer.style = 'position: initial;';
                                    videoContainer.className = 'video-container video-16-9';
                                    videoContainer.appendChild(videos[i]);
                                    dplayerWraps[i].appendChild(videoContainer);
                                    console.log('video-16-9 inserted.');
                                    const targetNode = videoContainer;
                                    const config = { childList: true };
                                    const callback = (mutationsList, observer) => {
                                        const newVideoContainers = videoContainer.querySelectorAll(
                                            '.video-container.video-4-3');
                                        if (newVideoContainers.length) {
                                            newVideoContainers[0].className = '';
                                            console.log('auto inserted video-4-3 fixed.');
                                            observer.disconnect();
                                        }
                                    };
                                    const observer = new MutationObserver(callback);
                                    observer.observe(targetNode, config);
                                    setTimeout(() => observer.disconnect(), 1000 * 120);
                                }
                            }
                            outerTimer = true;
                            clearInterval(outerInterval);
                        }
                    }, 500);
                }
            };
            const dps = document.getElementsByTagName('dp');
            if (dps.length !== 0) {
                common.loadResources(() => common.createDplayers(dps, () => {
                    // 修正 Mirages 视频比例错误
                    mirages.isMirages() ? mirages.fixVideoSize(dps.length) : null;
                }));
            }
        };
    </script>

如果你开启了 PJAX,可能需要单独加入回调函数。对于本主题,依次进入 控制台 - 外观 - 设置外观 - PJAX(BETA) - PJAX RELOAD,将 initDplayer(); 添加进入即可。

添加播放器

在文章所需位置以 html 形式插入代码,即可添加播放器,支持 m3u8、mp4,flv 和 mkv 格式,不过编码必须是 H.264 AAC。

分类: Typecho 标签: Typecho
公众号图片

评论

暂无评论数据

暂无评论数据

目录