Compare commits

..

21 Commits
2.1.2 ... 2.1.3

Author SHA1 Message Date
Ajay Ramachandran
2f5d4dfe03 Increase version number 2021-07-21 11:49:13 -04:00
Ajay Ramachandran
821c45a6b6 Merge branch 'master' of https://github.com/ajayyy/SponsorBlock 2021-07-21 11:48:21 -04:00
Ajay Ramachandran
ff44016003 Remove unlisted experiment 2021-07-21 11:48:19 -04:00
Ajay Ramachandran
b11d99a196 Cleanup help page a bit 2021-07-21 02:26:15 -04:00
Ajay Ramachandran
6f548987a3 Update db link in help page 2021-07-21 02:21:00 -04:00
Ajay Ramachandran
c6438872e2 Add github link 2021-07-20 12:28:02 -04:00
Ajay Ramachandran
caae61a6a8 Clear all skip notices when changing videos
Closes https://github.com/ajayyy/SponsorBlock/issues/726
2021-07-18 15:05:47 -04:00
Ajay Ramachandran
777f3a2769 Only show refresh button in popup if segments are found 2021-07-16 12:37:38 -04:00
Ajay Ramachandran
797c9d67e4 Switch back to es6 2021-07-15 14:09:10 -04:00
Ajay Ramachandran
528b475429 Fix quotations not working in localisation 2021-07-14 15:28:38 -04:00
Ajay Ramachandran
e39b441c16 Fix unused import 2021-07-13 18:23:14 -04:00
Ajay Ramachandran
b8cbedbc4d Add animation to copy button
Closes https://github.com/ajayyy/SponsorBlock/issues/796
2021-07-13 18:22:56 -04:00
Ajay Ramachandran
76b78ef132 Add animation to refresh segments
Closes https://github.com/ajayyy/SponsorBlock/issues/839
2021-07-13 18:12:45 -04:00
Ajay Ramachandran
572fee265d Handle / in channel URL
Fixes https://github.com/ajayyy/SponsorBlock/issues/766
2021-07-11 22:12:08 -04:00
Ajay Ramachandran
bfe1e8307d Increase version number 2021-07-11 18:16:22 -04:00
Ajay Ramachandran
1b96e2107a Fix type in unlisted experiment 2021-07-07 16:09:18 -04:00
Ajay Ramachandran
3eccf855e5 Fix type in unlisted experiment 2021-07-07 13:12:22 -04:00
Ajay Ramachandran
069ae4bb58 Add issue assigning action 2021-07-05 20:50:59 -04:00
Ajay Ramachandran
7f8947dd0a Add refresh segments button 2021-07-04 16:12:41 -04:00
Ajay Ramachandran
0ae34c9603 Update more packages 2021-07-04 15:01:33 -04:00
Ajay Ramachandran
41a3c695dc Update packages 2021-07-04 14:35:46 -04:00
15 changed files with 24675 additions and 4447 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1,2 +1,3 @@
github: ajayyy-org
patreon: ajayyy
custom: [sponsor.ajay.app/donate]

14
.github/workflows/take-action.yml vendored Normal file
View 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 }}

View File

@@ -1,7 +1,7 @@
{
"name": "__MSG_fullName__",
"short_name": "SponsorBlock",
"version": "2.1.2",
"version": "2.1.3",
"default_locale": "en",
"description": "__MSG_Description__",
"content_scripts": [{
@@ -40,6 +40,7 @@
"icons/help.svg",
"icons/report.png",
"icons/close.png",
"icons/refresh.svg",
"icons/beep.ogg",
"icons/PlayerInfoIconSponsorBlocker.svg",
"icons/PlayerDeleteIconSponsorBlocker.svg",

28763
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -24,12 +24,12 @@
"copy-webpack-plugin": "^6.0.3",
"eslint": "^7.15.0",
"eslint-plugin-react": "^7.21.5",
"jest": "^26.4.0",
"jest": "^27.0.6",
"rimraf": "^3.0.0",
"ts-jest": "^26.2.0",
"ts-jest": "^27.0.3",
"ts-loader": "^6.2.1",
"typescript": "~3.7.3",
"web-ext": "^5.4.1",
"typescript": "~4.3",
"web-ext": "^6.2.0",
"webpack": "~4.41.2",
"webpack-cli": "~3.3.10",
"webpack-merge": "~4.2.2"

View File

@@ -85,6 +85,9 @@
"noVideoID": {
"message": "No YouTube video found.\nIf this is incorrect, refresh the tab."
},
"refreshSegments": {
"message": "Refresh segments"
},
"success": {
"message": "Success!"
},
@@ -666,12 +669,6 @@
"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": {
"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."

View File

@@ -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>
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 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 style="margin-bottom: 0" class="bigText center">Please review the options below</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.
You can also hide/show all UI elements added to the YouTube page.
Many features are disabled by default. If you want to skip intros, outros, use Invidious, etc., enable them below.
You can also hide/show UI elements.
</p>
<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">
</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/>
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>
<h1>Editing</h1>
@@ -74,27 +74,21 @@
<img src="https://i.imgur.com/DZHqbsx.gif">
</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>
<h1>This is too slow</h1>
<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.
</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>
<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>
<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>.
</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>
<h4 style="display: inline">Client:</h4>

1
public/icons/refresh.svg Normal file
View 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

View File

@@ -259,7 +259,7 @@ background-color:#ec1c1c;
align-items: center;
}
#additionalButtons>button, button#setUsernameButton, #submitUsername {
.sbSlimButton, #additionalButtons>button, button#setUsernameButton, #submitUsername {
background: none;
border: none;
color: white;
@@ -321,12 +321,12 @@ label>p, #disableExtension>p, #usernameValue, #usernameElement > div > p,#sponso
margin-right: 8px;
}
#whitelistButton>label, #additionalButtons>button, div#setUsernameContainer {
#whitelistButton>label, #additionalButtons>button, div#setUsernameContainer>button {
display: flex;
flex-flow: row nowrap;
}
#whitelistButton>label, #additionalButtons>button, div#setUsernameContainer>button {
.sbSlimButton, #whitelistButton>label, #additionalButtons>button, div#setUsernameContainer>button {
cursor: pointer;
}

