mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-08 20:47:11 +03:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f5d4dfe03 | ||
|
|
821c45a6b6 | ||
|
|
ff44016003 | ||
|
|
b11d99a196 | ||
|
|
6f548987a3 | ||
|
|
c6438872e2 | ||
|
|
caae61a6a8 | ||
|
|
777f3a2769 | ||
|
|
797c9d67e4 | ||
|
|
528b475429 | ||
|
|
e39b441c16 | ||
|
|
b8cbedbc4d | ||
|
|
76b78ef132 | ||
|
|
572fee265d | ||
|
|
bfe1e8307d | ||
|
|
1b96e2107a | ||
|
|
3eccf855e5 | ||
|
|
069ae4bb58 | ||
|
|
7f8947dd0a | ||
|
|
0ae34c9603 | ||
|
|
41a3c695dc |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1,2 +1,3 @@
|
|||||||
|
github: ajayyy-org
|
||||||
patreon: ajayyy
|
patreon: ajayyy
|
||||||
custom: [sponsor.ajay.app/donate]
|
custom: [sponsor.ajay.app/donate]
|
||||||
|
|||||||
14
.github/workflows/take-action.yml
vendored
Normal file
14
.github/workflows/take-action.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# .github/workflows/take.yml
|
||||||
|
name: Assign issue to contributor
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
assign:
|
||||||
|
name: Take an issue
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: take the issue
|
||||||
|
uses: bdougie/take-action@main
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "__MSG_fullName__",
|
"name": "__MSG_fullName__",
|
||||||
"short_name": "SponsorBlock",
|
"short_name": "SponsorBlock",
|
||||||
"version": "2.1.2",
|
"version": "2.1.3",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"description": "__MSG_Description__",
|
"description": "__MSG_Description__",
|
||||||
"content_scripts": [{
|
"content_scripts": [{
|
||||||
@@ -40,6 +40,7 @@
|
|||||||
"icons/help.svg",
|
"icons/help.svg",
|
||||||
"icons/report.png",
|
"icons/report.png",
|
||||||
"icons/close.png",
|
"icons/close.png",
|
||||||
|
"icons/refresh.svg",
|
||||||
"icons/beep.ogg",
|
"icons/beep.ogg",
|
||||||
"icons/PlayerInfoIconSponsorBlocker.svg",
|
"icons/PlayerInfoIconSponsorBlocker.svg",
|
||||||
"icons/PlayerDeleteIconSponsorBlocker.svg",
|
"icons/PlayerDeleteIconSponsorBlocker.svg",
|
||||||
|
|||||||
28763
package-lock.json
generated
28763
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -24,12 +24,12 @@
|
|||||||
"copy-webpack-plugin": "^6.0.3",
|
"copy-webpack-plugin": "^6.0.3",
|
||||||
"eslint": "^7.15.0",
|
"eslint": "^7.15.0",
|
||||||
"eslint-plugin-react": "^7.21.5",
|
"eslint-plugin-react": "^7.21.5",
|
||||||
"jest": "^26.4.0",
|
"jest": "^27.0.6",
|
||||||
"rimraf": "^3.0.0",
|
"rimraf": "^3.0.0",
|
||||||
"ts-jest": "^26.2.0",
|
"ts-jest": "^27.0.3",
|
||||||
"ts-loader": "^6.2.1",
|
"ts-loader": "^6.2.1",
|
||||||
"typescript": "~3.7.3",
|
"typescript": "~4.3",
|
||||||
"web-ext": "^5.4.1",
|
"web-ext": "^6.2.0",
|
||||||
"webpack": "~4.41.2",
|
"webpack": "~4.41.2",
|
||||||
"webpack-cli": "~3.3.10",
|
"webpack-cli": "~3.3.10",
|
||||||
"webpack-merge": "~4.2.2"
|
"webpack-merge": "~4.2.2"
|
||||||
|
|||||||
@@ -85,6 +85,9 @@
|
|||||||
"noVideoID": {
|
"noVideoID": {
|
||||||
"message": "No YouTube video found.\nIf this is incorrect, refresh the tab."
|
"message": "No YouTube video found.\nIf this is incorrect, refresh the tab."
|
||||||
},
|
},
|
||||||
|
"refreshSegments": {
|
||||||
|
"message": "Refresh segments"
|
||||||
|
},
|
||||||
"success": {
|
"success": {
|
||||||
"message": "Success!"
|
"message": "Success!"
|
||||||
},
|
},
|
||||||
@@ -666,12 +669,6 @@
|
|||||||
"help": {
|
"help": {
|
||||||
"message": "Help"
|
"message": "Help"
|
||||||
},
|
},
|
||||||
"experimentUnlistedTitle": {
|
|
||||||
"message": "Help prevent this from disappearing"
|
|
||||||
},
|
|
||||||
"experimentUnlistedText": {
|
|
||||||
"message": "This video is detected as unlisted and uploaded before 2017\nOld unlisted videos are being set to private next month\nWe are collecting *public* videos to back up\nWould you like anonymously to send this video to us?\nhttps://support.google.com/youtube/answer/9230970"
|
|
||||||
},
|
|
||||||
"experiementOptOut": {
|
"experiementOptOut": {
|
||||||
"message": "Opt-out of all future experiments",
|
"message": "Opt-out of all future experiments",
|
||||||
"description": "This is used in a popup about a new experiment to get a list of unlisted videos to back up since all unlisted videos uploaded before 2017 will be set to private."
|
"description": "This is used in a popup about a new experiment to get a list of unlisted videos to back up since all unlisted videos uploaded before 2017 will be set to private."
|
||||||
|
|||||||
@@ -19,18 +19,18 @@
|
|||||||
<p class="createdBy">Created By <a href="https://ajay.app">Ajay Ramachandran</a> <img src="https://ajay.app/newprofilepic.jpg" height="30" class="profilepiccircle"/></p>
|
<p class="createdBy">Created By <a href="https://ajay.app">Ajay Ramachandran</a> <img src="https://ajay.app/newprofilepic.jpg" height="30" class="profilepiccircle"/></p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Thanks for installing SponsorBlock. Here are some quick tips for getting started. Feel free to contact me if you have any questions. By using this extension, you agree to the <a href="https://gist.github.com/ajayyy/aa9f8ded2b573d4f73a3ffa0ef74f796">Privacy Policy</a> and <a href="https://gist.github.com/ajayyy/9e8100f069348e0bc062641f34d6af12">Terms of Use</a>.
|
Thanks for installing SponsorBlock. By using this extension, you agree to the <a href="https://gist.github.com/ajayyy/aa9f8ded2b573d4f73a3ffa0ef74f796">Privacy Policy</a> and <a href="https://gist.github.com/ajayyy/9e8100f069348e0bc062641f34d6af12">Terms of Use</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="projectPreview">
|
<p class="projectPreview">
|
||||||
Come contribute, make some suggestions and help out in the Discord: <a href="https://discord.gg/QnmVMpU">https://discord.gg/QnmVMpU</a>
|
Come contribute, make some suggestions and help out on <a href="https://discord.gg/QnmVMpU">Discord</a> or on <a href="https://matrix.to/#/#sponsor:ajay.app?via=ajay.app&via=matrix.org&via=mozilla.org">Matrix</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p style="margin-bottom: 0" class="bigText center">Please review the options below</p>
|
<p style="margin-bottom: 0" class="bigText center">Please review the options below</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Many features are disabled by default. If you want to skip Intros, outros, use Invidious, please enable the specific options. These can be enabled or disabled at any time.
|
Many features are disabled by default. If you want to skip intros, outros, use Invidious, etc., enable them below.
|
||||||
You can also hide/show all UI elements added to the YouTube page.
|
You can also hide/show UI elements.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<iframe src="../options/options.html#embed" width="100%" height="500px" style="border: none"></iframe>
|
<iframe src="../options/options.html#embed" width="100%" height="500px" style="border: none"></iframe>
|
||||||
@@ -59,12 +59,12 @@
|
|||||||
<img src="https://i.imgur.com/A1ilk6x.gif">
|
<img src="https://i.imgur.com/A1ilk6x.gif">
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
Submitting can either be done in the popup by hitting the "Sponsorship Starts Now" button or in the video player with the buttons on the player.
|
Submitting can either be done in the popup by hitting the "Segment Starts Now" button or in the video player with the buttons on the player.
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
Clicking the play button indicated the start of a sponsorship section and clicking the stop icon indicates the end. You can prepare multiple sponsors before hitting submit. Clicking the upload button will submit. Clicking the garbage can will delete.
|
Clicking the play button indicated the start of a segment and clicking the stop icon indicates the end. You can prepare multiple sponsors before hitting submit. Clicking the upload button will submit. Clicking the garbage can will delete.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1>Editing</h1>
|
<h1>Editing</h1>
|
||||||
@@ -74,27 +74,21 @@
|
|||||||
<img src="https://i.imgur.com/DZHqbsx.gif">
|
<img src="https://i.imgur.com/DZHqbsx.gif">
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
If you messed up, you can edit or delete your sponsor times in the popup or in the info menu (by hitting the info icon).
|
If you messed up, you can edit or delete your segments in the popup or in the info menu (by hitting the info icon).
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1>This is too slow</h1>
|
<h1>This is too slow</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
There are hotkeys if you want to use them. You must be focused on the YouTube player to use them. Press the semicolon key to indicate the start/end of a sponsor segment and click the apostrophe to submit.
|
There are hotkeys if you want to use them. Press the semicolon key to indicate the start/end of a sponsor segment and click the apostrophe to submit.
|
||||||
These can be changed in the options. If you don't use QWERTY, you should probably change the keybinding.
|
These can be changed in the options. If you don't use QWERTY, you should probably change the keybinding.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1>I hate these buttons, they are so ugly</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
All player buttons can be hidden in the options.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h1>Can I get a copy of the Database? What happens if you disappear?</h1>
|
<h1>Can I get a copy of the Database? What happens if you disappear?</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The database is public and available at <a href="https://sponsor.ajay.app/database.db">https://sponsor.ajay.app/database.db</a>. The source code is freely available. So, even if something happens to me, your submissions are not lost.
|
The database is public and available at <a href="https://sponsor.ajay.app/database">https://sponsor.ajay.app/database</a> and the source code is freely available. So, even if something happens to me, your submissions are not lost.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1>News and how it is made</h1>
|
<h1>News and how it is made</h1>
|
||||||
@@ -103,12 +97,6 @@
|
|||||||
See <a href="https://sponsor.ajay.app/news">https://sponsor.ajay.app/news</a>.
|
See <a href="https://sponsor.ajay.app/news">https://sponsor.ajay.app/news</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1>I want more features!</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Ask on Discord or make an Issue on GitHub. I am happy to hear suggestions or improvements you want. You may also contribute code or graphics if you would like.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h1>Where can I get the source code?</h1>
|
<h1>Where can I get the source code?</h1>
|
||||||
|
|
||||||
<h4 style="display: inline">Client:</h4>
|
<h4 style="display: inline">Client:</h4>
|
||||||
|
|||||||
1
public/icons/refresh.svg
Normal file
1
public/icons/refresh.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
||||||
|
After Width: | Height: | Size: 361 B |
@@ -259,7 +259,7 @@ background-color:#ec1c1c;
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#additionalButtons>button, button#setUsernameButton, #submitUsername {
|
.sbSlimButton, #additionalButtons>button, button#setUsernameButton, #submitUsername {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
@@ -321,12 +321,12 @@ label>p, #disableExtension>p, #usernameValue, #usernameElement > div > p,#sponso
|
|||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#whitelistButton>label, #additionalButtons>button, div#setUsernameContainer {
|
#whitelistButton>label, #additionalButtons>button, div#setUsernameContainer>button {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row nowrap;
|
flex-flow: row nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#whitelistButton>label, #additionalButtons>button, div#setUsernameContainer>button {
|
.sbSlimButton, #whitelistButton>label, #additionalButtons>button, div#setUsernameContainer>button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,9 @@
|
|||||||
<p id="loadingIndicator">__MSG_noVideoID__</p>
|
<p id="loadingIndicator">__MSG_noVideoID__</p>
|
||||||
<!-- If the video was found in the database -->
|
<!-- If the video was found in the database -->
|
||||||
<p id="videoFound"></p>
|
<p id="videoFound"></p>
|
||||||
|
<button class="sbSlimButton hidden" id="refreshSegmentsButton" title="__MSG_refreshSegments__">
|
||||||
|
<img id="refreshSegments" src="/icons/refresh.svg"/>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="issueReporterContainer">
|
<div id="issueReporterContainer">
|
||||||
<div id="issueReporterTimeButtons"></div>
|
<div id="issueReporterTimeButtons"></div>
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ interface SBConfig {
|
|||||||
testingServer: boolean,
|
testingServer: boolean,
|
||||||
refetchWhenNotFound: boolean,
|
refetchWhenNotFound: boolean,
|
||||||
ytInfoPermissionGranted: boolean,
|
ytInfoPermissionGranted: boolean,
|
||||||
askAboutUnlistedVideos: boolean,
|
|
||||||
allowExpirements: boolean,
|
allowExpirements: boolean,
|
||||||
autoHideInfoButton: boolean,
|
autoHideInfoButton: boolean,
|
||||||
|
|
||||||
@@ -179,7 +178,6 @@ const Config: SBObject = {
|
|||||||
testingServer: false,
|
testingServer: false,
|
||||||
refetchWhenNotFound: true,
|
refetchWhenNotFound: true,
|
||||||
ytInfoPermissionGranted: false,
|
ytInfoPermissionGranted: false,
|
||||||
askAboutUnlistedVideos: true,
|
|
||||||
allowExpirements: true,
|
allowExpirements: true,
|
||||||
autoHideInfoButton: true,
|
autoHideInfoButton: true,
|
||||||
|
|
||||||
@@ -345,6 +343,10 @@ function fetchConfig(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function migrateOldFormats(config: SBConfig) {
|
function migrateOldFormats(config: SBConfig) {
|
||||||
|
if (config["askAboutUnlistedVideos"]) {
|
||||||
|
chrome.storage.sync.remove("askAboutUnlistedVideos");
|
||||||
|
}
|
||||||
|
|
||||||
// Adding preview category
|
// Adding preview category
|
||||||
if (!config["previewCategoryUpdate"]) {
|
if (!config["previewCategoryUpdate"]) {
|
||||||
config["previewCategoryUpdate"] = true;
|
config["previewCategoryUpdate"] = true;
|
||||||
|
|||||||
218
src/content.ts
218
src/content.ts
@@ -1,6 +1,6 @@
|
|||||||
import Config from "./config";
|
import Config from "./config";
|
||||||
|
|
||||||
import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, FetchResponse, VideoInfo, StorageChangesObject, ChannelIDInfo, ChannelIDStatus, SponsorSourceType } from "./types";
|
import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, VideoInfo, StorageChangesObject, ChannelIDInfo, ChannelIDStatus, SponsorSourceType } from "./types";
|
||||||
|
|
||||||
import { ContentContainer } from "./types";
|
import { ContentContainer } from "./types";
|
||||||
import Utils from "./utils";
|
import Utils from "./utils";
|
||||||
@@ -123,7 +123,7 @@ const manualSkipPercentCount = 0.5;
|
|||||||
//get messages from the background script and the popup
|
//get messages from the background script and the popup
|
||||||
chrome.runtime.onMessage.addListener(messageListener);
|
chrome.runtime.onMessage.addListener(messageListener);
|
||||||
|
|
||||||
function messageListener(request: Message, sender: unknown, sendResponse: (response: MessageResponse) => void): void {
|
async function messageListener(request: Message, sender: unknown, sendResponse: (response: MessageResponse) => void): Promise<void> {
|
||||||
//messages from popup script
|
//messages from popup script
|
||||||
switch(request.message){
|
switch(request.message){
|
||||||
case "update":
|
case "update":
|
||||||
@@ -178,6 +178,10 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
|||||||
case "submitTimes":
|
case "submitTimes":
|
||||||
submitSponsorTimes();
|
submitSponsorTimes();
|
||||||
break;
|
break;
|
||||||
|
case "refreshSegments":
|
||||||
|
await sponsorsLookup(sponsorVideoID, false);
|
||||||
|
sendResponse({});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +213,7 @@ function resetValues() {
|
|||||||
//reset sponsor times
|
//reset sponsor times
|
||||||
sponsorTimes = null;
|
sponsorTimes = null;
|
||||||
sponsorLookupRetries = 0;
|
sponsorLookupRetries = 0;
|
||||||
|
sponsorSkipped = [];
|
||||||
|
|
||||||
videoInfo = null;
|
videoInfo = null;
|
||||||
channelWhitelisted = false;
|
channelWhitelisted = false;
|
||||||
@@ -236,6 +241,10 @@ function resetValues() {
|
|||||||
|
|
||||||
// Reset advert playing flag
|
// Reset advert playing flag
|
||||||
isAdPlaying = false;
|
isAdPlaying = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < skipNotices.length; i++) {
|
||||||
|
skipNotices.pop().close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function videoIDChange(id) {
|
async function videoIDChange(id) {
|
||||||
@@ -273,9 +282,6 @@ async function videoIDChange(id) {
|
|||||||
// Update whitelist data when the video data is loaded
|
// Update whitelist data when the video data is loaded
|
||||||
whitelistCheck();
|
whitelistCheck();
|
||||||
|
|
||||||
// Temporary expirement
|
|
||||||
unlistedCheck();
|
|
||||||
|
|
||||||
//setup the preview bar
|
//setup the preview bar
|
||||||
if (previewBar === null) {
|
if (previewBar === null) {
|
||||||
if (onMobileYouTube) {
|
if (onMobileYouTube) {
|
||||||
@@ -569,7 +575,7 @@ function setupVideoListeners() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sponsorsLookup(id: string) {
|
async function sponsorsLookup(id: string, keepOldSubmissions = true) {
|
||||||
if (!video) refreshVideoAttachments();
|
if (!video) refreshVideoAttachments();
|
||||||
//there is still no video here
|
//there is still no video here
|
||||||
if (!video) {
|
if (!video) {
|
||||||
@@ -590,43 +596,45 @@ async function sponsorsLookup(id: string) {
|
|||||||
|
|
||||||
// Check for hashPrefix setting
|
// Check for hashPrefix setting
|
||||||
const hashPrefix = (await utils.getHash(id, 1)).substr(0, 4);
|
const hashPrefix = (await utils.getHash(id, 1)).substr(0, 4);
|
||||||
utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
|
const response = await utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
|
||||||
categories
|
categories
|
||||||
}).then(async (response: FetchResponse) => {
|
});
|
||||||
if (response?.ok) {
|
|
||||||
const recievedSegments: SponsorTime[] = JSON.parse(response.responseText)
|
|
||||||
?.filter((video) => video.videoID === id)
|
|
||||||
?.map((video) => video.segments)[0];
|
|
||||||
if (!recievedSegments || !recievedSegments.length) {
|
|
||||||
// return if no video found
|
|
||||||
retryFetch();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sponsorDataFound = true;
|
if (response?.ok) {
|
||||||
|
const recievedSegments: SponsorTime[] = JSON.parse(response.responseText)
|
||||||
|
?.filter((video) => video.videoID === id)
|
||||||
|
?.map((video) => video.segments)[0];
|
||||||
|
if (!recievedSegments || !recievedSegments.length) {
|
||||||
|
// return if no video found
|
||||||
|
retryFetch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if any old submissions should be kept
|
sponsorDataFound = true;
|
||||||
if (sponsorTimes !== null) {
|
|
||||||
for (let i = 0; i < sponsorTimes.length; i++) {
|
// Check if any old submissions should be kept
|
||||||
if (sponsorTimes[i].source === SponsorSourceType.Local) {
|
if (sponsorTimes !== null && keepOldSubmissions) {
|
||||||
// This is a user submission, keep it
|
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||||
recievedSegments.push(sponsorTimes[i]);
|
if (sponsorTimes[i].source === SponsorSourceType.Local) {
|
||||||
}
|
// This is a user submission, keep it
|
||||||
|
recievedSegments.push(sponsorTimes[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const oldSegments = sponsorTimes || [];
|
const oldSegments = sponsorTimes || [];
|
||||||
sponsorTimes = recievedSegments;
|
sponsorTimes = recievedSegments;
|
||||||
|
|
||||||
// Hide all submissions smaller than the minimum duration
|
// Hide all submissions smaller than the minimum duration
|
||||||
if (Config.config.minDuration !== 0) {
|
if (Config.config.minDuration !== 0) {
|
||||||
for (let i = 0; i < sponsorTimes.length; i++) {
|
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||||
if (sponsorTimes[i].segment[1] - sponsorTimes[i].segment[0] < Config.config.minDuration) {
|
if (sponsorTimes[i].segment[1] - sponsorTimes[i].segment[0] < Config.config.minDuration) {
|
||||||
sponsorTimes[i].hidden = SponsorHideType.MinimumDuration;
|
sponsorTimes[i].hidden = SponsorHideType.MinimumDuration;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keepOldSubmissions) {
|
||||||
for (const segment of oldSegments) {
|
for (const segment of oldSegments) {
|
||||||
const otherSegment = sponsorTimes.find((other) => segment.UUID === other.UUID);
|
const otherSegment = sponsorTimes.find((other) => segment.UUID === other.UUID);
|
||||||
if (otherSegment) {
|
if (otherSegment) {
|
||||||
@@ -635,37 +643,34 @@ async function sponsorsLookup(id: string) {
|
|||||||
otherSegment.category = segment.category;
|
otherSegment.category = segment.category;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startSkipScheduleCheckingForStartSponsors();
|
|
||||||
|
|
||||||
// Reset skip save
|
|
||||||
sponsorSkipped = [];
|
|
||||||
|
|
||||||
//update the preview bar
|
|
||||||
//leave the type blank for now until categories are added
|
|
||||||
if (lastPreviewBarUpdate == id || (lastPreviewBarUpdate == null && !isNaN(video.duration))) {
|
|
||||||
//set it now
|
|
||||||
//otherwise the listener can handle it
|
|
||||||
updatePreviewBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
sponsorLookupRetries = 0;
|
|
||||||
} else if (response?.status === 404) {
|
|
||||||
retryFetch();
|
|
||||||
} else if (sponsorLookupRetries < 15 && !recheckStarted) {
|
|
||||||
recheckStarted = true;
|
|
||||||
|
|
||||||
//TODO lower when server becomes better (back to 1 second)
|
|
||||||
//some error occurred, try again in a second
|
|
||||||
setTimeout(() => {
|
|
||||||
if (sponsorVideoID && sponsorTimes?.length === 0) {
|
|
||||||
sponsorsLookup(sponsorVideoID);
|
|
||||||
}
|
|
||||||
}, 5000 + Math.random() * 15000 + 5000 * sponsorLookupRetries);
|
|
||||||
|
|
||||||
sponsorLookupRetries++;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
startSkipScheduleCheckingForStartSponsors();
|
||||||
|
|
||||||
|
//update the preview bar
|
||||||
|
//leave the type blank for now until categories are added
|
||||||
|
if (lastPreviewBarUpdate == id || (lastPreviewBarUpdate == null && !isNaN(video.duration))) {
|
||||||
|
//set it now
|
||||||
|
//otherwise the listener can handle it
|
||||||
|
updatePreviewBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
sponsorLookupRetries = 0;
|
||||||
|
} else if (response?.status === 404) {
|
||||||
|
retryFetch();
|
||||||
|
} else if (sponsorLookupRetries < 15 && !recheckStarted) {
|
||||||
|
recheckStarted = true;
|
||||||
|
|
||||||
|
//TODO lower when server becomes better (back to 1 second)
|
||||||
|
//some error occurred, try again in a second
|
||||||
|
setTimeout(() => {
|
||||||
|
if (sponsorVideoID && sponsorTimes?.length === 0) {
|
||||||
|
sponsorsLookup(sponsorVideoID);
|
||||||
|
}
|
||||||
|
}, 5000 + Math.random() * 15000 + 5000 * sponsorLookupRetries);
|
||||||
|
|
||||||
|
sponsorLookupRetries++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function retryFetch(): void {
|
function retryFetch(): void {
|
||||||
@@ -849,7 +854,7 @@ async function whitelistCheck() {
|
|||||||
|
|
||||||
channelIDInfo = {
|
channelIDInfo = {
|
||||||
status: ChannelIDStatus.Found,
|
status: ChannelIDStatus.Found,
|
||||||
id: getChannelID()
|
id: getChannelID().match(/^\/?([^\s/]+)/)[0]
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
channelIDInfo = {
|
channelIDInfo = {
|
||||||
@@ -861,7 +866,8 @@ async function whitelistCheck() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//see if this is a whitelisted channel
|
//see if this is a whitelisted channel
|
||||||
if (whitelistedChannels != undefined && whitelistedChannels.includes(getChannelID())) {
|
if (whitelistedChannels != undefined &&
|
||||||
|
channelIDInfo.status === ChannelIDStatus.Found && whitelistedChannels.includes(channelIDInfo.id)) {
|
||||||
channelWhitelisted = true;
|
channelWhitelisted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -869,66 +875,6 @@ async function whitelistCheck() {
|
|||||||
if (Config.config.forceChannelCheck && sponsorTimes?.length > 0) startSkipScheduleCheckingForStartSponsors();
|
if (Config.config.forceChannelCheck && sponsorTimes?.length > 0) startSkipScheduleCheckingForStartSponsors();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function unlistedCheck() {
|
|
||||||
if (!Config.config.allowExpirements || !Config.config.askAboutUnlistedVideos) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await utils.wait(() => !!videoInfo && !!document.getElementById("info-text")
|
|
||||||
&& !!document.querySelector(".ytd-video-primary-info-renderer > .badge > yt-icon > svg"), 6000, 1000);
|
|
||||||
|
|
||||||
const isUnlisted = document.querySelector(".ytd-video-primary-info-renderer > .badge > yt-icon > svg > g > path")
|
|
||||||
?.getAttribute("d")?.includes("M3.9 12c0-1.71 1.39-3.1 3.1-3.1h"); // Icon of unlisted badge
|
|
||||||
const yearMatches = document.querySelector("#info-text > #info-strings > yt-formatted-string")
|
|
||||||
?.innerHTML?.match(/20[0-9]{2}/);
|
|
||||||
const year = yearMatches ? parseInt(yearMatches[0]) : -1;
|
|
||||||
const isOld = !isNaN(year) && year < 2017 && year > 2004;
|
|
||||||
const views = parseInt(videoInfo?.videoDetails?.viewCount);
|
|
||||||
const isHighViews = views > 15000;
|
|
||||||
|
|
||||||
if (isUnlisted && isOld && isHighViews && (!sponsorTimes || sponsorTimes.length <= 0)) {
|
|
||||||
// Ask if they want to submit this videoID
|
|
||||||
const notice = new GenericNotice(skipNoticeContentContainer, "unlistedWarning", {
|
|
||||||
title: chrome.i18n.getMessage("experimentUnlistedTitle"),
|
|
||||||
textBoxes: chrome.i18n.getMessage("experimentUnlistedText").split("\n"),
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
name: chrome.i18n.getMessage("experiementOptOut"),
|
|
||||||
listener: () => {
|
|
||||||
Config.config.allowExpirements = false;
|
|
||||||
|
|
||||||
notice.close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: chrome.i18n.getMessage("hideForever"),
|
|
||||||
listener: () => {
|
|
||||||
Config.config.askAboutUnlistedVideos = false;
|
|
||||||
|
|
||||||
notice.close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Submit",
|
|
||||||
listener: () => {
|
|
||||||
utils.asyncRequestToServer("POST", "/api/unlistedVideo", {
|
|
||||||
videoID: sponsorVideoID,
|
|
||||||
year,
|
|
||||||
views,
|
|
||||||
channelID: channelIDInfo.status === ChannelIDStatus.Found ? channelIDInfo.id : undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
notice.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns info about the next upcoming sponsor skip
|
* Returns info about the next upcoming sponsor skip
|
||||||
*/
|
*/
|
||||||
@@ -1408,11 +1354,12 @@ function openInfoMenu() {
|
|||||||
const settings = <HTMLImageElement> popup.querySelector("#sbPopupIconSettings");
|
const settings = <HTMLImageElement> popup.querySelector("#sbPopupIconSettings");
|
||||||
const edit = <HTMLImageElement> popup.querySelector("#sbPopupIconEdit");
|
const edit = <HTMLImageElement> popup.querySelector("#sbPopupIconEdit");
|
||||||
const check = <HTMLImageElement> popup.querySelector("#sbPopupIconCheck");
|
const check = <HTMLImageElement> popup.querySelector("#sbPopupIconCheck");
|
||||||
|
const refreshSegments = <HTMLImageElement> popup.querySelector("#refreshSegments");
|
||||||
logo.src = chrome.extension.getURL("icons/IconSponsorBlocker256px.png");
|
logo.src = chrome.extension.getURL("icons/IconSponsorBlocker256px.png");
|
||||||
settings.src = chrome.extension.getURL("icons/settings.svg");
|
settings.src = chrome.extension.getURL("icons/settings.svg");
|
||||||
edit.src = chrome.extension.getURL("icons/pencil.svg");
|
edit.src = chrome.extension.getURL("icons/pencil.svg");
|
||||||
check.src = chrome.extension.getURL("icons/check.svg");
|
check.src = chrome.extension.getURL("icons/check.svg");
|
||||||
check.src = chrome.extension.getURL("icons/thumb.svg");
|
refreshSegments.src = chrome.extension.getURL("icons/refresh.svg");
|
||||||
|
|
||||||
parentNode.insertBefore(popup, parentNode.firstChild);
|
parentNode.insertBefore(popup, parentNode.firstChild);
|
||||||
|
|
||||||
@@ -1552,7 +1499,7 @@ function submitSponsorTimes() {
|
|||||||
async function sendSubmitMessage() {
|
async function sendSubmitMessage() {
|
||||||
// Add loading animation
|
// Add loading animation
|
||||||
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker.svg");
|
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker.svg");
|
||||||
playerButtons.submit.button.style.animation = "rotate 1s 0s infinite";
|
const stopAnimation = utils.applyLoadingAnimation(playerButtons.submit.button, 1, () => updateEditButtonsOnPlayer());
|
||||||
|
|
||||||
//check if a sponsor exceeds the duration of the video
|
//check if a sponsor exceeds the duration of the video
|
||||||
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
|
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
|
||||||
@@ -1584,22 +1531,7 @@ async function sendSubmitMessage() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
// Handle submission success
|
stopAnimation();
|
||||||
const submitButton = playerButtons.submit.button;
|
|
||||||
|
|
||||||
// Make the animation finite
|
|
||||||
submitButton.style.animation = "rotate 1s";
|
|
||||||
|
|
||||||
// When the animation is over, hide the button
|
|
||||||
const animationEndListener = () => {
|
|
||||||
updateEditButtonsOnPlayer();
|
|
||||||
|
|
||||||
submitButton.style.animation = "none";
|
|
||||||
|
|
||||||
submitButton.removeEventListener("animationend", animationEndListener);
|
|
||||||
};
|
|
||||||
|
|
||||||
submitButton.addEventListener("animationend", animationEndListener);
|
|
||||||
|
|
||||||
// Remove segments from storage since they've already been submitted
|
// Remove segments from storage since they've already been submitted
|
||||||
Config.config.segmentTimes.delete(sponsorVideoID);
|
Config.config.segmentTimes.delete(sponsorVideoID);
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ interface DefaultMessage {
|
|||||||
| "getVideoID"
|
| "getVideoID"
|
||||||
| "getChannelID"
|
| "getChannelID"
|
||||||
| "isChannelWhitelisted"
|
| "isChannelWhitelisted"
|
||||||
| "submitTimes";
|
| "submitTimes"
|
||||||
|
| "refreshSegments";
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BoolValueMessage {
|
interface BoolValueMessage {
|
||||||
@@ -52,5 +53,6 @@ export type MessageResponse =
|
|||||||
| GetVideoIdResponse
|
| GetVideoIdResponse
|
||||||
| GetChannelIDResponse
|
| GetChannelIDResponse
|
||||||
| SponsorStartResponse
|
| SponsorStartResponse
|
||||||
| IsChannelWhitelistedResponse;
|
| IsChannelWhitelistedResponse
|
||||||
|
| Record<string, never>;
|
||||||
|
|
||||||
|
|||||||
25
src/popup.ts
25
src/popup.ts
@@ -102,6 +102,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
"videoFound",
|
"videoFound",
|
||||||
"sponsorMessageTimes",
|
"sponsorMessageTimes",
|
||||||
//"downloadedSponsorMessageTimes",
|
//"downloadedSponsorMessageTimes",
|
||||||
|
"refreshSegmentsButton",
|
||||||
"whitelistButton",
|
"whitelistButton",
|
||||||
"sbDonate"
|
"sbDonate"
|
||||||
].forEach(id => PageElements[id] = document.getElementById(id));
|
].forEach(id => PageElements[id] = document.getElementById(id));
|
||||||
@@ -131,6 +132,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
PageElements.submitUsername.addEventListener("click", submitUsername);
|
PageElements.submitUsername.addEventListener("click", submitUsername);
|
||||||
PageElements.optionsButton.addEventListener("click", openOptions);
|
PageElements.optionsButton.addEventListener("click", openOptions);
|
||||||
PageElements.helpButton.addEventListener("click", openHelp);
|
PageElements.helpButton.addEventListener("click", openHelp);
|
||||||
|
PageElements.refreshSegmentsButton.addEventListener("click", refreshSegments);
|
||||||
|
|
||||||
/** If true, the content script is in the process of creating a new segment. */
|
/** If true, the content script is in the process of creating a new segment. */
|
||||||
let creatingSegment = false;
|
let creatingSegment = false;
|
||||||
@@ -290,10 +292,12 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
|
|
||||||
if (request.found) {
|
if (request.found) {
|
||||||
PageElements.videoFound.innerHTML = chrome.i18n.getMessage("sponsorFound");
|
PageElements.videoFound.innerHTML = chrome.i18n.getMessage("sponsorFound");
|
||||||
|
PageElements.refreshSegmentsButton.classList.remove("hidden");
|
||||||
|
|
||||||
displayDownloadedSponsorTimes(request);
|
displayDownloadedSponsorTimes(request);
|
||||||
} else {
|
} else {
|
||||||
PageElements.videoFound.innerHTML = chrome.i18n.getMessage("sponsor404");
|
PageElements.videoFound.innerHTML = chrome.i18n.getMessage("sponsor404");
|
||||||
|
PageElements.refreshSegmentsButton.classList.add("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +433,11 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
uuidButton.id = "sponsorTimesCopyUUIDButtonContainer" + UUID;
|
uuidButton.id = "sponsorTimesCopyUUIDButtonContainer" + UUID;
|
||||||
uuidButton.className = "voteButton";
|
uuidButton.className = "voteButton";
|
||||||
uuidButton.src = chrome.extension.getURL("icons/clipboard.svg");
|
uuidButton.src = chrome.extension.getURL("icons/clipboard.svg");
|
||||||
uuidButton.addEventListener("click", () => navigator.clipboard.writeText(UUID));
|
uuidButton.addEventListener("click", () => {
|
||||||
|
navigator.clipboard.writeText(UUID);
|
||||||
|
const stopAnimation = utils.applyLoadingAnimation(uuidButton, 0.3);
|
||||||
|
stopAnimation();
|
||||||
|
});
|
||||||
|
|
||||||
//add thumbs up, thumbs down and uuid copy buttons to the container
|
//add thumbs up, thumbs down and uuid copy buttons to the container
|
||||||
voteButtonsContainer.appendChild(upvoteButton);
|
voteButtonsContainer.appendChild(upvoteButton);
|
||||||
@@ -676,6 +684,21 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshSegments() {
|
||||||
|
const stopAnimation = utils.applyLoadingAnimation(PageElements.refreshSegmentsButton, 0.3);
|
||||||
|
|
||||||
|
messageHandler.query({
|
||||||
|
active: true,
|
||||||
|
currentWindow: true
|
||||||
|
}, tabs => {
|
||||||
|
messageHandler.sendMessage(
|
||||||
|
tabs[0].id,
|
||||||
|
{message: 'refreshSegments'},
|
||||||
|
() => stopAnimation()
|
||||||
|
)}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should skipping be disabled (visuals stay)
|
* Should skipping be disabled (visuals stay)
|
||||||
*/
|
*/
|
||||||
|
|||||||
29
src/utils.ts
29
src/utils.ts
@@ -158,6 +158,31 @@ export default class Utils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a spinning animation and returns a function to be called when it should be stopped
|
||||||
|
* The callback will be called when the animation is finished
|
||||||
|
* It waits until a full rotation is complete
|
||||||
|
*/
|
||||||
|
applyLoadingAnimation(element: HTMLElement, time: number, callback?: () => void): () => void {
|
||||||
|
element.style.animation = `rotate ${time}s 0s infinite`;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
// Make the animation finite
|
||||||
|
element.style.animation = `rotate ${time}s`;
|
||||||
|
|
||||||
|
// When the animation is over, hide the button
|
||||||
|
const animationEndListener = () => {
|
||||||
|
if (callback) callback();
|
||||||
|
|
||||||
|
element.style.animation = "none";
|
||||||
|
|
||||||
|
element.removeEventListener("animationend", animationEndListener);
|
||||||
|
};
|
||||||
|
|
||||||
|
element.addEventListener("animationend", animationEndListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges any overlapping timestamp ranges into single segments and returns them as a new array.
|
* Merges any overlapping timestamp ranges into single segments and returns them as a new array.
|
||||||
*/
|
*/
|
||||||
@@ -243,8 +268,10 @@ export default class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getLocalizedMessage(text: string): string | false {
|
getLocalizedMessage(text: string): string | false {
|
||||||
|
console.log(chrome.i18n.getMessage("forceChannelCheckPopup"));
|
||||||
const valNewH = text.replace(/__MSG_(\w+)__/g, function(match, v1) {
|
const valNewH = text.replace(/__MSG_(\w+)__/g, function(match, v1) {
|
||||||
return v1 ? chrome.i18n.getMessage(v1).replace("\n", "<br/>") : "";
|
return v1 ? chrome.i18n.getMessage(v1).replace(/</g, "<")
|
||||||
|
.replace(/"/g, """).replace(/\n/g, "<br/>") : "";
|
||||||
});
|
});
|
||||||
|
|
||||||
if(valNewH != text) {
|
if(valNewH != text) {
|
||||||
|
|||||||
Reference in New Issue
Block a user