Compare commits

..

3 Commits

Author SHA1 Message Date
Ajay Ramachandran
d97d45ae14 Remove some redundant checks 2021-07-06 01:39:05 -04:00
Ajay Ramachandran
b066945331 Use segment count from the API 2021-07-06 01:35:39 -04:00
Ajay Ramachandran
584cfd4ff8 Add lock button shown for VIPs 2021-07-06 01:33:06 -04:00
11 changed files with 242 additions and 204 deletions

1
.github/FUNDING.yml vendored
View File

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

View File

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

View File

@@ -669,6 +669,12 @@
"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. 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. 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>.
</p>
<p class="projectPreview">
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>.
Come contribute, make some suggestions and help out in the Discord: <a href="https://discord.gg/QnmVMpU">https://discord.gg/QnmVMpU</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, etc., enable them below.
You can also hide/show UI elements.
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.
</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 "Segment 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 "Sponsorship 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 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.
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.
</p>
<h1>Editing</h1>
@@ -74,21 +74,27 @@
<img src="https://i.imgur.com/DZHqbsx.gif">
</span>
If you messed up, you can edit or delete your segments in the popup or in the info menu (by hitting the info icon).
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).
</p>
<h1>This is too slow</h1>
<p>
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.
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.
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">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.
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.
</p>
<h1>News and how it is made</h1>
@@ -97,6 +103,12 @@
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/lock.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"><g fill="none"><path d="M0 0h24v24H0V0z"/><path d="M0 0h24v24H0V0z" opacity=".87"/></g><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM9 6c0-1.66 1.34-3 3-3s3 1.34 3 3v2H9V6zm9 14H6V10h12v10zm-6-3c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z"/></svg>

After

Width:  |  Height:  |  Size: 426 B

View File

@@ -18,9 +18,12 @@
<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__">
<button class="sbSlimButton" id="refreshSegmentsButton" title="__MSG_refreshSegments__">
<img id="refreshSegments" src="/icons/refresh.svg"/>
</button>
<button class="sbSlimButton hidden" id="lockSegmentsButton">
<img id="LockSegments" src="/icons/lock.svg"/>
</button>
</div>
<div id="issueReporterContainer">
<div id="issueReporterTimeButtons"></div>

View File

