Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add feature: danmaku for skip #46

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions public/_locales/en/en.messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,30 @@
"autoSkipOnMusicVideos": {
"message": "Auto skip all segments when there is a non-music segment"
},
"danmakuSkip": {
"message": "Match parachute time from the danmaku"
},
"autoSkipDanmakuSkip": {
"message": "Automatically skip after matching successfully"
},
"menuDanmakuSkip": {
"message": "Open the submission window after matching successfully"
},
"checkTimeDanmakuSkip": {
"message": "Check if there is already uploaded skip info near the segment, if so, this feature will not be triggered"
},
"danmakuRegexPattern": {
"message": "Match Regex: "
},
"danmakuRegexPatternPlaceholder": {
"message": "Regex requires capturing 2 to 3 sets of numbers"
},
"danmakuRegexTitle": {
"message": "Please enter a regex that matches the time format, which should capture 2 to 3 sets of numbers. If you're not sure how to write a regex, do not modify this option"
},
"danmakuRegexPatternDescription": {
"message": "Enable the skip function based on danmaku, currently in experimental status"
},
"muteSegments": {
"message": "Allow segments that mute audio instead of skip"
},
Expand Down
24 changes: 24 additions & 0 deletions public/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,30 @@
"autoSkipOnMusicVideos": {
"message": "自动跳过所有音乐视频中的非音乐片段"
},
"danmakuSkip": {
"message": "从弹幕中匹配空降时间"
},
"autoSkipDanmakuSkip": {
"message": "匹配成功后自动跳过"
},
"menuDanmakuSkip": {
"message": "匹配成功后打开提交窗口"
},
"checkTimeDanmakuSkip": {
"message": "检查片段附近是否已有上传的跳过信息,已有则不触发此功能"
},
"danmakuRegexPattern": {
"message": "匹配正则: "
},
"danmakuRegexPatternPlaceholder": {
"message": "正则要求能捕获2到3组数字"
},
"danmakuRegexTitle": {
"message": "请输入一个匹配时间格式的正则表达式,要求能捕获2到3组数字。如果不清楚如何写正则表达式,请勿修改此项"
},
"danmakuRegexPatternDescription": {
"message": "启用基于弹幕的跳过功能,目前处于实验状态"
},
"muteSegments": {
"message": "允许片段静音而不是跳过"
},
Expand Down
24 changes: 24 additions & 0 deletions public/_locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,30 @@
"autoSkipOnMusicVideos": {
"message": "自动跳过所有音乐视频中的非音乐片段"
},
"danmakuSkip": {
"message": "从弹幕中匹配空降时间"
},
"autoSkipDanmakuSkip": {
"message": "匹配成功后自动跳过"
},
"menuDanmakuSkip": {
"message": "匹配成功后打开提交窗口"
},
"checkTimeDanmakuSkip": {
"message": "检查片段附近是否已有上传的跳过信息,已有则不触发此功能"
},
"danmakuRegexPattern": {
"message": "匹配正则: "
},
"danmakuRegexPatternPlaceholder": {
"message": "正则要求能捕获2到3组数字"
},
"danmakuRegexTitle": {
"message": "请输入一个匹配时间格式的正则表达式,要求能捕获2到3组数字。如果不清楚如何写正则表达式,请勿修改此项"
},
"danmakuRegexPatternDescription": {
"message": "启用基于弹幕的跳过功能,目前处于实验状态"
},
"muteSegments": {
"message": "允许片段静音而不是跳过"
},
Expand Down
24 changes: 24 additions & 0 deletions public/_locales/zh_TW/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,30 @@
"autoSkipOnMusicVideos": {
"message": "自動跳過所有音樂影片中的非音樂片段"
},
"danmakuSkip": {
"message": "從彈幕中匹配空降時間"
},
"autoSkipDanmakuSkip": {
"message": "匹配成功後自動跳過"
},
"menuDanmakuSkip": {
"message": "匹配成功後打開提交窗口"
},
"checkTimeDanmakuSkip": {
"message": "檢查片段附近是否已有上傳的跳過信息,已有則不觸發此功能"
},
"danmakuRegexPattern": {
"message": "匹配正則: "
},
"danmakuRegexPatternPlaceholder": {
"message": "正則要求能捕獲2到3組數字"
},
"danmakuRegexTitle": {
"message": "請輸入一個匹配時間格式的正則表達式,要求能捕獲2到3組數字。如果不清楚如何寫正則表達式,請勿修改此項"
},
"danmakuRegexPatternDescription": {
"message": "啟用基於彈幕的跳過功能,目前處於實驗狀態"
},
"muteSegments": {
"message": "允許片段靜音而不是跳過"
},
Expand Down
48 changes: 48 additions & 0 deletions public/options/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,54 @@
<div id="behavior" class="option-group hidden">

