mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-09 21:17:20 +03:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b87ae7b08 | ||
|
|
9ab8e63b1d | ||
|
|
0e147134f3 | ||
|
|
8605e23fbb | ||
|
|
5f34a777f1 | ||
|
|
1f43d07e16 | ||
|
|
a88aaf4590 | ||
|
|
25f78ed55b | ||
|
|
26e73c515c |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -12,7 +12,9 @@ jobs:
|
||||
# Initialization
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
- run: npm install
|
||||
with:
|
||||
node-version: '16'
|
||||
- run: npm ci
|
||||
- name: Copy configuration
|
||||
run: cp config.json.example config.json
|
||||
|
||||
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -14,7 +14,9 @@ jobs:
|
||||
# Initialization
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
- run: npm install
|
||||
with:
|
||||
node-version: '16'
|
||||
- run: npm ci
|
||||
- name: Copy configuration
|
||||
run: cp config.json.example config.json
|
||||
|
||||
|
||||
20
.github/workflows/tests.yml
vendored
Normal file
20
.github/workflows/tests.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
name: Run tests
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# Initialization
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
- run: npm ci
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test-without-building
|
||||
@@ -60,6 +60,8 @@ You can read the API docs [here](https://wiki.sponsor.ajay.app/index.php/API_Doc
|
||||
|
||||
# Building
|
||||
|
||||
You must have Node.js 16 installed.
|
||||
|
||||
Rename `config.json.example` to `config.json` and adjust configuration as desired.
|
||||
|
||||
There are also other build scripts available. Install `npm`, then run `npm install` in the repository to install dependencies.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_fullName__",
|
||||
"short_name": "SponsorBlock",
|
||||
"version": "3.3.1",
|
||||
"version": "3.3.2",
|
||||
"default_locale": "en",
|
||||
"description": "__MSG_Description__",
|
||||
"homepage_url": "https://sponsor.ajay.app",
|
||||
|
||||
2234
package-lock.json
generated
2234
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -274,8 +274,8 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
||||
if (countdownTime == 3) {
|
||||
//start fade out animation
|
||||
const notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
|
||||
notice.style.removeProperty("animation");
|
||||
notice.classList.add("sponsorSkipNoticeFadeOut");
|
||||
notice?.style.removeProperty("animation");
|
||||
notice?.classList.add("sponsorSkipNoticeFadeOut");
|
||||
}
|
||||
|
||||
this.setState({
|
||||
|
||||
@@ -16,6 +16,7 @@ import * as Chat from "./js-components/chat";
|
||||
import { getCategoryActionType } from "./utils/categoryUtils";
|
||||
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
|
||||
import { Tooltip } from "./render/Tooltip";
|
||||
import { getStartTimeFromUrl } from "./utils/urlParser";
|
||||
|
||||
// Hack to get the CSS loaded on permission-based sites (Invidious)
|
||||
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
|
||||
@@ -29,6 +30,7 @@ let sponsorVideoID: VideoID = null;
|
||||
// List of open skip notices
|
||||
const skipNotices: SkipNotice[] = [];
|
||||
let activeSkipKeybindElement: ToggleSkippable = null;
|
||||
let lastPOISkip = 0;
|
||||
|
||||
// JSON video info
|
||||
let videoInfo: VideoInfo = null;
|
||||
@@ -387,11 +389,11 @@ function durationChangeListener(): void {
|
||||
updateAdFlag();
|
||||
updatePreviewBar();
|
||||
|
||||
sponsorTimes = sponsorTimes.filter(segmentDurationFilter);
|
||||
if (sponsorTimes) sponsorTimes = sponsorTimes.filter(segmentDurationFilter);
|
||||
}
|
||||
|
||||
function segmentDurationFilter(segment: SponsorTime): boolean {
|
||||
return segment.videoDuration === 0 || video.duration === 0 || Math.abs(video.duration - segment.videoDuration) < 2;
|
||||
return segment.videoDuration === 0 || !video?.duration || Math.abs(video.duration - segment.videoDuration) < 2;
|
||||
}
|
||||
|
||||
function cancelSponsorSchedule(): void {
|
||||
@@ -557,13 +559,17 @@ function setupVideoListeners() {
|
||||
switchingVideos = false;
|
||||
|
||||
video.addEventListener('play', () => {
|
||||
switchingVideos = false;
|
||||
|
||||
// If it is not the first event, then the only way to get to 0 is if there is a seek event
|
||||
// This check makes sure that changing the video resolution doesn't cause the extension to think it
|
||||
// gone back to the begining
|
||||
if (!firstEvent && video.currentTime === 0) return;
|
||||
firstEvent = false;
|
||||
|
||||
if (switchingVideos) {
|
||||
switchingVideos = false;
|
||||
// If already segments loaded before video, retry to skip starting segments
|
||||
if (sponsorTimes) startSkipScheduleCheckingForStartSponsors();
|
||||
}
|
||||
|
||||
// Check if an ad is playing
|
||||
updateAdFlag();
|
||||
@@ -602,7 +608,9 @@ function setupVideoListeners() {
|
||||
getCategoryActionType(segment.category) === CategoryActionType.POI &&
|
||||
video.currentTime - segment.segment[0] > 0 &&
|
||||
video.currentTime - segment.segment[0] < previewBar.getMinimumSize(true));
|
||||
if (currentPoiSegment && !skipNotices.some((notice) => notice.segments.some((s) => s.UUID === currentPoiSegment.UUID))) {
|
||||
if (currentPoiSegment && lastPOISkip < Date.now() - 3000
|
||||
&& !skipNotices.some((notice) => notice.segments.some((s) => s.UUID === currentPoiSegment.UUID))) {
|
||||
lastPOISkip = Date.now();
|
||||
skipToTime({
|
||||
v: video,
|
||||
skipTime: currentPoiSegment.segment,
|
||||
@@ -766,24 +774,27 @@ function retryFetch(): void {
|
||||
* Ex. When segments are first loaded
|
||||
*/
|
||||
function startSkipScheduleCheckingForStartSponsors() {
|
||||
if (!switchingVideos) {
|
||||
if (!switchingVideos && sponsorTimes) {
|
||||
// See if there are any starting sponsors
|
||||
let startingSegmentTime = -1;
|
||||
let startingSegmentTime = getStartTimeFromUrl(document.URL) || -1;
|
||||
let found = false;
|
||||
let startingSegment: SponsorTime = null;
|
||||
for (const time of sponsorTimes) {
|
||||
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime
|
||||
&& getCategoryActionType(time.category) === CategoryActionType.Skippable) {
|
||||
startingSegmentTime = time.segment[0];
|
||||
startingSegment = time;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (startingSegmentTime === -1) {
|
||||
if (!found) {
|
||||
for (const time of sponsorTimesSubmitting) {
|
||||
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime
|
||||
&& getCategoryActionType(time.category) === CategoryActionType.Skippable) {
|
||||
startingSegmentTime = time.segment[0];
|
||||
startingSegment = time;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -919,7 +930,7 @@ function updatePreviewBar(): void {
|
||||
});
|
||||
});
|
||||
|
||||
previewBar.set(previewBarSegments, video.duration)
|
||||
previewBar.set(previewBarSegments, video?.duration)
|
||||
|
||||
if (Config.config.showTimeWithSkips) {
|
||||
const skippedDuration = utils.getTimestampsDuration(previewBarSegments.map(({segment}) => segment));
|
||||
@@ -1387,7 +1398,7 @@ function getRealCurrentTime(): number {
|
||||
|
||||
if (playButtonSVGData === replaceSVGData) {
|
||||
// At the end of the video
|
||||
return video.duration;
|
||||
return video?.duration;
|
||||
} else {
|
||||
return video.currentTime;
|
||||
}
|
||||
@@ -1887,7 +1898,7 @@ function showTimeWithoutSkips(skippedDuration: number): void {
|
||||
display.appendChild(duration);
|
||||
}
|
||||
|
||||
const durationAfterSkips = utils.getFormattedTime(video.duration - skippedDuration)
|
||||
const durationAfterSkips = utils.getFormattedTime(video?.duration - skippedDuration)
|
||||
|
||||
duration.innerText = (durationAfterSkips == null || skippedDuration <= 0) ? "" : " (" + durationAfterSkips + ")";
|
||||
}
|
||||
|
||||
26
src/utils/urlParser.ts
Normal file
26
src/utils/urlParser.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
export function getStartTimeFromUrl(url: string): number {
|
||||
const urlParams = new URLSearchParams(url);
|
||||
const time = urlParams?.get('t') || urlParams?.get('time_continue');
|
||||
|
||||
return urlTimeToSeconds(time);
|
||||
}
|
||||
|
||||
export function urlTimeToSeconds(time: string): number {
|
||||
if (!time) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const re = /(?:(?<hours>\d{1,3})h)?(?:(?<minutes>\d{1,2})m)?(?<seconds>\d+)s?/;
|
||||
const match = re.exec(time);
|
||||
|
||||
if (match) {
|
||||
const hours = parseInt(match.groups.hours ?? '0', 10);
|
||||
const minutes = parseInt(match.groups.minutes ?? '0', 10);
|
||||
const seconds = parseInt(match.groups.seconds ?? '0', 10);
|
||||
|
||||
return hours * 3600 + minutes * 60 + seconds;
|
||||
} else if (/\d+/.test(time)) {
|
||||
return parseInt(time, 10);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,15 @@ import * as Chrome from "selenium-webdriver/chrome";
|
||||
import * as Path from "path";
|
||||
|
||||
test("Selenium Chrome test", async () => {
|
||||
const driver = await setup();
|
||||
let driver;
|
||||
try {
|
||||
driver = await setup();
|
||||
} catch (e) {
|
||||
console.warn("A browser is probably not installed, skipping selenium tests");
|
||||
console.warn(e);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await waitForInstall(driver);
|
||||
|
||||
27
test/urlParser.test.ts
Normal file
27
test/urlParser.test.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { getStartTimeFromUrl } from '../src/utils/urlParser';
|
||||
|
||||
describe("getStartTimeFromUrl", () => {
|
||||
it("parses with a number", () => {
|
||||
expect(getStartTimeFromUrl("https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=123")).toBe(123);
|
||||
});
|
||||
|
||||
it("parses with a seconds", () => {
|
||||
expect(getStartTimeFromUrl("https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=123s")).toBe(123);
|
||||
});
|
||||
|
||||
it("parses with a minutes", () => {
|
||||
expect(getStartTimeFromUrl("https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=23m3s")).toBe(23 * 60 + 3);
|
||||
});
|
||||
|
||||
it("parses with a hours", () => {
|
||||
expect(getStartTimeFromUrl("https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=1h2m3s")).toBe(1 * 60 * 60 + 2 * 60 + 3);
|
||||
});
|
||||
|
||||
it("works with time_continue", () => {
|
||||
expect(getStartTimeFromUrl("https://www.youtube.com/watch?v=dQw4w9WgXcQ&time_continue=123")).toBe(123);
|
||||
});
|
||||
|
||||
it("works with no time", () => {
|
||||
expect(getStartTimeFromUrl("https://www.youtube.com/watch?v=dQw4w9WgXcQ")).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user