Bilibili Auto Pause

在使用 Enhancer for YouTube 插件时有一个功能 “自动暂停在后台标签中打开的视频” 特别好用,在国内的环境下,多少还是要用一下 BilBili 的,所以想着自己来实现这个功能

对还不懂写代码的我来说,去单独写一个插件难度太大了,所以就选择去叫 GPT 写一个油猴脚本吧。

就得到了

0.5.2版本的代码不足之处:非焦点窗口加载视频时会让焦点窗口的视频停止。

叫GPT改了之后(v0.6)的新BUG:同一窗口的视频又不会自动暂停了😭。

怎么改都不对,就放弃折腾,v0.5.2已经够我用

v0.5.2的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// ==UserScript==
// @name Bilibili Auto Pause
// @version 0.5.2
// @description 当 B 站在前台播放新视频时,自动暂停其他后台标签页的视频,适用于同一窗口和不同窗口的多标签页
// @match https://*.bilibili.com/*
// ==/UserScript==
(function() {
'use strict';

let video = null;
let channel = null;
const CHANNEL_NAME = 'bilibili-video-control';

// 唯一标识每个标签页
const TAB_ID = Date.now() + Math.random();

// 初始化广播通道和事件监听
function init() {
video = document.querySelector('video');
if (!video) return;

channel = new BroadcastChannel(CHANNEL_NAME);
video.addEventListener('play', onPlay);
channel.addEventListener('message', onMessage);
// 移除visibilitychange事件监听
// document.addEventListener('visibilitychange', onVisibilityChange);
}

// 视频播放事件处理
function onPlay() {
if (document.hidden) {
video.pause();
return;
}

if (channel) {
channel.postMessage({ action: 'pauseOthers', sender: TAB_ID });
}
}

// 接收到消息时的处理
function onMessage(event) {
const data = event.data;
if (!data || data.action !== 'pauseOthers') return;

if (data.sender !== TAB_ID && video && !video.paused) {
video.pause();
}
}

// 观察器回调函数,检测视频元素的添加
function mutationCallback(mutationsList) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
const newVideo = document.querySelector('video');
if (newVideo && newVideo !== video) {
if (video) {
video.removeEventListener('play', onPlay);
}
video = newVideo;
video.addEventListener('play', onPlay);
}
}
}
}

// 设置MutationObserver以监控视频元素的变化
const observer = new MutationObserver(mutationCallback);
observer.observe(document.body, { childList: true, subtree: true });

// 初始化脚本
init();

// 清理资源
window.addEventListener('beforeunload', () => {
if (channel) {
channel.close();
}
if (video) {
video.removeEventListener('play', onPlay);
}
// 移除visibilitychange事件监听
// document.removeEventListener('visibilitychange', onVisibilityChange);
observer.disconnect();
});
})();

另附上我的v0.6.1的代码,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// ==UserScript==
// @name Bilibili Auto Pause
// @version 0.6.1
// @description 当 B 站在前台播放新视频时,自动暂停其他后台标签页视频,适用于同一窗口和不同窗口的多标签页
// @match https://*.bilibili.com/*
// ==/UserScript==

(function() {
'use strict';

let video = null;
let channel = null;
const CHANNEL_NAME = 'bilibili-video-control';
const TAB_ID = Date.now() + Math.random();

// 初始化广播通道和事件监听
function init() {
video = document.querySelector('video');
if (!video) {
return;
}

// 创建广播通道
channel = new BroadcastChannel(CHANNEL_NAME);
video.addEventListener('play', onPlay);
channel.addEventListener('message', onMessage);
document.addEventListener('visibilitychange', onVisibilityChange);
}

// 视频播放事件处理
function onPlay() {
// 检查当前标签页是否可见且具有焦点
if (document.visibilityState !== 'visible' || !document.hasFocus()) {
// 如果标签页不可见或失去焦点,暂停视频
video.pause();
return;
}

if (channel) {
// 发送消息通知其他标签页暂停视频
channel.postMessage({ action: 'pauseOthers', sender: TAB_ID });
}
}

// 接收到消息时的处理
function onMessage(event) {
const data = event.data;
if (!data || !data.action) return;

if (data.action === 'pauseOthers') {
handlePauseOthers(data.sender);
}
}

// 处理暂停其他标签页的视频
function handlePauseOthers(senderId) {
// 如果消息来自自身,不执行任何操作
if (senderId === TAB_ID) return;

// 仅当当前标签页可见且视频正在播放时,暂停视频
if (document.visibilityState === 'visible' && video && !video.paused) {
video.pause();
}
}

// 处理页面可见性变化
function onVisibilityChange() {
if (document.visibilityState === 'visible' && video && !video.paused) {
if (channel) {
channel.postMessage({ action: 'pauseOthers', sender: TAB_ID });
}
}
}

// 观察器回调函数,检测视频元素的添加
function mutationCallback(mutationsList) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
const newVideo = document.querySelector('video');
if (newVideo && newVideo !== video) {
if (video) {
video.removeEventListener('play', onPlay);
}
video = newVideo;
video.addEventListener('play', onPlay);
}
}
}
}

const observer = new MutationObserver(mutationCallback);
observer.observe(document.body, { childList: true, subtree: true });

init();

// 清理资源
window.addEventListener('beforeunload', () => {
if (channel) {
channel.close();
}
if (video) {
video.removeEventListener('play', onPlay);
}
document.removeEventListener('visibilitychange', onVisibilityChange);
observer.disconnect();
});
})();