muzuiget / dualsub-support

Dualsub - Dual Subtitles for YouTube
https://www.dualsub.xyz/
281 stars 24 forks source link

BBC iPlayer 全屏问题 #407

Closed muzuiget closed 2 years ago

muzuiget commented 2 years ago

在 Dualsub v1.80.2 之后,在 BBC iPlayer 通过网站播放器操作进入全屏(双击画面区域、点击全屏按钮),Dualsub 的字幕会消失,这里提供一些解决方法。

BBC iPlayer 有两个播放器界面,它的全屏实现有点特殊,甚至在 Android 下和桌面有不同行为,比较难以整合。

要全屏视频并保持 Dualsub 的字幕显示,只需要把 Dualsub 渲染层的父容器元素全屏即可,一行代码即可搞定。

document.querySelector('.dualsub-renderer')?.parentElement.requestFullscreen();

在 DevTools 运行即可看到效果。

当然在 DevTools 里运行是不方便的,所以要加一些 GUI 组件在页面上交互运行。

下面有几个方法,都是使用 Dualsub 设置页面中的“插件”功能,来运行自定义脚本。

方法 1:使用自定义全屏按钮

v1.80.2 后默认创建一个叫 全屏按钮 的脚本,如果从旧版本升级上来,需要重置扩展设置自动生成,或者从以下代码新建一个:

const button = document.createElement('div');
button.textContent = 'FS';
button.style.fontSize = '16px';
button.style.width = '32px';
button.style.height = '32px';
button.style.lineHeight = '32px';
button.style.textAlign = 'center';
button.style.background = '#000';
button.style.color = '#fff';
button.style.position = 'fixed';
button.style.top = '40%';
button.style.right = '5%';
button.style.border = '1px solid #fff';
button.style.borderRadius = '50%';
button.style.cursor = 'pointer';
document.body.append(button);

button.addEventListener('click', (event) => {
    const node = document.querySelector('.dualsub-renderer');
    node?.parentElement?.requestFullscreen();
});

这会在页面右边中间添加一个按钮

Screenshot_20220429_163811

通过这个按钮来全屏,退出全屏则按 ESC 键。

这个方法对所有网站都适用,只需要在勾选对应的网站单选框即可。

方法 2:使用快捷键

使用以下代码可以按 f 键来切换全屏。

document.addEventListener('keydown', (event) => {
    if (event.key === 'f') {
        if (document.fullscreenElement) {
            document.exitFullscreen();
        } else {
            const node = document.querySelector('.dualsub-renderer');
            node?.parentElement?.requestFullscreen();
        }
    }
});

需要光标焦点在页面空白处。

方法 3:替换播放器交互

替换网站的双击和全屏按钮行为,仅对这种播放器有效。

Screenshot_20220429_163832

代码比较长:

const querySelectors = (root, selectors) => {
    const result = [];
    for (const selector of selectors) {
        if (root === null) {
            break;
        }
        if (root.shadowRoot) {
            root = root.shadowRoot;
        }
        root = root.querySelector(selector);
        result.push(root);
    }
    return result;
};

const changeFullscreenIcon = (button, enter) => {
    const shadowRoot = button.shadowRoot;
    const icon1 = shadowRoot.querySelector('.enter_fullscreen_button');
    const icon2 = shadowRoot.querySelector('.exit_fullscreen_button');
    if (enter) {
        icon1.style.display = 'block';
        icon2.style.display = 'none';
    } else {
        icon1.style.display = 'none';
        icon2.style.display = 'block';
    }
};

const toggleCustomFullscreen = (button) => {
    if (document.fullscreen) {
        document.exitFullscreen();
        changeFullscreenIcon(button, true);
        return;
    }
    changeFullscreenIcon(button, false);
    const node = document.querySelector('.dualsub-renderer');
    if (node?.parentElement) {
        node.parentElement.requestFullscreen();
    }
};

const replaceFullscreen = (nodes) => {
    nodes[0].shadowRoot.addEventListener('dblclick', (event) => {
        const target = event.target;
        if (target.tagName === 'SMP-VIDEO-LAYOUT') {
            event.stopPropagation();
            event.preventDefault();
            toggleCustomFullscreen(nodes[3]);
        }
    }, true);

    const opacity = nodes[1].shadowRoot.querySelector('.opacity_layer');
    opacity.style.display = 'none';

    nodes[2].shadowRoot.addEventListener('click', (event) => {
        const target = event.target;
        if (target.tagName === 'SMP-FULLSCREEN-BUTTON') {
            event.stopPropagation();
            event.preventDefault();
            toggleCustomFullscreen(nodes[3]);
        }
    }, true);
};

(async function() {
    const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
    let nodes = [];
    while (nodes.length !== 4) {
        await sleep(3000);
        nodes = querySelectors(document, [
            'smp-toucan-player',
            'smp-video-layout',
            'smp-secondary-controls',
            'smp-fullscreen-button',
        ]);
    }
    replaceFullscreen(nodes);
})();

理论上方法 1 的代码通用性和兼容性最好,两种播放器,桌面和 Android 下都能使用。