@@ -39,6 +39,7 @@ interface SBConfig {
testingServer: boolean,
refetchWhenNotFound: boolean,
ytInfoPermissionGranted: boolean,
askAboutUnlistedVideos: boolean,
allowExpirements: boolean,
autoHideInfoButton: boolean,
@@ -178,6 +179,7 @@ const Config: SBObject = {
testingServer: false,
refetchWhenNotFound: true,
ytInfoPermissionGranted: false,
askAboutUnlistedVideos: true,
allowExpirements: true,
autoHideInfoButton: true,
@@ -343,10 +345,6 @@ 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, VideoInfo, StorageChangesObject, ChannelIDInfo, ChannelIDStatus, SponsorSourceType } from "./types";
import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, FetchResponse, 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);
async function messageListener(request: Message, sender: unknown, sendResponse: (response: MessageResponse) => void): Promise<void> {
function messageListener(request: Message, sender: unknown, sendResponse: (response: MessageResponse) => void): void {
//messages from popup script
switch(request.message){
case "update":
@@ -179,8 +179,7 @@ async function messageListener(request: Message, sender: unknown, sendResponse:
submitSponsorTimes();
break;
case "refreshSegments":
await sponsorsLookup(sponsorVideoID, false);
sendResponse({});
sponsorsLookup(sponsorVideoID, false);
break;
}
}
@@ -241,10 +240,6 @@ function resetValues() {
// Reset advert playing flag
isAdPlaying = false;
for (let i = 0; i < skipNotices.length; i++) {
skipNotices.pop().close();
}
}
async function videoIDChange(id) {
@@ -282,6 +277,9 @@ 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) {
@@ -596,81 +594,81 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) {
// Check for hashPrefix setting
const hashPrefix = (await utils.getHash(id, 1)).substr(0, 4);
const response = await utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
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;
}
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
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;
// 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) {
// If they downvoted it, or changed the category, keep it
otherSegment.hidden = segment.hidden;
otherSegment.category = segment.category;
}
}
}
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();
return;
}
} else if (sponsorLookupRetries < 15 && !recheckStarted) {
recheckStarted = true;
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]);
//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++;
}
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;
}
}
}
if (keepOldSubmissions) {
for (const segment of oldSegments) {
const otherSegment = sponsorTimes.find((other) => segment.UUID === other.UUID);
if (otherSegment) {
// If they downvoted it, or changed the category, keep it
otherSegment.hidden = segment.hidden;
otherSegment.category = segment.category;
}
}
}
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 {
@@ -854,7 +852,7 @@ async function whitelistCheck() {
channelIDInfo = {
status: ChannelIDStatus.Found,
id: getChannelID().match(/^\/?([^\s/]+)/)[0]
id: getChannelID()
}
} catch (e) {
channelIDInfo = {
@@ -866,8 +864,7 @@ async function whitelistCheck() {
}
//see if this is a whitelisted channel
if (whitelistedChannels != undefined &&
channelIDInfo.status === ChannelIDStatus.Found && whitelistedChannels.includes(channelIDInfo.id)) {
if (whitelistedChannels != undefined && whitelistedChannels.includes(getChannelID())) {
channelWhitelisted = true;
}
@@ -875,6 +872,66 @@ 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
*/
@@ -1355,11 +1412,13 @@ function openInfoMenu() {
const edit = <HTMLImageElement> popup.querySelector("#sbPopupIconEdit");
const check = <HTMLImageElement> popup.querySelector("#sbPopupIconCheck");
const refreshSegments = <HTMLImageElement> popup.querySelector("#refreshSegments");
const lockSegments = <HTMLImageElement> popup.querySelector("#lockSegments");
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");
refreshSegments.src = chrome.extension.getURL("icons/refresh.svg");
lockSegments.src = chrome.extension.getURL("icons/lock.svg");
parentNode.insertBefore(popup, parentNode.firstChild);
@@ -1499,7 +1558,7 @@ function submitSponsorTimes() {
async function sendSubmitMessage() {
// Add loading animation
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker.svg");
const stopAnimation = utils.applyLoadingAnimation(playerButtons.submit.button, 1, () => updateEditButtonsOnPlayer());
playerButtons.submit.button.style.animation = "rotate 1s 0s infinite";
//check if a sponsor exceeds the duration of the video
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
@@ -1531,7 +1590,22 @@ async function sendSubmitMessage() {
});
if (response.status === 200) {
stopAnimation();
// 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);
// Remove segments from storage since they've already been submitted
Config.config.segmentTimes.delete(sponsorVideoID);

View File

@@ -53,6 +53,5 @@ export type MessageResponse =
| GetVideoIdResponse
| GetChannelIDResponse
| SponsorStartResponse
| IsChannelWhitelistedResponse
| Record<string, never>;
| IsChannelWhitelistedResponse;

View File

@@ -103,6 +103,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
"sponsorMessageTimes",
//"downloadedSponsorMessageTimes",
"refreshSegmentsButton",
"lockSegmentsButton",
"whitelistButton",
"sbDonate"
].forEach(id => PageElements[id] = document.getElementById(id));
@@ -133,6 +134,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
PageElements.optionsButton.addEventListener("click", openOptions);
PageElements.helpButton.addEventListener("click", openHelp);
PageElements.refreshSegmentsButton.addEventListener("click", refreshSegments);
PageElements.lockSegmentsButton.addEventListener("click", lockSegments);
/** If true, the content script is in the process of creating a new segment. */
let creatingSegment = false;
@@ -144,93 +146,68 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
let currentVideoID = null;
//show proper disable skipping button
const disableSkipping = Config.config.disableSkipping;
if (disableSkipping != undefined && disableSkipping) {
if (Config.config.disableSkipping) {
PageElements.disableSkipping.style.display = "none";
PageElements.enableSkipping.style.display = "unset";
PageElements.toggleSwitch.checked = false;
}
//if the don't show notice again variable is true, an option to
// disable should be available
const dontShowNotice = Config.config.dontShowNotice;
if (dontShowNotice != undefined && dontShowNotice) {
if (Config.config.dontShowNotice) {
PageElements.showNoticeAgain.style.display = "unset";
}
utils.sendRequestToServer("GET", "/api/getUsername?userID=" + Config.config.userID, (res) => {
if (res.status === 200) {
PageElements.usernameValue.innerText = JSON.parse(res.responseText).userName
}
})
const userInfoRes = await utils.asyncRequestToServer("GET", "/api/userInfo?userID=" + Config.config.userID);
if (userInfoRes.ok) {
const userInfo = JSON.parse(userInfoRes.responseText);
//get the amount of times this user has contributed and display it to thank them
if (Config.config.sponsorTimesContributed != undefined) {
PageElements.sponsorTimesContributionsDisplay.innerText = Config.config.sponsorTimesContributed.toLocaleString();
if (userInfo.vip) PageElements.lockSegmentsButton.classList.remove("hidden");
PageElements.usernameValue.innerText = userInfo.userName;
PageElements.sponsorTimesContributionsDisplay.innerText = userInfo.segmentCount.toLocaleString();
PageElements.sponsorTimesContributionsContainer.classList.remove("hidden");
//get the userID
const userID = Config.config.userID;
if (userID != undefined) {
//there are probably some views on these submissions then
//get the amount of views from the sponsors submitted
utils.sendRequestToServer("GET", "/api/getViewsForUser?userID=" + userID, function(response) {
if (response.status == 200) {
const viewCount = JSON.parse(response.responseText).viewCount;
if (viewCount != 0) {
if (viewCount > 1) {
PageElements.sponsorTimesViewsDisplayEndWord.innerText = chrome.i18n.getMessage("Segments");
} else {
PageElements.sponsorTimesViewsDisplayEndWord.innerText = chrome.i18n.getMessage("Segment");
}
const viewCount = userInfo.viewCount;
if (viewCount != 0) {
if (viewCount > 1) {
PageElements.sponsorTimesViewsDisplayEndWord.innerText = chrome.i18n.getMessage("Segments");
} else {
PageElements.sponsorTimesViewsDisplayEndWord.innerText = chrome.i18n.getMessage("Segment");
}
PageElements.sponsorTimesViewsDisplay.innerText = viewCount.toLocaleString();
PageElements.sponsorTimesViewsContainer.style.display = "unset";
}
}
});
PageElements.sponsorTimesViewsDisplay.innerText = viewCount.toLocaleString();
PageElements.sponsorTimesViewsContainer.style.display = "unset";
}
//get this time in minutes
utils.sendRequestToServer("GET", "/api/getSavedTimeForUser?userID=" + userID, function(response) {
if (response.status == 200) {
const minutesSaved = JSON.parse(response.responseText).timeSaved;
if (minutesSaved != 0) {
if (minutesSaved != 1) {
PageElements.sponsorTimesOthersTimeSavedEndWord.innerText = chrome.i18n.getMessage("minsLower");
} else {
PageElements.sponsorTimesOthersTimeSavedEndWord.innerText = chrome.i18n.getMessage("minLower");
}
const minutesSaved = userInfo.minutesSaved;
if (minutesSaved != 0) {
if (minutesSaved != 1) {
PageElements.sponsorTimesOthersTimeSavedEndWord.innerText = chrome.i18n.getMessage("minsLower");
} else {
PageElements.sponsorTimesOthersTimeSavedEndWord.innerText = chrome.i18n.getMessage("minLower");
}
PageElements.sponsorTimesOthersTimeSavedDisplay.innerText = getFormattedHours(minutesSaved);
}
}
});
PageElements.sponsorTimesOthersTimeSavedDisplay.innerText = getFormattedHours(minutesSaved);
}
}
//get the amount of times this user has skipped a sponsor
if (Config.config.skipCount != undefined) {
if (Config.config.skipCount != 1) {
PageElements.sponsorTimesSkipsDoneEndWord.innerText = chrome.i18n.getMessage("Segments");
} else {
PageElements.sponsorTimesSkipsDoneEndWord.innerText = chrome.i18n.getMessage("Segment");
}
PageElements.sponsorTimesSkipsDoneDisplay.innerText = Config.config.skipCount.toLocaleString();
PageElements.sponsorTimesSkipsDoneContainer.style.display = "unset";
if (Config.config.skipCount != 1) {
PageElements.sponsorTimesSkipsDoneEndWord.innerText = chrome.i18n.getMessage("Segments");
} else {
PageElements.sponsorTimesSkipsDoneEndWord.innerText = chrome.i18n.getMessage("Segment");
}
//get the amount of time this user has saved.
if (Config.config.minutesSaved != undefined) {
if (Config.config.minutesSaved != 1) {
PageElements.sponsorTimeSavedEndWord.innerText = chrome.i18n.getMessage("minsLower");
} else {
PageElements.sponsorTimeSavedEndWord.innerText = chrome.i18n.getMessage("minLower");
}
PageElements.sponsorTimesSkipsDoneDisplay.innerText = Config.config.skipCount.toLocaleString();
PageElements.sponsorTimesSkipsDoneContainer.style.display = "unset";
PageElements.sponsorTimeSavedDisplay.innerText = getFormattedHours(Config.config.minutesSaved);
if (Config.config.minutesSaved != 1) {
PageElements.sponsorTimeSavedEndWord.innerText = chrome.i18n.getMessage("minsLower");
} else {
PageElements.sponsorTimeSavedEndWord.innerText = chrome.i18n.getMessage("minLower");
}
PageElements.sponsorTimeSavedDisplay.innerText = getFormattedHours(Config.config.minutesSaved);
// Must be delayed so it only happens once loaded
setTimeout(() => PageElements.sponsorblockPopup.classList.remove("preload"), 250);
@@ -292,12 +269,10 @@ 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");
}
}
@@ -433,11 +408,7 @@ 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);
const stopAnimation = utils.applyLoadingAnimation(uuidButton, 0.3);
stopAnimation();
});
uuidButton.addEventListener("click", () => navigator.clipboard.writeText(UUID));
//add thumbs up, thumbs down and uuid copy buttons to the container
voteButtonsContainer.appendChild(upvoteButton);
@@ -685,20 +656,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()
{message: 'refreshSegments'}
)}
);
}
function lockSegments() {
alert("lock");
}
/**
* Should skipping be disabled (visuals stay)
*/

View File

@@ -158,31 +158,6 @@ 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.
*/
@@ -268,10 +243,8 @@ 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(/</g, "&#60;")
.replace(/"/g, "&quot;").replace(/\n/g, "<br/>") : "";
return v1 ? chrome.i18n.getMessage(v1).replace("\n", "<br/>") : "";
});
if(valNewH != text) {