<div id="category-type" data-type="react-CategoryChooserComponent"></div>

<div>
<div class="switch-container" data-type="toggle" data-sync="enableDanmakuSkip">
<label class="switch">
<input id="enableDanmakuSkip" type="checkbox" checked>
<span class="slider round"></span>
</label>
<label class="switch-label" for="enableDanmakuSkip">
__MSG_danmakuSkip__
</label>
</div>
<div class="switch-container" data-type="toggle" data-sync="enableAutoSkipDanmakuSkip">
<label class="switch">
<input id="enableAutoSkipDanmakuSkip" type="checkbox" checked>
<span class="slider round"></span>
</label>
<label class="switch-label" for="enableAutoSkipDanmakuSkip">
__MSG_autoSkipDanmakuSkip__
</label>
</div>
<div class="switch-container" data-type="toggle" data-sync="enableMenuDanmakuSkip">
<label class="switch">
<input id="enableMenuDanmakuSkip" type="checkbox" checked>
<span class="slider round"></span>
</label>
<label class="switch-label" for="enableMenuDanmakuSkip">
__MSG_menuDanmakuSkip__
</label>
</div>
<div class="switch-container" data-type="toggle" data-sync="checkTimeDanmakuSkip">
<label class="switch">
<input id="checkTimeDanmakuSkip" type="checkbox" checked>
<span class="slider round"></span>
</label>
<label class="switch-label" for="checkTimeDanmakuSkip">
__MSG_checkTimeDanmakuSkip__
</label>
</div>
<div class="input-container" data-type="text-change" data-sync="danmakuRegexPattern">
<label for="danmakuRegexPattern">__MSG_danmakuRegexPattern__</label>
<input id="danmakuRegexPattern" class="option-text-box"
type="text" placeholder="__MSG_danmakuRegexPatternPlaceholder__"
title="__MSG_danmakuRegexTitle__">
<button class="text-change-set">__MSG_save__</button>
<button class="text-change-reset">__MSG_reset__</button>
</div>
<div class="small-description">__MSG_danmakuRegexPatternDescription__</div>
</div>

<div data-type="toggle" data-sync="muteSegments">
<div class="switch-container">
Expand Down
10 changes: 10 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ interface SBConfig {
submissionCountSinceCategories: number; // New count used to show the "Read The Guidelines!!" message
showTimeWithSkips: boolean;
disableSkipping: boolean;
enableDanmakuSkip: boolean;
enableAutoSkipDanmakuSkip: boolean;
enableMenuDanmakuSkip: boolean;
danmakuRegexPattern: string;
checkTimeDanmakuSkip: boolean;
muteSegments: boolean;
fullVideoSegments: boolean;
fullVideoLabelsOnThumbnails: boolean;
Expand Down Expand Up @@ -197,6 +202,11 @@ const syncDefaults = {
submissionCountSinceCategories: 0,
showTimeWithSkips: true,
disableSkipping: false,
enableDanmakuSkip: false,
enableAutoSkipDanmakuSkip: false,
enableMenuDanmakuSkip: false,
danmakuRegexPattern: "(?:空降\\s*)?(\\d{1,2}):(\\d{1,2})(?::(\\d{1,2}))?",
checkTimeDanmakuSkip: true,
muteSegments: true,
fullVideoSegments: true,
fullVideoLabelsOnThumbnails: true,
Expand Down
106 changes: 106 additions & 0 deletions src/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,101 @@
}
}

