Skip to content

Commit

Permalink
Merge pull request #46 from lanytcc/feature-danmakuForSkip
Browse files Browse the repository at this point in the history
add feature: danmaku for skip
  • Loading branch information
hanydd authored Oct 21, 2024
2 parents 0797308 + 04db002 commit f2de4dc
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 0 deletions.
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 @@ async function startSponsorSchedule(
}
}

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

if (match) {
const timeComponents = match.slice(1).filter(Boolean).map((value) => parseInt(value, 10));
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 @@ function inMuteSegment(currentTime: number, includeOverlap: boolean): boolean {
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 @@ function setupVideoListeners() {
}

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

let startedWaiting = false;
Expand Down

0 comments on commit f2de4dc

Please sign in to comment.