View File

@@ -18,6 +18,9 @@
<p id="loadingIndicator">__MSG_noVideoID__</p>
<!-- If the video was found in the database -->
<p id="videoFound"></p>
<button class="sbSlimButton hidden" id="refreshSegmentsButton" title="__MSG_refreshSegments__">
<img id="refreshSegments" src="/icons/refresh.svg"/>
</button>
</div>
<div id="issueReporterContainer">
<div id="issueReporterTimeButtons"></div>

View File

@@ -39,7 +39,6 @@ interface SBConfig {
testingServer: boolean,
refetchWhenNotFound: boolean,
ytInfoPermissionGranted: boolean,
askAboutUnlistedVideos: boolean,
allowExpirements: boolean,
autoHideInfoButton: boolean,
@@ -179,7 +178,6 @@ const Config: SBObject = {
testingServer: false,
refetchWhenNotFound: true,
ytInfoPermissionGranted: false,
askAboutUnlistedVideos: true,
allowExpirements: true,
autoHideInfoButton: true,
@@ -345,6 +343,10 @@ function fetchConfig(): Promise<void> {
}
function migrateOldFormats(config: SBConfig) {
if (config["askAboutUnlistedVideos"]) {
chrome.storage.sync.remove("askAboutUnlistedVideos");
}
// Adding preview category
if (!config["previewCategoryUpdate"]) {
config["previewCategoryUpdate"] = true;

View File

@@ -1,6 +1,6 @@
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 Utils from "./utils";
@@ -123,7 +123,7 @@ const manualSkipPercentCount = 0.5;
//get messages from the background script and the popup
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
switch(request.message){
case "update":
@@ -178,6 +178,10 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
case "submitTimes":
submitSponsorTimes();
break;
case "refreshSegments":
await sponsorsLookup(sponsorVideoID, false);
sendResponse({});
break;
}
}
@@ -209,6 +213,7 @@ function resetValues() {
//reset sponsor times
sponsorTimes = null;
sponsorLookupRetries = 0;
sponsorSkipped = [];
videoInfo = null;
channelWhitelisted = false;
@@ -236,6 +241,10 @@ function resetValues() {
// Reset advert playing flag
isAdPlaying = false;
for (let i = 0; i < skipNotices.length; i++) {
skipNotices.pop().close();
}
}
async function videoIDChange(id) {
@@ -273,9 +282,6 @@ async function videoIDChange(id) {
// Update whitelist data when the video data is loaded
whitelistCheck();
// Temporary expirement
unlistedCheck();
//setup the preview bar
if (previewBar === null) {
if (onMobileYouTube) {
@@ -569,7 +575,7 @@ function setupVideoListeners() {
}
}
async function sponsorsLookup(id: string) {
async function sponsorsLookup(id: string, keepOldSubmissions = true) {
if (!video) refreshVideoAttachments();
//there is still no video here
if (!video) {
@@ -590,43 +596,45 @@ async function sponsorsLookup(id: string) {
// Check for hashPrefix setting
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
}).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
if (sponsorTimes !== null) {
for (let i = 0; i < sponsorTimes.length; i++) {
if (sponsorTimes[i].source === SponsorSourceType.Local) {
// This is a user submission, keep it
recievedSegments.push(sponsorTimes[i]);
}
sponsorDataFound = true;
// Check if any old submissions should be kept
if (sponsorTimes !== null && keepOldSubmissions) {
for (let i = 0; i < sponsorTimes.length; i++) {
if (sponsorTimes[i].source === SponsorSourceType.Local) {
// This is a user submission, keep it
recievedSegments.push(sponsorTimes[i]);
}
}
}
const oldSegments = sponsorTimes || [];
sponsorTimes = recievedSegments;
const oldSegments = sponsorTimes || [];
sponsorTimes = recievedSegments;
// Hide all submissions smaller than the minimum duration
if (Config.config.minDuration !== 0) {
for (let i = 0; i < sponsorTimes.length; i++) {
if (sponsorTimes[i].segment[1] - sponsorTimes[i].segment[0] < Config.config.minDuration) {
sponsorTimes[i].hidden = SponsorHideType.MinimumDuration;
}
// Hide all submissions smaller than the minimum duration
if (Config.config.minDuration !== 0) {
for (let i = 0; i < sponsorTimes.length; i++) {
if (sponsorTimes[i].segment[1] - sponsorTimes[i].segment[0] < Config.config.minDuration) {
sponsorTimes[i].hidden = SponsorHideType.MinimumDuration;
}
}
}
if (keepOldSubmissions) {
for (const segment of oldSegments) {
const otherSegment = sponsorTimes.find((other) => segment.UUID === other.UUID);
if (otherSegment) {
@@ -635,37 +643,34 @@ async function sponsorsLookup(id: string) {
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 {
@@ -849,7 +854,7 @@ async function whitelistCheck() {
channelIDInfo = {
status: ChannelIDStatus.Found,
id: getChannelID()
id: getChannelID().match(/^\/?([^\s/]+)/)[0]
}
} catch (e) {
channelIDInfo = {
@@ -861,7 +866,8 @@ async function whitelistCheck() {
}
//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;
}
@@ -869,66 +875,6 @@ async function whitelistCheck() {
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
*/
@@ -1408,11 +1354,12 @@ function openInfoMenu() {
const settings = <HTMLImageElement> popup.querySelector("#sbPopupIconSettings");
const edit = <HTMLImageElement> popup.querySelector("#sbPopupIconEdit");
const check = <HTMLImageElement> popup.querySelector("#sbPopupIconCheck");
const refreshSegments = <HTMLImageElement> popup.querySelector("#refreshSegments");
logo.src = chrome.extension.getURL("icons/IconSponsorBlocker256px.png");
settings.src = chrome.extension.getURL("icons/settings.svg");
edit.src = chrome.extension.getURL("icons/pencil.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);
@@ -1552,7 +1499,7 @@ function submitSponsorTimes() {
async function sendSubmitMessage() {
// Add loading animation
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
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
@@ -1584,22 +1531,7 @@ async function sendSubmitMessage() {
});
if (response.status === 200) {
// Handle submission success
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);
stopAnimation();
// Remove segments from storage since they've already been submitted
Config.config.segmentTimes.delete(sponsorVideoID);

View File

@@ -16,7 +16,8 @@ interface DefaultMessage {
| "getVideoID"
| "getChannelID"
| "isChannelWhitelisted"
| "submitTimes";
| "submitTimes"
| "refreshSegments";
}
interface BoolValueMessage {
@@ -52,5 +53,6 @@ export type MessageResponse =
| GetVideoIdResponse
| GetChannelIDResponse
| SponsorStartResponse
| IsChannelWhitelistedResponse;
| IsChannelWhitelistedResponse
| Record<string, never>;

View File

@@ -102,6 +102,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
"videoFound",
"sponsorMessageTimes",
//"downloadedSponsorMessageTimes",
"refreshSegmentsButton",
"whitelistButton",
"sbDonate"
].forEach(id => PageElements[id] = document.getElementById(id));
@@ -131,6 +132,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
PageElements.submitUsername.addEventListener("click", submitUsername);
PageElements.optionsButton.addEventListener("click", openOptions);
PageElements.helpButton.addEventListener("click", openHelp);
PageElements.refreshSegmentsButton.addEventListener("click", refreshSegments);
/** If true, the content script is in the process of creating a new segment. */
let creatingSegment = false;
@@ -290,10 +292,12 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
if (request.found) {
PageElements.videoFound.innerHTML = chrome.i18n.getMessage("sponsorFound");
PageElements.refreshSegmentsButton.classList.remove("hidden");
displayDownloadedSponsorTimes(request);
} else {
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.className = "voteButton";
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
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)
*/

View File

@@ -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.
*/
@@ -243,8 +268,10 @@ export default class Utils {
}
getLocalizedMessage(text: string): string | false {
console.log(chrome.i18n.getMessage("forceChannelCheckPopup"));
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, "&#60;")
.replace(/"/g, "&quot;").replace(/\n/g, "<br/>") : "";
});
if(valNewH != text) {