function checkDanmaku(text: string, offset: number) {
const regex = new RegExp(Config.config.danmakuRegexPattern)
const match = regex.exec(text);

if (match) {
let timeComponents = match.slice(1).filter(Boolean).map((value) => parseInt(value, 10));

Check failure on line 770 in src/content.ts

View workflow job for this annotation

GitHub Actions / Create artifacts

'timeComponents' is never reassigned. Use 'const' instead
let hours = 0, minutes = 0, seconds = 0;

if (timeComponents.length === 2) {
minutes = timeComponents[0];
seconds = timeComponents[1];
} else if (timeComponents.length === 3) {
hours = timeComponents[0];
minutes = timeComponents[1];
seconds = timeComponents[2];
}

const time = hours * 3600 + minutes * 60 + seconds;
const currentTime = getVirtualTime() + offset;

if (Config.config.checkTimeDanmakuSkip && isSegmentMarkedNearCurrentTime(currentTime))
return;

const skippingSegments: SponsorTime[] =
[{ actionType: ActionType.Skip, segment: [currentTime, time],
source: SponsorSourceType.Local, UUID: generateUserID() as SegmentUUID,
category: "sponsor" as Category}];
setTimeout(() => {
skipToTime({
v: getVideo(),
skipTime: [currentTime, time],
skippingSegments,
openNotice: true,
forceAutoSkip: Config.config.enableAutoSkipDanmakuSkip,
unskipTime: currentTime
});
if (Config.config.enableMenuDanmakuSkip) {
setTimeout(() => {
if (!sponsorTimesSubmitting?.some((s) => s.segment[1] === skippingSegments[0].segment[1])) {
sponsorTimesSubmitting.push(skippingSegments[0]);
}
openSubmissionMenu();
}, Config.config.skipNoticeDuration * 1000 + 500);
}
}, offset * 1000 - 100);
}
}

let observer: MutationObserver = null;
const processedDanmaku = new Set<string>();

function danmakuForSkip(clean: boolean = false) {
if (observer)
return;
if (clean) {
observer.disconnect();
observer = null;
processedDanmaku.clear();
return;
}
const targetNode = document.querySelector('.bpx-player-row-dm-wrap'); // 选择父节点
const config = { attributes: true, subtree: true }; // 观察属性变化
const callback = (mutationsList: MutationRecord[]) => {
if (!Config.config.enableDanmakuSkip || Config.config.disableSkipping)
return;
if (targetNode.classList.contains('bili-danmaku-x-paused'))
return;
for (const mutation of mutationsList) {
const target = mutation.target as HTMLElement;
if (mutation.type === 'attributes' && target.classList.contains('bili-danmaku-x-dm')) {
const content = mutation.target.textContent;
if (target.classList.contains('bili-danmaku-x-show')) {
if (!content || processedDanmaku.has(content)) {
continue;
}
processedDanmaku.add(content);
const offset = target.classList.contains("bili-danmaku-x-center") ? 0.3 : 1;
checkDanmaku(content, offset);
} else {
if (!content || processedDanmaku.has(content)) {
processedDanmaku.delete(content);
}
}
}
}
};
observer = new MutationObserver(callback);
observer.observe(targetNode, config);
addCleanupListener(() => {
if (observer) {
danmakuForSkip(true);
}
});
}

/**
* Used on Firefox only, waits for the next animation frame until
* the video time has changed
Expand Down Expand Up @@ -802,6 +897,16 @@
return sponsorTimes?.some(checkFunction) || sponsorTimesSubmitting.some(checkFunction);
}

function isSegmentMarkedNearCurrentTime(currentTime: number, range: number = 5): boolean {
const lowerBound = currentTime - range;
const upperBound = currentTime + range;

return sponsorTimes?.some(sponsorTime => {
const { segment: [startTime, endTime] } = sponsorTime;
return startTime <= upperBound && endTime >= lowerBound;
});
}

/**
* This makes sure the videoID is still correct and if the sponsorTime is included
*/
Expand Down Expand Up @@ -858,6 +963,7 @@
}

if (!Config.config.disableSkipping) {
danmakuForSkip();
switchingVideos = false;

let startedWaiting = false;
Expand Down
Loading