mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-09 13:07:05 +03:00
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
"serverAddress": "https://sponsor.ajay.app",
|
"serverAddress": "https://sponsor.ajay.app",
|
||||||
"serverAddressComment": "This specifies the default SponsorBlock server to conect to"
|
"testingServerAddress": "https://sponsor.ajay.app/test",
|
||||||
|
"serverAddressComment": "This specifies the default SponsorBlock server to conect to",
|
||||||
|
"categoryList": ["sponsor", "intro", "outro", "interaction", "selfpromo", "offtopic"]
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "__MSG_fullName__",
|
"name": "__MSG_fullName__",
|
||||||
"short_name": "__MSG_Name__",
|
"short_name": "__MSG_Name__",
|
||||||
"version": "1.2.24",
|
"version": "1.2.25",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"description": "__MSG_Description__",
|
"description": "__MSG_Description__",
|
||||||
"content_scripts": [{
|
"content_scripts": [{
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
"icons/downvote.png",
|
"icons/downvote.png",
|
||||||
"icons/report.png",
|
"icons/report.png",
|
||||||
"icons/close.png",
|
"icons/close.png",
|
||||||
|
"icons/beep.ogg",
|
||||||
"icons/PlayerInfoIconSponsorBlocker256px.png",
|
"icons/PlayerInfoIconSponsorBlocker256px.png",
|
||||||
"icons/PlayerDeleteIconSponsorBlocker256px.png",
|
"icons/PlayerDeleteIconSponsorBlocker256px.png",
|
||||||
"popup.html",
|
"popup.html",
|
||||||
|
|||||||
925
package-lock.json
generated
925
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -4,7 +4,15 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "background.js",
|
"main": "background.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"concurrently": "^5.1.0"
|
"@types/react": "^16.9.22",
|
||||||
|
"@types/react-dom": "^16.9.5",
|
||||||
|
"babel": "^6.23.0",
|
||||||
|
"babel-core": "^6.26.3",
|
||||||
|
"babel-loader": "^8.0.6",
|
||||||
|
"babel-preset-env": "^1.7.0",
|
||||||
|
"concurrently": "^5.1.0",
|
||||||
|
"react": "^16.12.0",
|
||||||
|
"react-dom": "^16.12.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"web-ext": "^4.0.0",
|
"web-ext": "^4.0.0",
|
||||||
|
|||||||
@@ -291,6 +291,12 @@
|
|||||||
"autoSkipDescription": {
|
"autoSkipDescription": {
|
||||||
"message": "Auto skip will skip sponsors for you. If disabled, a notice will appear asking if you'd like to skip."
|
"message": "Auto skip will skip sponsors for you. If disabled, a notice will appear asking if you'd like to skip."
|
||||||
},
|
},
|
||||||
|
"audioNotification": {
|
||||||
|
"message": "Audio Notification On Skip"
|
||||||
|
},
|
||||||
|
"audioNotificationDescription": {
|
||||||
|
"message": "Audio notification on skip will play a sound whenever a sponsor is skipped. If disabled (or auto skip is disabled), no sound will be played."
|
||||||
|
},
|
||||||
"youHaveSkipped": {
|
"youHaveSkipped": {
|
||||||
"message": "You have skipped "
|
"message": "You have skipped "
|
||||||
},
|
},
|
||||||
@@ -441,6 +447,24 @@
|
|||||||
"incorrectlyFormattedOptions": {
|
"incorrectlyFormattedOptions": {
|
||||||
"message": "This JSON is not formatted correctly. Your options have not been changed."
|
"message": "This JSON is not formatted correctly. Your options have not been changed."
|
||||||
},
|
},
|
||||||
|
"confirmNoticeTitle" : {
|
||||||
|
"message": "Submit Segment"
|
||||||
|
},
|
||||||
|
"submit": {
|
||||||
|
"message": "Submit"
|
||||||
|
},
|
||||||
|
"cancel": {
|
||||||
|
"message": "Cancel"
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"message": "Delete"
|
||||||
|
},
|
||||||
|
"preview": {
|
||||||
|
"message": "Preview"
|
||||||
|
},
|
||||||
|
"edit": {
|
||||||
|
"message": "Edit"
|
||||||
|
},
|
||||||
"copyDebugInformation": {
|
"copyDebugInformation": {
|
||||||
"message": "Copy Debug Information To Clipboard"
|
"message": "Copy Debug Information To Clipboard"
|
||||||
},
|
},
|
||||||
@@ -461,5 +485,48 @@
|
|||||||
},
|
},
|
||||||
"keyAlreadyUsed": {
|
"keyAlreadyUsed": {
|
||||||
"message": "is bound to another action. Please select another key."
|
"message": "is bound to another action. Please select another key."
|
||||||
|
},
|
||||||
|
"to": {
|
||||||
|
"message": "to",
|
||||||
|
"description": "Used between sponsor times. Example: 1:20 to 1:30"
|
||||||
|
},
|
||||||
|
"category_sponsor": {
|
||||||
|
"message": "Sponsor"
|
||||||
|
},
|
||||||
|
"category_intro": {
|
||||||
|
"message": "Intro"
|
||||||
|
},
|
||||||
|
"category_outro": {
|
||||||
|
"message": "Outro"
|
||||||
|
},
|
||||||
|
"category_interaction": {
|
||||||
|
"message": "Interaction (Redundant Like, Subscribe, Follow, etc.)"
|
||||||
|
},
|
||||||
|
"category_selfpromo": {
|
||||||
|
"message": "Self-Promotion and Merchandise"
|
||||||
|
},
|
||||||
|
"category_offtopic": {
|
||||||
|
"message": "Offtopic tangent (Subjective)"
|
||||||
|
},
|
||||||
|
"disable": {
|
||||||
|
"message": "Disable"
|
||||||
|
},
|
||||||
|
"manualSkip": {
|
||||||
|
"message": "Manual Skip"
|
||||||
|
},
|
||||||
|
"showOverlay": {
|
||||||
|
"message": "Show Overlay On Player"
|
||||||
|
},
|
||||||
|
"enableTestingServer": {
|
||||||
|
"message": "Enable Beta Testing Server"
|
||||||
|
},
|
||||||
|
"whatEnableTestingServer": {
|
||||||
|
"message": "Your submissions and votes WILL NOT COUNT towards the main server. Only use this for testing."
|
||||||
|
},
|
||||||
|
"testingServerWarning": {
|
||||||
|
"message": "All submissions and votes WILL NOT COUNT towards the main server while connecting to the test server. Make sure to disable this when you want to make real submissions."
|
||||||
|
},
|
||||||
|
"bracketNow": {
|
||||||
|
"message": "(Now)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,13 +80,15 @@
|
|||||||
|
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
||||||
animation: fadeIn 0.5s;
|
|
||||||
|
|
||||||
border-spacing: 5px 10px;
|
border-spacing: 5px 10px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sponsorSkipNoticeFadeIn {
|
||||||
|
animation: fadeIn 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
.sponsorSkipNoticeFadeOut {
|
.sponsorSkipNoticeFadeOut {
|
||||||
animation: fadeOut 3s cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
animation: fadeOut 3s cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||||
}
|
}
|
||||||
@@ -312,3 +314,58 @@
|
|||||||
position:relative;
|
position:relative;
|
||||||
top:1px;
|
top:1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Submission Notice */
|
||||||
|
|
||||||
|
.sponsorTimeDisplay {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsorTimeEditButton {
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-right: 20px;
|
||||||
|
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsorTimeEdit > input::-webkit-outer-spin-button,
|
||||||
|
input::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsorTimeEdit {
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsorTimeEditMinutes {
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsorTimeEditSeconds {
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsorNowButton {
|
||||||
|
font-size: 11px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsorTimeCategories {
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
|
||||||
|
background-color: rgba(28, 28, 28, 0.9);
|
||||||
|
border-color: rgb(130,0,0,0.9);
|
||||||
|
color: white;
|
||||||
|
border-width: 3px;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
BIN
public/icons/beep.ogg
Normal file
BIN
public/icons/beep.ogg
Normal file
Binary file not shown.
@@ -324,3 +324,26 @@ svg {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* React styles */
|
||||||
|
|
||||||
|
.categoryTableElement {
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryTableElement > * {
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryOptionsSelector {
|
||||||
|
background-color: #c00000;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
@@ -24,6 +24,13 @@
|
|||||||
|
|
||||||
<div id="options" class="hidden">
|
<div id="options" class="hidden">
|
||||||
|
|
||||||
|
<div id="category-type" option-type="react-CategoryChooserComponent">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
<div id="support-invidious" option-type="toggle" sync-option="supportInvidious">
|
<div id="support-invidious" option-type="toggle" sync-option="supportInvidious">
|
||||||
<label class="switch-container" label-name="__MSG_supportInvidious__">
|
<label class="switch-container" label-name="__MSG_supportInvidious__">
|
||||||
<label class="switch">
|
<label class="switch">
|
||||||
@@ -77,24 +84,6 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<div option-type="toggle" toggle-type="reverse" sync-option="disableAutoSkip">
|
|
||||||
<label class="switch-container" label-name="__MSG_autoSkip__">
|
|
||||||
<label class="switch">
|
|
||||||
<input type="checkbox" checked>
|
|
||||||
<span class="slider round"></span>
|
|
||||||
</label>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
<div class="small-description">__MSG_autoSkipDescription__</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
|
|
||||||
<div option-type="keybind-change" sync-option="startSponsorKeybind">
|
<div option-type="keybind-change" sync-option="startSponsorKeybind">
|
||||||
<div class="option-button trigger-button">
|
<div class="option-button trigger-button">
|
||||||
__MSG_setStartSponsorShortcut__
|
__MSG_setStartSponsorShortcut__
|
||||||
@@ -231,6 +220,23 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
<div option-type="toggle" sync-option="audioNotificationOnSkip">
|
||||||
|
<label class="switch-container" label-name="__MSG_audioNotification__">
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" checked>
|
||||||
|
<span class="slider round"></span>
|
||||||
|
</label>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<div class="small-description">__MSG_audioNotificationDescription__</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
<div option-type="toggle" sync-option="autoUpvote">
|
<div option-type="toggle" sync-option="autoUpvote">
|
||||||
<label class="switch-container" label-name="__MSG_enableAutoUpvote__">
|
<label class="switch-container" label-name="__MSG_enableAutoUpvote__">
|
||||||
<label class="switch">
|
<label class="switch">
|
||||||
@@ -347,6 +353,22 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
<div option-type="toggle" sync-option="testingServer" confirm-message="testingServerWarning">
|
||||||
|
<label class="switch-container" label-name="__MSG_enableTestingServer__">
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox">
|
||||||
|
<span class="slider round"></span>
|
||||||
|
</label>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<div class="small-description">__MSG_whatEnableTestingServer__</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
<div option-type="text-change" sync-option="serverAddress">
|
<div option-type="text-change" sync-option="serverAddress">
|
||||||
<label class="text-label-container">
|
<label class="text-label-container">
|
||||||
|
|||||||
@@ -39,29 +39,15 @@
|
|||||||
|
|
||||||
<div id="submissionSection" class="popupElement" style="display: none">
|
<div id="submissionSection" class="popupElement" style="display: none">
|
||||||
<h3 class="popupElement">__MSG_lastTimes__</h3>
|
<h3 class="popupElement">__MSG_lastTimes__</h3>
|
||||||
<b>
|
|
||||||
<div id="sponsorMessageTimes" class="popupElement">
|
|
||||||
|
|
||||||
</div>
|
<b>Sponsor Editing has been moved and will appear after you click submit</b>
|
||||||
</b>
|
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<button id="clearTimes" class="smallButton popupElement">__MSG_clearTimesButton__</button>
|
|
||||||
|
|
||||||
<div id="submitTimesContainer" class="popupElement" style="display: none">
|
<div id="submitTimesContainer" class="popupElement" style="display: none">
|
||||||
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
<button id="submitTimes" class="smallButton popupElement">__MSG_submitTimesButton__</button>
|
<button id="submitTimes" class="smallButton popupElement">__MSG_submitTimesButton__</button>
|
||||||
|
|
||||||
<div id="submitTimesInfoMessageContainer" class="popupElement" style="display: none">
|
|
||||||
<h3 id="submitTimesInfoMessage" class="popupElement">
|
|
||||||
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -184,13 +184,13 @@ function submitVote(type, UUID, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitTimes(videoID, callback) {
|
async function submitTimes(videoID: string, callback) {
|
||||||
//get the video times from storage
|
//get the video times from storage
|
||||||
let sponsorTimes = Config.config.sponsorTimes.get(videoID);
|
let sponsorTimes = Config.config.sponsorTimes.get(videoID);
|
||||||
let userID = Config.config.userID;
|
let userID = Config.config.userID;
|
||||||
|
|
||||||
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
|
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
|
||||||
let durationResult = <Types.videoDurationResponse> await new Promise((resolve, reject) => {
|
let durationResult = <Types.VideoDurationResponse> await new Promise((resolve, reject) => {
|
||||||
chrome.tabs.query({
|
chrome.tabs.query({
|
||||||
active: true,
|
active: true,
|
||||||
currentWindow: true
|
currentWindow: true
|
||||||
@@ -218,21 +218,24 @@ async function submitTimes(videoID, callback) {
|
|||||||
+ "&userID=" + userID, function(xmlhttp, error) {
|
+ "&userID=" + userID, function(xmlhttp, error) {
|
||||||
if (xmlhttp.readyState == 4 && !error) {
|
if (xmlhttp.readyState == 4 && !error) {
|
||||||
callback({
|
callback({
|
||||||
statusCode: xmlhttp.status
|
statusCode: xmlhttp.status,
|
||||||
|
responseText: xmlhttp.responseText
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (xmlhttp.status == 200) {
|
if (xmlhttp.status == 200) {
|
||||||
//save the amount contributed
|
//save the amount contributed
|
||||||
if (!increasedContributionAmount) {
|
if (!increasedContributionAmount) {
|
||||||
increasedContributionAmount = true;
|
increasedContributionAmount = true;
|
||||||
Config.config.sponsorTimesContributed = Config.config.sponsorTimesContributed + sponsorTimes.length;
|
Config.config.sponsorTimesContributed = Config.config.sponsorTimesContributed + sponsorTimes.length;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if (error) {
|
} else if (error) {
|
||||||
callback({
|
callback({
|
||||||
statusCode: -1
|
statusCode: -1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
53
src/components/CategoryChooserComponent.tsx
Normal file
53
src/components/CategoryChooserComponent.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import Config from "../config"
|
||||||
|
import * as CompileConfig from "../../config.json";
|
||||||
|
import CategorySkipOptionsComponent from "./CategorySkipOptionsComponent";
|
||||||
|
|
||||||
|
export interface CategoryChooserProps {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CategoryChooserState {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class CategoryChooserComponent extends React.Component<CategoryChooserProps, CategoryChooserState> {
|
||||||
|
|
||||||
|
constructor(props: CategoryChooserProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
// Setup state
|
||||||
|
this.state = {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<table id="categoryChooserTable"
|
||||||
|
className="categoryChooserTable">
|
||||||
|
<tbody>
|
||||||
|
{this.getCategorySkipOptions()}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCategorySkipOptions(): JSX.Element[] {
|
||||||
|
let elements: JSX.Element[] = [];
|
||||||
|
|
||||||
|
for (const category of CompileConfig.categoryList) {
|
||||||
|
elements.push(
|
||||||
|
<CategorySkipOptionsComponent category={category}
|
||||||
|
defaultColor={"00d400"}
|
||||||
|
key={category}>
|
||||||
|
</CategorySkipOptionsComponent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CategoryChooserComponent;
|
||||||
130
src/components/CategorySkipOptionsComponent.tsx
Normal file
130
src/components/CategorySkipOptionsComponent.tsx
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import Config from "../config"
|
||||||
|
import { CategorySkipOption } from "../types";
|
||||||
|
|
||||||
|
export interface CategorySkipOptionsProps {
|
||||||
|
category: string;
|
||||||
|
defaultColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CategorySkipOptionsState {
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsProps, CategorySkipOptionsState> {
|
||||||
|
|
||||||
|
constructor(props: CategorySkipOptionsProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
// Setup state
|
||||||
|
this.state = {
|
||||||
|
color: props.defaultColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let defaultOption = "disable";
|
||||||
|
// Set the default opton properly
|
||||||
|
for (const categorySelection of Config.config.categorySelections) {
|
||||||
|
if (categorySelection.name === this.props.category) {
|
||||||
|
switch (categorySelection.option) {
|
||||||
|
case CategorySkipOption.ShowOverlay:
|
||||||
|
defaultOption = "showOverlay";
|
||||||
|
break;
|
||||||
|
case CategorySkipOption.ManualSkip:
|
||||||
|
defaultOption = "manualSkip";
|
||||||
|
break;
|
||||||
|
case CategorySkipOption.AutoSkip:
|
||||||
|
defaultOption = "autoSkip";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr id={this.props.category + "OptionsRow"}
|
||||||
|
className="categoryTableElement">
|
||||||
|
<td id={this.props.category + "OptionName"}
|
||||||
|
className="categoryTableLabel">
|
||||||
|
{chrome.i18n.getMessage("category_" + this.props.category)}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td id={this.props.category + "SkipOption"}>
|
||||||
|
<select
|
||||||
|
className="categoryOptionsSelector"
|
||||||
|
defaultValue={defaultOption}
|
||||||
|
onChange={this.skipOptionSelected.bind(this)}>
|
||||||
|
{this.getCategorySkipOptions()}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* TODO: Add colour chooser */}
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
skipOptionSelected(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||||
|
let option: CategorySkipOption;
|
||||||
|
|
||||||
|
this.removeCurrentCategorySelection();
|
||||||
|
|
||||||
|
switch (event.target.value) {
|
||||||
|
case "disable":
|
||||||
|
return;
|
||||||
|
case "showOverlay":
|
||||||
|
option = CategorySkipOption.ShowOverlay;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "manualSkip":
|
||||||
|
option = CategorySkipOption.ManualSkip;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "autoSkip":
|
||||||
|
option = CategorySkipOption.AutoSkip;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.config.categorySelections.push({
|
||||||
|
name: this.props.category,
|
||||||
|
option: option
|
||||||
|
});
|
||||||
|
|
||||||
|
// Forces the Proxy to send this to the chrome storage API
|
||||||
|
Config.config.categorySelections = Config.config.categorySelections;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Removes this category from the config list of category selections */
|
||||||
|
removeCurrentCategorySelection(): void {
|
||||||
|
// Remove it if it exists
|
||||||
|
for (let i = 0; i < Config.config.categorySelections.length; i++) {
|
||||||
|
if (Config.config.categorySelections[i].name === this.props.category) {
|
||||||
|
Config.config.categorySelections.splice(i, 1);
|
||||||
|
|
||||||
|
// Forces the Proxy to send this to the chrome storage API
|
||||||
|
Config.config.categorySelections = Config.config.categorySelections;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCategorySkipOptions(): JSX.Element[] {
|
||||||
|
let elements: JSX.Element[] = [];
|
||||||
|
""
|
||||||
|
let optionNames = ["disable", "showOverlay", "manualSkip", "autoSkip"];
|
||||||
|
|
||||||
|
for (const optionName of optionNames) {
|
||||||
|
elements.push(
|
||||||
|
<option key={optionName} value={optionName}>
|
||||||
|
{chrome.i18n.getMessage(optionName)}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CategorySkipOptionsComponent;
|
||||||
252
src/components/NoticeComponent.tsx
Normal file
252
src/components/NoticeComponent.tsx
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
export interface NoticeProps {
|
||||||
|
noticeTitle: string,
|
||||||
|
|
||||||
|
maxCountdownTime?: () => number,
|
||||||
|
amountOfPreviousNotices?: number,
|
||||||
|
timed?: boolean,
|
||||||
|
idSuffix?: string,
|
||||||
|
|
||||||
|
fadeIn?: boolean,
|
||||||
|
|
||||||
|
// Callback for when this is closed
|
||||||
|
closeListener?: () => void,
|
||||||
|
|
||||||
|
zIndex?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NoticeState {
|
||||||
|
noticeTitle: string,
|
||||||
|
|
||||||
|
maxCountdownTime?: () => number,
|
||||||
|
|
||||||
|
countdownTime: number,
|
||||||
|
countdownText: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
||||||
|
countdownInterval: NodeJS.Timeout;
|
||||||
|
idSuffix: any;
|
||||||
|
|
||||||
|
amountOfPreviousNotices: number;
|
||||||
|
|
||||||
|
constructor(props: NoticeProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
let maxCountdownTime = () => {
|
||||||
|
if (this.props.maxCountdownTime) return this.props.maxCountdownTime();
|
||||||
|
else return 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
//the id for the setInterval running the countdown
|
||||||
|
this.countdownInterval = null;
|
||||||
|
|
||||||
|
this.amountOfPreviousNotices = props.amountOfPreviousNotices || 0;
|
||||||
|
|
||||||
|
this.idSuffix = props.idSuffix || "";
|
||||||
|
|
||||||
|
// Setup state
|
||||||
|
this.state = {
|
||||||
|
noticeTitle: props.noticeTitle,
|
||||||
|
|
||||||
|
maxCountdownTime,
|
||||||
|
|
||||||
|
//the countdown until this notice closes
|
||||||
|
countdownTime: maxCountdownTime(),
|
||||||
|
countdownText: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.startCountdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let noticeStyle: React.CSSProperties = {
|
||||||
|
zIndex: this.props.zIndex || (50 + this.amountOfPreviousNotices)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<table id={"sponsorSkipNotice" + this.idSuffix}
|
||||||
|
className={"sponsorSkipObject sponsorSkipNotice" + (this.props.fadeIn ? " sponsorSkipNoticeFadeIn" : "")}
|
||||||
|
style={noticeStyle}
|
||||||
|
onMouseEnter={this.pauseCountdown.bind(this)}
|
||||||
|
onMouseLeave={this.startCountdown.bind(this)}>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
{/* First row */}
|
||||||
|
<tr id={"sponsorSkipNoticeFirstRow" + this.idSuffix}>
|
||||||
|
{/* Left column */}
|
||||||
|
<td>
|
||||||
|
{/* Logo */}
|
||||||
|
<img id={"sponsorSkipLogo" + this.idSuffix}
|
||||||
|
className="sponsorSkipLogo sponsorSkipObject"
|
||||||
|
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}>
|
||||||
|
</img>
|
||||||
|
|
||||||
|
<span id={"sponsorSkipMessage" + this.idSuffix}
|
||||||
|
className="sponsorSkipMessage sponsorSkipObject">
|
||||||
|
|
||||||
|
{this.state.noticeTitle}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* Right column */}
|
||||||
|
<td className="sponsorSkipNoticeRightSection"
|
||||||
|
style={{top: "11px"}}>
|
||||||
|
|
||||||
|
{/* Time left */}
|
||||||
|
{this.props.timed ? (
|
||||||
|
<span id={"sponsorSkipNoticeTimeLeft" + this.idSuffix}
|
||||||
|
className="sponsorSkipObject sponsorSkipNoticeTimeLeft">
|
||||||
|
|
||||||
|
{this.state.countdownText || (this.state.countdownTime + "s")}
|
||||||
|
</span>
|
||||||
|
) : ""}
|
||||||
|
|
||||||
|
|
||||||
|
{/* Close button */}
|
||||||
|
<img src={chrome.extension.getURL("icons/close.png")}
|
||||||
|
className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeCloseButton sponsorSkipNoticeRightButton"
|
||||||
|
onClick={() => this.close()}>
|
||||||
|
</img>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{this.props.children}
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//called every second to lower the countdown before hiding the notice
|
||||||
|
countdown() {
|
||||||
|
if (!this.props.timed) return;
|
||||||
|
|
||||||
|
let countdownTime = this.state.countdownTime - 1;
|
||||||
|
|
||||||
|
if (countdownTime <= 0) {
|
||||||
|
//remove this from setInterval
|
||||||
|
clearInterval(this.countdownInterval);
|
||||||
|
|
||||||
|
//time to close this notice
|
||||||
|
this.close();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countdownTime == 3) {
|
||||||
|
//start fade out animation
|
||||||
|
let notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
|
||||||
|
notice.style.removeProperty("animation");
|
||||||
|
notice.classList.add("sponsorSkipNoticeFadeOut");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
countdownTime
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pauseCountdown() {
|
||||||
|
if (!this.props.timed) return;
|
||||||
|
|
||||||
|
//remove setInterval
|
||||||
|
clearInterval(this.countdownInterval);
|
||||||
|
this.countdownInterval = null;
|
||||||
|
|
||||||
|
//reset countdown and inform the user
|
||||||
|
this.setState({
|
||||||
|
countdownTime: this.state.maxCountdownTime(),
|
||||||
|
countdownText: chrome.i18n.getMessage("paused")
|
||||||
|
});
|
||||||
|
|
||||||
|
//remove the fade out class if it exists
|
||||||
|
let notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
|
||||||
|
notice.classList.remove("sponsorSkipNoticeFadeOut");
|
||||||
|
notice.style.animation = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
startCountdown() {
|
||||||
|
if (!this.props.timed) return;
|
||||||
|
|
||||||
|
//if it has already started, don't start it again
|
||||||
|
if (this.countdownInterval !== null) return;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
countdownTime: this.state.maxCountdownTime(),
|
||||||
|
countdownText: null
|
||||||
|
});
|
||||||
|
|
||||||
|
this.countdownInterval = setInterval(this.countdown.bind(this), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetCountdown() {
|
||||||
|
if (!this.props.timed) return;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
countdownTime: this.state.maxCountdownTime(),
|
||||||
|
countdownText: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param silent If true, the close listener will not be called
|
||||||
|
*/
|
||||||
|
close(silent?: boolean) {
|
||||||
|
//TODO: Change to a listener in the renderer (not component)
|
||||||
|
let notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
|
||||||
|
if (notice != null) {
|
||||||
|
notice.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove setInterval
|
||||||
|
if (this.countdownInterval !== null) clearInterval(this.countdownInterval);
|
||||||
|
|
||||||
|
if (this.props.closeListener && !silent) this.props.closeListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeNoticeTitle(title) {
|
||||||
|
this.setState({
|
||||||
|
noticeTitle: title
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addNoticeInfoMessage(message: string, message2: string = "") {
|
||||||
|
//TODO: Replace
|
||||||
|
|
||||||
|
let previousInfoMessage = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix);
|
||||||
|
if (previousInfoMessage != null) {
|
||||||
|
//remove it
|
||||||
|
document.getElementById("sponsorSkipNotice" + this.idSuffix).removeChild(previousInfoMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
let previousInfoMessage2 = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix + "2");
|
||||||
|
if (previousInfoMessage2 != null) {
|
||||||
|
//remove it
|
||||||
|
document.getElementById("sponsorSkipNotice" + this.idSuffix).removeChild(previousInfoMessage2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//add info
|
||||||
|
let thanksForVotingText = document.createElement("p");
|
||||||
|
thanksForVotingText.id = "sponsorTimesInfoMessage" + this.idSuffix;
|
||||||
|
thanksForVotingText.className = "sponsorTimesInfoMessage";
|
||||||
|
thanksForVotingText.innerText = message;
|
||||||
|
|
||||||
|
//add element to div
|
||||||
|
document.querySelector("#sponsorSkipNotice" + this.idSuffix + " > tbody").insertBefore(thanksForVotingText, document.getElementById("sponsorSkipNoticeSpacer" + this.idSuffix));
|
||||||
|
|
||||||
|
if (message2 !== undefined) {
|
||||||
|
let thanksForVotingText2 = document.createElement("p");
|
||||||
|
thanksForVotingText2.id = "sponsorTimesInfoMessage" + this.idSuffix + "2";
|
||||||
|
thanksForVotingText2.className = "sponsorTimesInfoMessage";
|
||||||
|
thanksForVotingText2.innerText = message2;
|
||||||
|
|
||||||
|
//add element to div
|
||||||
|
document.querySelector("#sponsorSkipNotice" + this.idSuffix + " > tbody").insertBefore(thanksForVotingText2, document.getElementById("sponsorSkipNoticeSpacer" + this.idSuffix));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NoticeComponent;
|
||||||
28
src/components/NoticeTextSectionComponent.tsx
Normal file
28
src/components/NoticeTextSectionComponent.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
export interface NoticeTextSelectionProps {
|
||||||
|
text: string,
|
||||||
|
idSuffix: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NoticeTextSelectionState {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoticeTextSelectionComponent extends React.Component<NoticeTextSelectionProps, NoticeTextSelectionState> {
|
||||||
|
|
||||||
|
constructor(props: NoticeTextSelectionProps) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<p id={"sponsorTimesInfoMessage" + this.props.idSuffix}
|
||||||
|
className="sponsorTimesInfoMessage">
|
||||||
|
{this.props.text}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NoticeTextSelectionComponent;
|
||||||
318
src/components/SkipNoticeComponent.tsx
Normal file
318
src/components/SkipNoticeComponent.tsx
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import Config from "../config"
|
||||||
|
import { ContentContainer } from "../types";
|
||||||
|
|
||||||
|
import Utils from "../utils";
|
||||||
|
var utils = new Utils();
|
||||||
|
|
||||||
|
import NoticeComponent from "./NoticeComponent";
|
||||||
|
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
|
||||||
|
|
||||||
|
|
||||||
|
export interface SkipNoticeProps {
|
||||||
|
UUID: string;
|
||||||
|
autoSkip: boolean;
|
||||||
|
// Contains functions and variables from the content script needed by the skip notice
|
||||||
|
contentContainer: ContentContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SkipNoticeState {
|
||||||
|
noticeTitle: string,
|
||||||
|
|
||||||
|
messages: string[],
|
||||||
|
|
||||||
|
countdownTime: number,
|
||||||
|
maxCountdownTime: () => number;
|
||||||
|
countdownText: string,
|
||||||
|
|
||||||
|
unskipText: string,
|
||||||
|
unskipCallback: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeState> {
|
||||||
|
UUID: string;
|
||||||
|
autoSkip: boolean;
|
||||||
|
// Contains functions and variables from the content script needed by the skip notice
|
||||||
|
contentContainer: ContentContainer;
|
||||||
|
|
||||||
|
amountOfPreviousNotices: number;
|
||||||
|
audio: HTMLAudioElement;
|
||||||
|
|
||||||
|
idSuffix: any;
|
||||||
|
|
||||||
|
noticeRef: React.MutableRefObject<NoticeComponent>;
|
||||||
|
|
||||||
|
constructor(props: SkipNoticeProps) {
|
||||||
|
super(props);
|
||||||
|
this.noticeRef = React.createRef();
|
||||||
|
|
||||||
|
this.UUID = props.UUID;
|
||||||
|
this.autoSkip = props.autoSkip;
|
||||||
|
this.contentContainer = props.contentContainer;
|
||||||
|
this.audio = null;
|
||||||
|
|
||||||
|
let noticeTitle = chrome.i18n.getMessage("noticeTitle");
|
||||||
|
|
||||||
|
if (!this.autoSkip) {
|
||||||
|
noticeTitle = chrome.i18n.getMessage("noticeTitleNotSkipped");
|
||||||
|
}
|
||||||
|
|
||||||
|
//add notice
|
||||||
|
this.amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
|
||||||
|
|
||||||
|
//this is the suffix added at the end of every id
|
||||||
|
this.idSuffix = this.UUID + this.amountOfPreviousNotices;
|
||||||
|
|
||||||
|
if (this.amountOfPreviousNotices > 0) {
|
||||||
|
//another notice exists
|
||||||
|
|
||||||
|
let previousNotice = document.getElementsByClassName("sponsorSkipNotice")[0];
|
||||||
|
previousNotice.classList.add("secondSkipNotice")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup state
|
||||||
|
this.state = {
|
||||||
|
noticeTitle,
|
||||||
|
messages: [],
|
||||||
|
|
||||||
|
//the countdown until this notice closes
|
||||||
|
maxCountdownTime: () => 4,
|
||||||
|
countdownTime: 4,
|
||||||
|
countdownText: null,
|
||||||
|
|
||||||
|
unskipText: chrome.i18n.getMessage("unskip"),
|
||||||
|
unskipCallback: this.unskip.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.autoSkip) {
|
||||||
|
Object.assign(this.state, this.getUnskippedModeInfo(chrome.i18n.getMessage("skip")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (Config.config.audioNotificationOnSkip && this.audio) {
|
||||||
|
this.audio.volume = this.contentContainer().v.volume * 0.1;
|
||||||
|
this.audio.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let noticeStyle: React.CSSProperties = {
|
||||||
|
zIndex: 50 + this.amountOfPreviousNotices
|
||||||
|
}
|
||||||
|
if (this.contentContainer().onMobileYouTube) {
|
||||||
|
noticeStyle.bottom = "4em";
|
||||||
|
noticeStyle.transform = "scale(0.8) translate(10%, 10%)";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NoticeComponent noticeTitle={this.state.noticeTitle}
|
||||||
|
amountOfPreviousNotices={this.amountOfPreviousNotices}
|
||||||
|
idSuffix={this.idSuffix}
|
||||||
|
fadeIn={true}
|
||||||
|
timed={true}
|
||||||
|
maxCountdownTime={this.state.maxCountdownTime}
|
||||||
|
ref={this.noticeRef}>
|
||||||
|
|
||||||
|
{(Config.config.audioNotificationOnSkip) && <audio ref={(source) => { this.audio = source; }}>
|
||||||
|
<source src={chrome.extension.getURL("icons/beep.ogg")} type="audio/ogg"></source>
|
||||||
|
</audio>}
|
||||||
|
|
||||||
|
{/* Text Boxes */}
|
||||||
|
{this.getMessageBoxes()}
|
||||||
|
|
||||||
|
{/* Last Row */}
|
||||||
|
<tr id={"sponsorSkipNoticeSecondRow" + this.idSuffix}>
|
||||||
|
|
||||||
|
{/* Vote Button Container */}
|
||||||
|
<td id={"sponsorTimesVoteButtonsContainer" + this.idSuffix}
|
||||||
|
className="sponsorTimesVoteButtonsContainer">
|
||||||
|
|
||||||
|
{/* Report Text */}
|
||||||
|
<span id={"sponsorTimesReportText" + this.idSuffix}
|
||||||
|
className="sponsorTimesInfoMessage sponsorTimesVoteButtonMessage"
|
||||||
|
title={chrome.i18n.getMessage("reportButtonInfo")}
|
||||||
|
style={{marginRight: "5px"}}>
|
||||||
|
|
||||||
|
{chrome.i18n.getMessage("reportButtonTitle")}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{/* Report Button */}
|
||||||
|
<img id={"sponsorTimesDownvoteButtonsContainer" + this.idSuffix}
|
||||||
|
className="sponsorSkipObject voteButton"
|
||||||
|
src={chrome.extension.getURL("icons/report.png")}
|
||||||
|
title={chrome.i18n.getMessage("reportButtonInfo")}
|
||||||
|
onClick={() => this.contentContainer().vote(0, this.UUID, this)}>
|
||||||
|
|
||||||
|
</img>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* Unskip Button */}
|
||||||
|
<td className="sponsorSkipNoticeUnskipSection">
|
||||||
|
<button id={"sponsorSkipUnskipButton" + this.idSuffix}
|
||||||
|
className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||||
|
style={{marginLeft: "4px"}}
|
||||||
|
onClick={this.state.unskipCallback}>
|
||||||
|
|
||||||
|
{this.state.unskipText}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* Never show button if autoSkip is enabled */}
|
||||||
|
{!this.autoSkip ? "" :
|
||||||
|
<td className="sponsorSkipNoticeRightSection">
|
||||||
|
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
|
||||||
|
onClick={this.contentContainer().dontShowNoticeAgain}>
|
||||||
|
|
||||||
|
{chrome.i18n.getMessage("Hide")}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</NoticeComponent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMessageBoxes(): JSX.Element[] | JSX.Element {
|
||||||
|
if (this.state.messages.length === 0) {
|
||||||
|
// Add a spacer if there is no text
|
||||||
|
return (
|
||||||
|
<tr id={"sponsorSkipNoticeSpacer" + this.idSuffix}
|
||||||
|
className="sponsorBlockSpacer">
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let elements: JSX.Element[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.state.messages.length; i++) {
|
||||||
|
elements.push(
|
||||||
|
<NoticeTextSelectionComponent idSuffix={this.idSuffix}
|
||||||
|
text={this.state.messages[i]}
|
||||||
|
key={i}>
|
||||||
|
</NoticeTextSelectionComponent>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
unskip() {
|
||||||
|
this.contentContainer().unskipSponsorTime(this.UUID);
|
||||||
|
|
||||||
|
this.unskippedMode(chrome.i18n.getMessage("reskip"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets up notice to be not skipped yet */
|
||||||
|
unskippedMode(buttonText: string) {
|
||||||
|
//setup new callback and reset countdown
|
||||||
|
this.setState(this.getUnskippedModeInfo(buttonText), () => {
|
||||||
|
this.noticeRef.current.resetCountdown();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnskippedModeInfo(buttonText: string) {
|
||||||
|
let maxCountdownTime = function() {
|
||||||
|
let sponsorTime = utils.getSponsorTimeFromUUID(this.contentContainer().sponsorTimes, this.UUID);
|
||||||
|
let duration = Math.round((sponsorTime.segment[1] - this.contentContainer().v.currentTime) * (1 / this.contentContainer().v.playbackRate));
|
||||||
|
|
||||||
|
return Math.max(duration, 4);
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
return {
|
||||||
|
unskipText: buttonText,
|
||||||
|
|
||||||
|
unskipCallback: this.reskip.bind(this),
|
||||||
|
|
||||||
|
//change max duration to however much of the sponsor is left
|
||||||
|
maxCountdownTime: maxCountdownTime,
|
||||||
|
|
||||||
|
countdownTime: maxCountdownTime()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reskip() {
|
||||||
|
this.contentContainer().reskipSponsorTime(this.UUID);
|
||||||
|
|
||||||
|
//reset countdown
|
||||||
|
this.setState({
|
||||||
|
unskipText: chrome.i18n.getMessage("unskip"),
|
||||||
|
unskipCallback: this.unskip.bind(this),
|
||||||
|
|
||||||
|
maxCountdownTime: () => 4,
|
||||||
|
countdownTime: 4
|
||||||
|
});
|
||||||
|
|
||||||
|
// See if the title should be changed
|
||||||
|
if (!this.autoSkip) {
|
||||||
|
this.setState({
|
||||||
|
noticeTitle: chrome.i18n.getMessage("noticeTitle")
|
||||||
|
});
|
||||||
|
|
||||||
|
if(Config.config.autoUpvote) this.contentContainer().vote(1, this.UUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
afterDownvote() {
|
||||||
|
this.addVoteButtonInfo(chrome.i18n.getMessage("voted"));
|
||||||
|
this.setNoticeInfoMessage(chrome.i18n.getMessage("hitGoBack"));
|
||||||
|
|
||||||
|
//remove this sponsor from the sponsors looked up
|
||||||
|
//find which one it is
|
||||||
|
for (let i = 0; i < this.contentContainer().sponsorTimes.length; i++) {
|
||||||
|
if (this.contentContainer().sponsorTimes[i].UUID == this.UUID) {
|
||||||
|
//this one is the one to hide
|
||||||
|
|
||||||
|
//add this as a hidden sponsorTime
|
||||||
|
this.contentContainer().hiddenSponsorTimes.push(i);
|
||||||
|
|
||||||
|
this.contentContainer().updatePreviewBar();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setNoticeInfoMessage(...messages: string[]) {
|
||||||
|
this.setState({
|
||||||
|
messages
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
addVoteButtonInfo(message) {
|
||||||
|
this.resetVoteButtonInfo();
|
||||||
|
|
||||||
|
//hide report button and text for it
|
||||||
|
let downvoteButton = document.getElementById("sponsorTimesDownvoteButtonsContainer" + this.idSuffix);
|
||||||
|
if (downvoteButton != null) {
|
||||||
|
downvoteButton.style.display = "none";
|
||||||
|
}
|
||||||
|
let downvoteButtonText = document.getElementById("sponsorTimesReportText" + this.idSuffix);
|
||||||
|
if (downvoteButtonText != null) {
|
||||||
|
downvoteButtonText.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
//add info
|
||||||
|
let thanksForVotingText = document.createElement("td");
|
||||||
|
thanksForVotingText.id = "sponsorTimesVoteButtonInfoMessage" + this.idSuffix;
|
||||||
|
thanksForVotingText.className = "sponsorTimesInfoMessage sponsorTimesVoteButtonMessage";
|
||||||
|
thanksForVotingText.innerText = message;
|
||||||
|
|
||||||
|
//add element to div
|
||||||
|
document.getElementById("sponsorSkipNoticeSecondRow" + this.idSuffix).prepend(thanksForVotingText);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetVoteButtonInfo() {
|
||||||
|
let previousInfoMessage = document.getElementById("sponsorTimesVoteButtonInfoMessage" + this.idSuffix);
|
||||||
|
if (previousInfoMessage != null) {
|
||||||
|
//remove it
|
||||||
|
document.getElementById("sponsorSkipNoticeSecondRow" + this.idSuffix).removeChild(previousInfoMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
//show button again
|
||||||
|
document.getElementById("sponsorTimesDownvoteButtonsContainer" + this.idSuffix).style.removeProperty("display");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SkipNoticeComponent;
|
||||||
302
src/components/SponsorTimeEditComponent.tsx
Normal file
302
src/components/SponsorTimeEditComponent.tsx
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import Config from "../config";
|
||||||
|
import * as CompileConfig from "../../config.json";
|
||||||
|
|
||||||
|
import Utils from "../utils";
|
||||||
|
import { ContentContainer, SponsorTime } from "../types";
|
||||||
|
import SubmissionNoticeComponent from "./SubmissionNoticeComponent";
|
||||||
|
var utils = new Utils();
|
||||||
|
|
||||||
|
export interface SponsorTimeEditProps {
|
||||||
|
index: number,
|
||||||
|
|
||||||
|
idSuffix: string,
|
||||||
|
// Contains functions and variables from the content script needed by the skip notice
|
||||||
|
contentContainer: ContentContainer,
|
||||||
|
|
||||||
|
submissionNotice: SubmissionNoticeComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SponsorTimeEditState {
|
||||||
|
editing: boolean;
|
||||||
|
sponsorTimeEdits: string[][];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, SponsorTimeEditState> {
|
||||||
|
|
||||||
|
idSuffix: string;
|
||||||
|
|
||||||
|
categoryOptionRef: React.RefObject<HTMLSelectElement>;
|
||||||
|
|
||||||
|
constructor(props: SponsorTimeEditProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.categoryOptionRef = React.createRef();
|
||||||
|
|
||||||
|
this.idSuffix = this.props.idSuffix;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
editing: false,
|
||||||
|
sponsorTimeEdits: [[null, null], [null, null]]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
// Prevent inputs from triggering key events
|
||||||
|
document.getElementById("sponsorTimesContainer" + this.idSuffix).addEventListener('keydown', function (event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let style: React.CSSProperties = {
|
||||||
|
textAlign: "center"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.props.index != 0) {
|
||||||
|
style.marginTop = "15px";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create time display
|
||||||
|
let timeDisplay: JSX.Element;
|
||||||
|
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
|
||||||
|
let segment = sponsorTime.segment;
|
||||||
|
if (this.state.editing) {
|
||||||
|
timeDisplay = (
|
||||||
|
<div id={"sponsorTimesContainer" + this.idSuffix}
|
||||||
|
className="sponsorTimeDisplay">
|
||||||
|
|
||||||
|
<span id={"nowButton0" + this.idSuffix}
|
||||||
|
className="sponsorNowButton"
|
||||||
|
onClick={() => this.setTimeToNow(0)}>
|
||||||
|
{chrome.i18n.getMessage("bracketNow")}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<input id={"submittingTimeMinutes0" + this.idSuffix}
|
||||||
|
className="sponsorTimeEdit sponsorTimeEditMinutes"
|
||||||
|
type="number"
|
||||||
|
value={this.state.sponsorTimeEdits[0][0]}
|
||||||
|
onChange={(e) => {
|
||||||
|
let sponsorTimeEdits = this.state.sponsorTimeEdits;
|
||||||
|
sponsorTimeEdits[0][0] = e.target.value;
|
||||||
|
|
||||||
|
this.setState({sponsorTimeEdits});
|
||||||
|
}}>
|
||||||
|
</input>
|
||||||
|
|
||||||
|
<input id={"submittingTimeSeconds0" + this.idSuffix}
|
||||||
|
className="sponsorTimeEdit sponsorTimeEditSeconds"
|
||||||
|
type="number"
|
||||||
|
value={this.state.sponsorTimeEdits[0][1]}
|
||||||
|
onChange={(e) => {
|
||||||
|
let sponsorTimeEdits = this.state.sponsorTimeEdits;
|
||||||
|
sponsorTimeEdits[0][1] = e.target.value;
|
||||||
|
|
||||||
|
this.setState({sponsorTimeEdits});
|
||||||
|
}}>
|
||||||
|
</input>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
{" " + chrome.i18n.getMessage("to") + " "}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<input id={"submittingTimeMinutes1" + this.idSuffix}
|
||||||
|
className="sponsorTimeEdit sponsorTimeEditMinutes"
|
||||||
|
type="text"
|
||||||
|
value={this.state.sponsorTimeEdits[1][0]}
|
||||||
|
onChange={(e) => {
|
||||||
|
let sponsorTimeEdits = this.state.sponsorTimeEdits;
|
||||||
|
sponsorTimeEdits[1][0] = e.target.value;
|
||||||
|
|
||||||
|
this.setState({sponsorTimeEdits});
|
||||||
|
}}>
|
||||||
|
</input>
|
||||||
|
|
||||||
|
<input id={"submittingTimeSeconds1" + this.idSuffix}
|
||||||
|
className="sponsorTimeEdit sponsorTimeEditSeconds"
|
||||||
|
type="text"
|
||||||
|
value={this.state.sponsorTimeEdits[1][1]}
|
||||||
|
onChange={(e) => {
|
||||||
|
let sponsorTimeEdits = this.state.sponsorTimeEdits;
|
||||||
|
sponsorTimeEdits[1][1] = e.target.value;
|
||||||
|
|
||||||
|
this.setState({sponsorTimeEdits});
|
||||||
|
}}>
|
||||||
|
</input>
|
||||||
|
|
||||||
|
<span id={"nowButton1" + this.idSuffix}
|
||||||
|
className="sponsorNowButton"
|
||||||
|
onClick={() => this.setTimeToNow(1)}>
|
||||||
|
{chrome.i18n.getMessage("bracketNow")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
timeDisplay = (
|
||||||
|
<div id={"sponsorTimesContainer" + this.idSuffix}
|
||||||
|
className="sponsorTimeDisplay"
|
||||||
|
onClick={this.toggleEditTime.bind(this)}>
|
||||||
|
{utils.getFormattedTime(segment[0], true) +
|
||||||
|
((!isNaN(segment[1])) ? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segment[1], true) : "")}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={style}>
|
||||||
|
|
||||||
|
{timeDisplay}
|
||||||
|
|
||||||
|
{/* Category */}
|
||||||
|
|
||||||
|
<select id={"sponsorTimeCategories" + this.idSuffix}
|
||||||
|
className="sponsorTimeCategories"
|
||||||
|
defaultValue={sponsorTime.category}
|
||||||
|
ref={this.categoryOptionRef}
|
||||||
|
onChange={this.saveEditTimes.bind(this)}>
|
||||||
|
{this.getCategoryOptions()}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
{/* Editing Tools */}
|
||||||
|
|
||||||
|
<span id={"sponsorTimeDeleteButton" + this.idSuffix}
|
||||||
|
className="sponsorTimeEditButton"
|
||||||
|
onClick={this.deleteTime.bind(this)}>
|
||||||
|
{chrome.i18n.getMessage("delete")}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{(!isNaN(segment[1])) ? (
|
||||||
|
<span id={"sponsorTimePreviewButton" + this.idSuffix}
|
||||||
|
className="sponsorTimeEditButton"
|
||||||
|
onClick={this.previewTime.bind(this)}>
|
||||||
|
{chrome.i18n.getMessage("preview")}
|
||||||
|
</span>
|
||||||
|
): ""}
|
||||||
|
|
||||||
|
{(!isNaN(segment[1])) ? (
|
||||||
|
<span id={"sponsorTimeEditButton" + this.idSuffix}
|
||||||
|
className="sponsorTimeEditButton"
|
||||||
|
onClick={this.toggleEditTime.bind(this)}>
|
||||||
|
{this.state.editing ? chrome.i18n.getMessage("save") : chrome.i18n.getMessage("edit")}
|
||||||
|
</span>
|
||||||
|
): ""}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCategoryOptions() {
|
||||||
|
let elements = [];
|
||||||
|
|
||||||
|
//TODO: Remove this when testing server is not needed
|
||||||
|
let categoryList = Config.config.testingServer ? CompileConfig.categoryList : ["sponsor"];
|
||||||
|
for (const category of categoryList) {
|
||||||
|
elements.push(
|
||||||
|
<option value={category}
|
||||||
|
key={category}>
|
||||||
|
{chrome.i18n.getMessage("category_" + category)}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeToNow(index: number) {
|
||||||
|
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
|
||||||
|
|
||||||
|
sponsorTime.segment[index] =
|
||||||
|
this.props.contentContainer().v.currentTime;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
sponsorTimeEdits: this.getFormattedSponsorTimesEdits(sponsorTime)
|
||||||
|
}, this.saveEditTimes);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleEditTime(): void {
|
||||||
|
if (this.state.editing) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
editing: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.saveEditTimes();
|
||||||
|
} else {
|
||||||
|
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
editing: true,
|
||||||
|
sponsorTimeEdits: this.getFormattedSponsorTimesEdits(sponsorTime)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns an array in the sponsorTimeEdits form (minutes and seconds) from a normal seconds sponsor time */
|
||||||
|
getFormattedSponsorTimesEdits(sponsorTime: SponsorTime): string[][] {
|
||||||
|
return [[String(utils.getFormattedMinutes(sponsorTime.segment[0])), String(utils.getFormattedSeconds(sponsorTime.segment[0]))],
|
||||||
|
[String(utils.getFormattedMinutes(sponsorTime.segment[1])), String(utils.getFormattedSeconds(sponsorTime.segment[1]))]];
|
||||||
|
}
|
||||||
|
|
||||||
|
saveEditTimes() {
|
||||||
|
let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
|
||||||
|
|
||||||
|
if (this.state.editing) {
|
||||||
|
sponsorTimesSubmitting[this.props.index].segment =
|
||||||
|
[utils.getRawSeconds(parseFloat(this.state.sponsorTimeEdits[0][0]), parseFloat(this.state.sponsorTimeEdits[0][1])),
|
||||||
|
utils.getRawSeconds(parseFloat(this.state.sponsorTimeEdits[1][0]), parseFloat(this.state.sponsorTimeEdits[1][1]))];
|
||||||
|
}
|
||||||
|
|
||||||
|
sponsorTimesSubmitting[this.props.index].category = this.categoryOptionRef.current.value;
|
||||||
|
|
||||||
|
Config.config.sponsorTimes.set(this.props.contentContainer().sponsorVideoID, utils.getSegmentsFromSponsorTimes(sponsorTimesSubmitting));
|
||||||
|
|
||||||
|
this.props.contentContainer().updatePreviewBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
previewTime(): void {
|
||||||
|
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
|
||||||
|
let index = this.props.index;
|
||||||
|
|
||||||
|
let skipTime = sponsorTimes[index][0];
|
||||||
|
|
||||||
|
if (this.state.editing) {
|
||||||
|
// Save edits before previewing
|
||||||
|
this.saveEditTimes();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.contentContainer().previewTime(skipTime - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTime(): void {
|
||||||
|
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
|
||||||
|
let index = this.props.index;
|
||||||
|
|
||||||
|
//if it is not a complete sponsor time
|
||||||
|
if (sponsorTimes[index].segment.length < 2) {
|
||||||
|
//update video player
|
||||||
|
this.props.contentContainer().changeStartSponsorButton(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
sponsorTimes.splice(index, 1);
|
||||||
|
|
||||||
|
//save this
|
||||||
|
Config.config.sponsorTimes.set(this.props.contentContainer().sponsorVideoID, sponsorTimes);
|
||||||
|
|
||||||
|
this.props.contentContainer().updatePreviewBar();
|
||||||
|
|
||||||
|
//if they are all removed
|
||||||
|
if (sponsorTimes.length == 0) {
|
||||||
|
this.props.submissionNotice.cancel();
|
||||||
|
|
||||||
|
//update video player
|
||||||
|
this.props.contentContainer().changeStartSponsorButton(true, false);
|
||||||
|
} else {
|
||||||
|
//update display
|
||||||
|
this.props.submissionNotice.forceUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SponsorTimeEditComponent;
|
||||||
150
src/components/SubmissionNoticeComponent.tsx
Normal file
150
src/components/SubmissionNoticeComponent.tsx
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import Config from "../config"
|
||||||
|
import { ContentContainer } from "../types";
|
||||||
|
|
||||||
|
import NoticeComponent from "./NoticeComponent";
|
||||||
|
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
|
||||||
|
import SponsorTimeEditComponent from "./SponsorTimeEditComponent";
|
||||||
|
|
||||||
|
export interface SubmissionNoticeProps {
|
||||||
|
// Contains functions and variables from the content script needed by the skip notice
|
||||||
|
contentContainer: ContentContainer;
|
||||||
|
|
||||||
|
callback: () => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SubmissionNoticeeState {
|
||||||
|
noticeTitle: string,
|
||||||
|
messages: string[],
|
||||||
|
idSuffix: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, SubmissionNoticeeState> {
|
||||||
|
// Contains functions and variables from the content script needed by the skip notice
|
||||||
|
contentContainer: ContentContainer;
|
||||||
|
|
||||||
|
callback: () => any;
|
||||||
|
|
||||||
|
noticeRef: React.MutableRefObject<NoticeComponent>;
|
||||||
|
timeEditRefs: React.RefObject<SponsorTimeEditComponent>[];
|
||||||
|
|
||||||
|
constructor(props: SubmissionNoticeProps) {
|
||||||
|
super(props);
|
||||||
|
this.noticeRef = React.createRef();
|
||||||
|
|
||||||
|
this.contentContainer = props.contentContainer;
|
||||||
|
this.callback = props.callback;
|
||||||
|
|
||||||
|
let noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle");
|
||||||
|
|
||||||
|
// Setup state
|
||||||
|
this.state = {
|
||||||
|
noticeTitle,
|
||||||
|
messages: [],
|
||||||
|
idSuffix: "SubmissionNotice"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<NoticeComponent noticeTitle={this.state.noticeTitle}
|
||||||
|
idSuffix={this.state.idSuffix}
|
||||||
|
ref={this.noticeRef}
|
||||||
|
closeListener={this.cancel.bind(this)}
|
||||||
|
zIndex={50000}>
|
||||||
|
|
||||||
|
{/* Text Boxes */}
|
||||||
|
{this.getMessageBoxes()}
|
||||||
|
|
||||||
|
{/* Sponsor Time List */}
|
||||||
|
<tr id={"sponsorSkipNoticeMiddleRow" + this.state.idSuffix}>
|
||||||
|
<td>
|
||||||
|
{this.getSponsorTimeMessages()}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{/* Last Row */}
|
||||||
|
<tr id={"sponsorSkipNoticeSecondRow" + this.state.idSuffix}>
|
||||||
|
|
||||||
|
<td className="sponsorSkipNoticeRightSection"
|
||||||
|
style={{position: "relative"}}>
|
||||||
|
|
||||||
|
{/* Cancel Button */}
|
||||||
|
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
|
||||||
|
onClick={this.cancel.bind(this)}>
|
||||||
|
|
||||||
|
{chrome.i18n.getMessage("cancel")}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
|
||||||
|
onClick={this.submit.bind(this)}>
|
||||||
|
|
||||||
|
{chrome.i18n.getMessage("submit")}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</NoticeComponent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSponsorTimeMessages(): JSX.Element[] | JSX.Element {
|
||||||
|
let elements: JSX.Element[] = [];
|
||||||
|
this.timeEditRefs = [];
|
||||||
|
|
||||||
|
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
|
||||||
|
|
||||||
|
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||||
|
let timeRef = React.createRef<SponsorTimeEditComponent>();
|
||||||
|
|
||||||
|
elements.push(
|
||||||
|
<SponsorTimeEditComponent key={i}
|
||||||
|
idSuffix={this.state.idSuffix + i}
|
||||||
|
index={i}
|
||||||
|
contentContainer={this.props.contentContainer}
|
||||||
|
submissionNotice={this}
|
||||||
|
ref={timeRef}>
|
||||||
|
</SponsorTimeEditComponent>
|
||||||
|
);
|
||||||
|
|
||||||
|
this.timeEditRefs.push(timeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMessageBoxes(): JSX.Element[] | JSX.Element {
|
||||||
|
let elements: JSX.Element[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.state.messages.length; i++) {
|
||||||
|
elements.push(
|
||||||
|
<NoticeTextSelectionComponent idSuffix={this.state.idSuffix + i}
|
||||||
|
text={this.state.messages[i]}
|
||||||
|
key={i}>
|
||||||
|
</NoticeTextSelectionComponent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
this.noticeRef.current.close(true);
|
||||||
|
|
||||||
|
this.contentContainer().resetSponsorSubmissionNotice();
|
||||||
|
}
|
||||||
|
|
||||||
|
submit() {
|
||||||
|
// save all items
|
||||||
|
for (const ref of this.timeEditRefs) {
|
||||||
|
ref.current.saveEditTimes();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.callback();
|
||||||
|
|
||||||
|
this.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SubmissionNoticeComponent;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as CompileConfig from "../config.json";
|
import * as CompileConfig from "../config.json";
|
||||||
|
import { CategorySelection, CategorySkipOption } from "./types";
|
||||||
|
|
||||||
interface SBConfig {
|
interface SBConfig {
|
||||||
userID: string,
|
userID: string,
|
||||||
@@ -10,7 +11,6 @@ interface SBConfig {
|
|||||||
skipCount: number,
|
skipCount: number,
|
||||||
sponsorTimesContributed: number,
|
sponsorTimesContributed: number,
|
||||||
disableSkipping: boolean,
|
disableSkipping: boolean,
|
||||||
disableAutoSkip: boolean,
|
|
||||||
trackViewCount: boolean,
|
trackViewCount: boolean,
|
||||||
dontShowNotice: boolean,
|
dontShowNotice: boolean,
|
||||||
hideVideoPlayerControls: boolean,
|
hideVideoPlayerControls: boolean,
|
||||||
@@ -24,8 +24,13 @@ interface SBConfig {
|
|||||||
supportInvidious: boolean,
|
supportInvidious: boolean,
|
||||||
serverAddress: string,
|
serverAddress: string,
|
||||||
minDuration: number,
|
minDuration: number,
|
||||||
|
audioNotificationOnSkip,
|
||||||
checkForUnlistedVideos: boolean,
|
checkForUnlistedVideos: boolean,
|
||||||
mobileUpdateShowCount: number
|
mobileUpdateShowCount: number,
|
||||||
|
testingServer: boolean,
|
||||||
|
|
||||||
|
// What categories should be skipped
|
||||||
|
categorySelections: CategorySelection[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SBObject {
|
interface SBObject {
|
||||||
@@ -105,7 +110,6 @@ var Config: SBObject = {
|
|||||||
skipCount: 0,
|
skipCount: 0,
|
||||||
sponsorTimesContributed: 0,
|
sponsorTimesContributed: 0,
|
||||||
disableSkipping: false,
|
disableSkipping: false,
|
||||||
disableAutoSkip: false,
|
|
||||||
trackViewCount: true,
|
trackViewCount: true,
|
||||||
dontShowNotice: false,
|
dontShowNotice: false,
|
||||||
hideVideoPlayerControls: false,
|
hideVideoPlayerControls: false,
|
||||||
@@ -119,8 +123,15 @@ var Config: SBObject = {
|
|||||||
supportInvidious: false,
|
supportInvidious: false,
|
||||||
serverAddress: CompileConfig.serverAddress,
|
serverAddress: CompileConfig.serverAddress,
|
||||||
minDuration: 0,
|
minDuration: 0,
|
||||||
|
audioNotificationOnSkip: false,
|
||||||
checkForUnlistedVideos: false,
|
checkForUnlistedVideos: false,
|
||||||
mobileUpdateShowCount: 0
|
mobileUpdateShowCount: 0,
|
||||||
|
testingServer: false,
|
||||||
|
|
||||||
|
categorySelections: [{
|
||||||
|
name: "sponsor",
|
||||||
|
option: CategorySkipOption.AutoSkip
|
||||||
|
}]
|
||||||
},
|
},
|
||||||
localConfig: null,
|
localConfig: null,
|
||||||
config: null,
|
config: null,
|
||||||
@@ -225,11 +236,14 @@ function fetchConfig() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function migrateOldFormats() { // Convert sponsorTimes format
|
function migrateOldFormats() {
|
||||||
for (const key in Config.localConfig) {
|
if (Config.config["disableAutoSkip"]) {
|
||||||
if (key.startsWith("sponsorTimes") && key !== "sponsorTimes" && key !== "sponsorTimesContributed") {
|
for (const selection of Config.config.categorySelections) {
|
||||||
Config.config.sponsorTimes.set(key.substr(12), Config.config[key]);
|
if (selection.name === "sponsor") {
|
||||||
delete Config.config[key];
|
selection.option = CategorySkipOption.ManualSkip;
|
||||||
|
|
||||||
|
chrome.storage.sync.remove("disableAutoSkip");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
656
src/content.ts
656
src/content.ts
File diff suppressed because it is too large
Load Diff
@@ -8,15 +8,55 @@
|
|||||||
let barTypes = {
|
let barTypes = {
|
||||||
"undefined": {
|
"undefined": {
|
||||||
color: "#00d400",
|
color: "#00d400",
|
||||||
opacity: "0.5"
|
opacity: "0.7"
|
||||||
},
|
},
|
||||||
"sponsor": {
|
"sponsor": {
|
||||||
color: "#00d400",
|
color: "#00d400",
|
||||||
opacity: "0.5"
|
opacity: "0.7"
|
||||||
},
|
},
|
||||||
"previewSponsor": {
|
"preview-sponsor": {
|
||||||
color: "#0000d4",
|
color: "#007800",
|
||||||
opacity: "0.5"
|
opacity: "0.7"
|
||||||
|
},
|
||||||
|
"intro": {
|
||||||
|
color: "#00ffff",
|
||||||
|
opacity: "0.7"
|
||||||
|
},
|
||||||
|
"preview-intro": {
|
||||||
|
color: "#008080",
|
||||||
|
opacity: "0.7"
|
||||||
|
},
|
||||||
|
"outro": {
|
||||||
|
color: "#0202ed",
|
||||||
|
opacity: "0.7"
|
||||||
|
},
|
||||||
|
"preview-outro": {
|
||||||
|
color: "#000070",
|
||||||
|
opacity: "0.7"
|
||||||
|
},
|
||||||
|
"interaction": {
|
||||||
|
color: "#cc00ff",
|
||||||
|
opacity: "0.7"
|
||||||
|
},
|
||||||
|
"preview-interaction": {
|
||||||
|
color: "#6c0087",
|
||||||
|
opacity: "0.7"
|
||||||
|
},
|
||||||
|
"selfpromo": {
|
||||||
|
color: "#ffff00",
|
||||||
|
opacity: "0.7"
|
||||||
|
},
|
||||||
|
"preview-selfpromo": {
|
||||||
|
color: "#bfbf35",
|
||||||
|
opacity: "0.7"
|
||||||
|
},
|
||||||
|
"offtopic": {
|
||||||
|
color: "#ff9900",
|
||||||
|
opacity: "0.7"
|
||||||
|
},
|
||||||
|
"preview-offtopic": {
|
||||||
|
color: "#a6634a",
|
||||||
|
opacity: "0.7"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,444 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import Config from "../config";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The notice that tells the user that a sponsor was just skipped
|
|
||||||
*/
|
|
||||||
class SkipNotice {
|
|
||||||
parent: HTMLElement;
|
|
||||||
UUID: string;
|
|
||||||
manualSkip: boolean;
|
|
||||||
// Contains functions and variables from the content script needed by the skip notice
|
|
||||||
contentContainer: () => any;
|
|
||||||
|
|
||||||
maxCountdownTime: () => number;
|
|
||||||
countdownTime: any;
|
|
||||||
countdownInterval: NodeJS.Timeout;
|
|
||||||
unskipCallback: any;
|
|
||||||
idSuffix: any;
|
|
||||||
|
|
||||||
constructor(parent: HTMLElement, UUID: string, manualSkip: boolean = false, contentContainer) {
|
|
||||||
this.parent = parent;
|
|
||||||
this.UUID = UUID;
|
|
||||||
this.manualSkip = manualSkip;
|
|
||||||
this.contentContainer = contentContainer;
|
|
||||||
|
|
||||||
let noticeTitle = chrome.i18n.getMessage("noticeTitle");
|
|
||||||
|
|
||||||
if (manualSkip) {
|
|
||||||
noticeTitle = chrome.i18n.getMessage("noticeTitleNotSkipped");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.maxCountdownTime = () => 4;
|
|
||||||
//the countdown until this notice closes
|
|
||||||
this.countdownTime = this.maxCountdownTime();
|
|
||||||
//the id for the setInterval running the countdown
|
|
||||||
this.countdownInterval = null;
|
|
||||||
|
|
||||||
//the unskip button's callback
|
|
||||||
this.unskipCallback = this.unskip.bind(this);
|
|
||||||
|
|
||||||
//add notice
|
|
||||||
let amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
|
|
||||||
|
|
||||||
//this is the suffix added at the end of every id
|
|
||||||
this.idSuffix = this.UUID + amountOfPreviousNotices;
|
|
||||||
|
|
||||||
if (amountOfPreviousNotices > 0) {
|
|
||||||
//already exists
|
|
||||||
|
|
||||||
let previousNotice = document.getElementsByClassName("sponsorSkipNotice")[0];
|
|
||||||
previousNotice.classList.add("secondSkipNotice")
|
|
||||||
}
|
|
||||||
|
|
||||||
let noticeElement = document.createElement("div");
|
|
||||||
//what sponsor time this is about
|
|
||||||
noticeElement.id = "sponsorSkipNotice" + this.idSuffix;
|
|
||||||
noticeElement.classList.add("sponsorSkipObject");
|
|
||||||
noticeElement.classList.add("sponsorSkipNotice");
|
|
||||||
noticeElement.style.zIndex = String(50 + amountOfPreviousNotices);
|
|
||||||
if (contentContainer().onMobileYouTube) {
|
|
||||||
noticeElement.style.bottom = "4em";
|
|
||||||
noticeElement.style.transform = "scale(0.8) translate(10%, 10%)";
|
|
||||||
}
|
|
||||||
|
|
||||||
//add mouse enter and leave listeners
|
|
||||||
noticeElement.addEventListener("mouseenter", this.pauseCountdown.bind(this));
|
|
||||||
noticeElement.addEventListener("mouseleave", this.startCountdown.bind(this));
|
|
||||||
|
|
||||||
//the row that will contain the info
|
|
||||||
let firstRow = document.createElement("tr");
|
|
||||||
firstRow.id = "sponsorSkipNoticeFirstRow" + this.idSuffix;
|
|
||||||
|
|
||||||
let logoColumn = document.createElement("td");
|
|
||||||
|
|
||||||
let logoElement = document.createElement("img");
|
|
||||||
logoElement.id = "sponsorSkipLogo" + this.idSuffix;
|
|
||||||
logoElement.className = "sponsorSkipLogo sponsorSkipObject";
|
|
||||||
logoElement.src = chrome.extension.getURL("icons/IconSponsorBlocker256px.png");
|
|
||||||
|
|
||||||
let noticeMessage = document.createElement("span");
|
|
||||||
noticeMessage.id = "sponsorSkipMessage" + this.idSuffix;
|
|
||||||
noticeMessage.classList.add("sponsorSkipMessage");
|
|
||||||
noticeMessage.classList.add("sponsorSkipObject");
|
|
||||||
noticeMessage.innerText = noticeTitle;
|
|
||||||
|
|
||||||
//create the first column
|
|
||||||
logoColumn.appendChild(logoElement);
|
|
||||||
logoColumn.appendChild(noticeMessage);
|
|
||||||
|
|
||||||
//add the x button
|
|
||||||
let closeButtonContainer = document.createElement("td");
|
|
||||||
closeButtonContainer.className = "sponsorSkipNoticeRightSection";
|
|
||||||
closeButtonContainer.style.top = "11px";
|
|
||||||
|
|
||||||
let timeLeft = document.createElement("span");
|
|
||||||
timeLeft.id = "sponsorSkipNoticeTimeLeft" + this.idSuffix;
|
|
||||||
timeLeft.innerText = this.countdownTime + "s";
|
|
||||||
timeLeft.className = "sponsorSkipObject sponsorSkipNoticeTimeLeft";
|
|
||||||
|
|
||||||
let hideButton = document.createElement("img");
|
|
||||||
hideButton.src = chrome.extension.getURL("icons/close.png");
|
|
||||||
hideButton.className = "sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeCloseButton sponsorSkipNoticeRightButton";
|
|
||||||
hideButton.addEventListener("click", this.close.bind(this));
|
|
||||||
|
|
||||||
closeButtonContainer.appendChild(timeLeft);
|
|
||||||
closeButtonContainer.appendChild(hideButton);
|
|
||||||
|
|
||||||
//add all objects to first row
|
|
||||||
firstRow.appendChild(logoColumn);
|
|
||||||
firstRow.appendChild(closeButtonContainer);
|
|
||||||
|
|
||||||
let spacer = document.createElement("hr");
|
|
||||||
spacer.id = "sponsorSkipNoticeSpacer" + this.idSuffix;
|
|
||||||
spacer.className = "sponsorBlockSpacer";
|
|
||||||
|
|
||||||
//the row that will contain the buttons
|
|
||||||
let secondRow = document.createElement("tr");
|
|
||||||
secondRow.id = "sponsorSkipNoticeSecondRow" + this.idSuffix;
|
|
||||||
|
|
||||||
//thumbs up and down buttons
|
|
||||||
let voteButtonsContainer = document.createElement("td");
|
|
||||||
voteButtonsContainer.id = "sponsorTimesVoteButtonsContainer" + this.idSuffix;
|
|
||||||
voteButtonsContainer.className = "sponsorTimesVoteButtonsContainer"
|
|
||||||
|
|
||||||
let reportText = document.createElement("span");
|
|
||||||
reportText.id = "sponsorTimesReportText" + this.idSuffix;
|
|
||||||
reportText.className = "sponsorTimesInfoMessage sponsorTimesVoteButtonMessage";
|
|
||||||
reportText.innerText = chrome.i18n.getMessage("reportButtonTitle");
|
|
||||||
reportText.style.marginRight = "5px";
|
|
||||||
reportText.setAttribute("title", chrome.i18n.getMessage("reportButtonInfo"));
|
|
||||||
|
|
||||||
let downvoteButton = document.createElement("img");
|
|
||||||
downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + this.idSuffix;
|
|
||||||
downvoteButton.className = "sponsorSkipObject voteButton";
|
|
||||||
downvoteButton.src = chrome.extension.getURL("icons/report.png");
|
|
||||||
downvoteButton.addEventListener("click", () => this.contentContainer().vote(0, this.UUID, this));
|
|
||||||
downvoteButton.setAttribute("title", chrome.i18n.getMessage("reportButtonInfo"));
|
|
||||||
|
|
||||||
//add downvote and report text to container
|
|
||||||
voteButtonsContainer.appendChild(reportText);
|
|
||||||
voteButtonsContainer.appendChild(downvoteButton);
|
|
||||||
|
|
||||||
//add unskip button
|
|
||||||
let unskipContainer = document.createElement("td");
|
|
||||||
unskipContainer.className = "sponsorSkipNoticeUnskipSection";
|
|
||||||
|
|
||||||
let unskipButton = document.createElement("button");
|
|
||||||
unskipButton.id = "sponsorSkipUnskipButton" + this.idSuffix;
|
|
||||||
unskipButton.innerText = chrome.i18n.getMessage("unskip");
|
|
||||||
unskipButton.className = "sponsorSkipObject sponsorSkipNoticeButton";
|
|
||||||
unskipButton.addEventListener("click", this.unskipCallback);
|
|
||||||
|
|
||||||
unskipButton.style.marginLeft = "4px";
|
|
||||||
|
|
||||||
unskipContainer.appendChild(unskipButton);
|
|
||||||
|
|
||||||
//add don't show again button
|
|
||||||
let dontshowContainer = document.createElement("td");
|
|
||||||
dontshowContainer.className = "sponsorSkipNoticeRightSection";
|
|
||||||
|
|
||||||
let dontShowAgainButton = document.createElement("button");
|
|
||||||
dontShowAgainButton.innerText = chrome.i18n.getMessage("Hide");
|
|
||||||
dontShowAgainButton.className = "sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton";
|
|
||||||
dontShowAgainButton.addEventListener("click", this.contentContainer().dontShowNoticeAgain);
|
|
||||||
|
|
||||||
// Don't let them hide it if manually skipping
|
|
||||||
if (!this.manualSkip) {
|
|
||||||
dontshowContainer.appendChild(dontShowAgainButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
//add to row
|
|
||||||
secondRow.appendChild(voteButtonsContainer);
|
|
||||||
secondRow.appendChild(unskipContainer);
|
|
||||||
secondRow.appendChild(dontshowContainer);
|
|
||||||
|
|
||||||
noticeElement.appendChild(firstRow);
|
|
||||||
noticeElement.appendChild(spacer);
|
|
||||||
noticeElement.appendChild(secondRow);
|
|
||||||
|
|
||||||
//get reference node
|
|
||||||
let referenceNode = document.getElementById("player-container-id")
|
|
||||||
|| document.getElementById("movie_player") || document.querySelector("#player-container .video-js");
|
|
||||||
if (referenceNode == null) {
|
|
||||||
//for embeds
|
|
||||||
let player = document.getElementById("player");
|
|
||||||
referenceNode = <HTMLElement> player.firstChild;
|
|
||||||
let index = 1;
|
|
||||||
|
|
||||||
//find the child that is the video player (sometimes it is not the first)
|
|
||||||
while (!referenceNode.classList.contains("html5-video-player") || !referenceNode.classList.contains("ytp-embed")) {
|
|
||||||
referenceNode = <HTMLElement> player.children[index];
|
|
||||||
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
referenceNode.prepend(noticeElement);
|
|
||||||
|
|
||||||
if (manualSkip) {
|
|
||||||
this.unskippedMode(chrome.i18n.getMessage("skip"));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.startCountdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
//called every second to lower the countdown before hiding the notice
|
|
||||||
countdown() {
|
|
||||||
this.countdownTime--;
|
|
||||||
|
|
||||||
if (this.countdownTime <= 0) {
|
|
||||||
//remove this from setInterval
|
|
||||||
clearInterval(this.countdownInterval);
|
|
||||||
|
|
||||||
//time to close this notice
|
|
||||||
this.close();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.countdownTime == 3) {
|
|
||||||
//start fade out animation
|
|
||||||
let notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
|
|
||||||
notice.style.removeProperty("animation");
|
|
||||||
notice.classList.add("sponsorSkipNoticeFadeOut");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateTimerDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
pauseCountdown() {
|
|
||||||
//remove setInterval
|
|
||||||
clearInterval(this.countdownInterval);
|
|
||||||
this.countdownInterval = null;
|
|
||||||
|
|
||||||
//reset countdown
|
|
||||||
this.countdownTime = this.maxCountdownTime();
|
|
||||||
|
|
||||||
//inform the user
|
|
||||||
let timeLeft = document.getElementById("sponsorSkipNoticeTimeLeft" + this.idSuffix);
|
|
||||||
timeLeft.innerText = chrome.i18n.getMessage("paused");
|
|
||||||
|
|
||||||
//remove the fade out class if it exists
|
|
||||||
let notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
|
|
||||||
notice.classList.remove("sponsorSkipNoticeFadeOut");
|
|
||||||
notice.style.animation = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
startCountdown() {
|
|
||||||
//if it has already started, don't start it again
|
|
||||||
if (this.countdownInterval !== null) return;
|
|
||||||
|
|
||||||
this.countdownInterval = setInterval(this.countdown.bind(this), 1000);
|
|
||||||
|
|
||||||
this.updateTimerDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTimerDisplay() {
|
|
||||||
//update the timer display
|
|
||||||
let timeLeft = document.getElementById("sponsorSkipNoticeTimeLeft" + this.idSuffix);
|
|
||||||
timeLeft.innerText = this.countdownTime + "s";
|
|
||||||
}
|
|
||||||
|
|
||||||
unskip() {
|
|
||||||
this.contentContainer().unskipSponsorTime(this.UUID);
|
|
||||||
|
|
||||||
this.unskippedMode(chrome.i18n.getMessage("reskip"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets up notice to be not skipped yet */
|
|
||||||
unskippedMode(buttonText) {
|
|
||||||
//change unskip button to a reskip button
|
|
||||||
let unskipButton = this.changeUnskipButton(buttonText);
|
|
||||||
|
|
||||||
//setup new callback
|
|
||||||
this.unskipCallback = this.reskip.bind(this);
|
|
||||||
unskipButton.addEventListener("click", this.unskipCallback);
|
|
||||||
|
|
||||||
//change max duration to however much of the sponsor is left
|
|
||||||
this.maxCountdownTime = function() {
|
|
||||||
let sponsorTime = this.contentContainer().sponsorTimes[this.contentContainer().UUIDs.indexOf(this.UUID)];
|
|
||||||
let duration = Math.round(sponsorTime[1] - this.contentContainer().v.currentTime);
|
|
||||||
|
|
||||||
return Math.max(duration, 4);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.countdownTime = this.maxCountdownTime();
|
|
||||||
this.updateTimerDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
reskip() {
|
|
||||||
this.contentContainer().reskipSponsorTime(this.UUID);
|
|
||||||
|
|
||||||
//change reskip button to a unskip button
|
|
||||||
let unskipButton = this.changeUnskipButton(chrome.i18n.getMessage("unskip"));
|
|
||||||
|
|
||||||
//setup new callback
|
|
||||||
this.unskipCallback = this.unskip.bind(this);
|
|
||||||
unskipButton.addEventListener("click", this.unskipCallback);
|
|
||||||
|
|
||||||
//reset duration
|
|
||||||
this.maxCountdownTime = () => 4;
|
|
||||||
this.countdownTime = this.maxCountdownTime();
|
|
||||||
this.updateTimerDisplay();
|
|
||||||
|
|
||||||
// See if the title should be changed
|
|
||||||
if (this.manualSkip) {
|
|
||||||
this.changeNoticeTitle(chrome.i18n.getMessage("noticeTitle"));
|
|
||||||
|
|
||||||
if (Config.config.autoUpvote) this.contentContainer().vote(1, this.UUID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the text on the reskip button
|
|
||||||
*
|
|
||||||
* @param {string} text
|
|
||||||
* @returns {HTMLElement} unskipButton
|
|
||||||
*/
|
|
||||||
changeUnskipButton(text) {
|
|
||||||
let unskipButton = document.getElementById("sponsorSkipUnskipButton" + this.idSuffix);
|
|
||||||
unskipButton.innerText = text;
|
|
||||||
unskipButton.removeEventListener("click", this.unskipCallback);
|
|
||||||
|
|
||||||
return unskipButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
afterDownvote() {
|
|
||||||
this.addVoteButtonInfo(chrome.i18n.getMessage("voted"));
|
|
||||||
this.addNoticeInfoMessage(chrome.i18n.getMessage("hitGoBack"));
|
|
||||||
|
|
||||||
//remove this sponsor from the sponsors looked up
|
|
||||||
//find which one it is
|
|
||||||
for (let i = 0; i < this.contentContainer().sponsorTimes.length; i++) {
|
|
||||||
if (this.contentContainer().UUIDs[i] == this.UUID) {
|
|
||||||
//this one is the one to hide
|
|
||||||
|
|
||||||
//add this as a hidden sponsorTime
|
|
||||||
this.contentContainer().hiddenSponsorTimes.push(i);
|
|
||||||
|
|
||||||
this.contentContainer().updatePreviewBar();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
changeNoticeTitle(title) {
|
|
||||||
let noticeElement = document.getElementById("sponsorSkipMessage" + this.idSuffix);
|
|
||||||
|
|
||||||
noticeElement.innerText = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
addNoticeInfoMessage(message: string, message2: string = "") {
|
|
||||||
let previousInfoMessage = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix);
|
|
||||||
if (previousInfoMessage != null) {
|
|
||||||
//remove it
|
|
||||||
document.getElementById("sponsorSkipNotice" + this.idSuffix).removeChild(previousInfoMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
let previousInfoMessage2 = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix + "2");
|
|
||||||
if (previousInfoMessage2 != null) {
|
|
||||||
//remove it
|
|
||||||
document.getElementById("sponsorSkipNotice" + this.idSuffix).removeChild(previousInfoMessage2);
|
|
||||||
}
|
|
||||||
|
|
||||||
//add info
|
|
||||||
let thanksForVotingText = document.createElement("p");
|
|
||||||
thanksForVotingText.id = "sponsorTimesInfoMessage" + this.idSuffix;
|
|
||||||
thanksForVotingText.className = "sponsorTimesInfoMessage";
|
|
||||||
thanksForVotingText.innerText = message;
|
|
||||||
|
|
||||||
//add element to div
|
|
||||||
document.getElementById("sponsorSkipNotice" + this.idSuffix).insertBefore(thanksForVotingText, document.getElementById("sponsorSkipNoticeSpacer" + this.idSuffix));
|
|
||||||
|
|
||||||
if (message2 !== undefined) {
|
|
||||||
let thanksForVotingText2 = document.createElement("p");
|
|
||||||
thanksForVotingText2.id = "sponsorTimesInfoMessage" + this.idSuffix + "2";
|
|
||||||
thanksForVotingText2.className = "sponsorTimesInfoMessage";
|
|
||||||
thanksForVotingText2.innerText = message2;
|
|
||||||
|
|
||||||
//add element to div
|
|
||||||
document.getElementById("sponsorSkipNotice" + this.idSuffix).insertBefore(thanksForVotingText2, document.getElementById("sponsorSkipNoticeSpacer" + this.idSuffix));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resetNoticeInfoMessage() {
|
|
||||||
let previousInfoMessage = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix);
|
|
||||||
if (previousInfoMessage != null) {
|
|
||||||
//remove it
|
|
||||||
document.getElementById("sponsorSkipNotice" + this.idSuffix).removeChild(previousInfoMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addVoteButtonInfo(message) {
|
|
||||||
this.resetVoteButtonInfo();
|
|
||||||
|
|
||||||
//hide report button and text for it
|
|
||||||
let downvoteButton = document.getElementById("sponsorTimesDownvoteButtonsContainer" + this.idSuffix);
|
|
||||||
if (downvoteButton != null) {
|
|
||||||
downvoteButton.style.display = "none";
|
|
||||||
}
|
|
||||||
let downvoteButtonText = document.getElementById("sponsorTimesReportText" + this.idSuffix);
|
|
||||||
if (downvoteButtonText != null) {
|
|
||||||
downvoteButtonText.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
//add info
|
|
||||||
let thanksForVotingText = document.createElement("td");
|
|
||||||
thanksForVotingText.id = "sponsorTimesVoteButtonInfoMessage" + this.idSuffix;
|
|
||||||
thanksForVotingText.className = "sponsorTimesInfoMessage sponsorTimesVoteButtonMessage";
|
|
||||||
thanksForVotingText.innerText = message;
|
|
||||||
|
|
||||||
//add element to div
|
|
||||||
document.getElementById("sponsorSkipNoticeSecondRow" + this.idSuffix).prepend(thanksForVotingText);
|
|
||||||
}
|
|
||||||
|
|
||||||
resetVoteButtonInfo() {
|
|
||||||
let previousInfoMessage = document.getElementById("sponsorTimesVoteButtonInfoMessage" + this.idSuffix);
|
|
||||||
if (previousInfoMessage != null) {
|
|
||||||
//remove it
|
|
||||||
document.getElementById("sponsorSkipNoticeSecondRow" + this.idSuffix).removeChild(previousInfoMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
//show button again
|
|
||||||
document.getElementById("sponsorTimesDownvoteButtonsContainer" + this.idSuffix).style.removeProperty("display");
|
|
||||||
}
|
|
||||||
|
|
||||||
//close this notice
|
|
||||||
close() {
|
|
||||||
//reset message
|
|
||||||
this.resetNoticeInfoMessage();
|
|
||||||
|
|
||||||
let notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
|
|
||||||
if (notice != null) {
|
|
||||||
notice.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
//remove setInterval
|
|
||||||
if (this.countdownInterval !== null) clearInterval(this.countdownInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SkipNotice;
|
|
||||||
@@ -5,6 +5,7 @@ import * as CompileConfig from "../config.json";
|
|||||||
(<any> window).SB = Config;
|
(<any> window).SB = Config;
|
||||||
|
|
||||||
import Utils from "./utils";
|
import Utils from "./utils";
|
||||||
|
import CategoryChooser from "./render/CategoryChooser";
|
||||||
var utils = new Utils();
|
var utils = new Utils();
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', init);
|
window.addEventListener('DOMContentLoaded', init);
|
||||||
@@ -31,6 +32,8 @@ async function init() {
|
|||||||
let checkbox = optionsElements[i].querySelector("input");
|
let checkbox = optionsElements[i].querySelector("input");
|
||||||
let reverse = optionsElements[i].getAttribute("toggle-type") === "reverse";
|
let reverse = optionsElements[i].getAttribute("toggle-type") === "reverse";
|
||||||
|
|
||||||
|
let confirmMessage = optionsElements[i].getAttribute("confirm-message");
|
||||||
|
|
||||||
if (optionResult != undefined) {
|
if (optionResult != undefined) {
|
||||||
checkbox.checked = optionResult;
|
checkbox.checked = optionResult;
|
||||||
|
|
||||||
@@ -48,6 +51,12 @@ async function init() {
|
|||||||
|
|
||||||
// Add click listener
|
// Add click listener
|
||||||
checkbox.addEventListener("click", () => {
|
checkbox.addEventListener("click", () => {
|
||||||
|
// Confirm if required
|
||||||
|
if (checkbox.checked && confirmMessage && !confirm(chrome.i18n.getMessage(confirmMessage))){
|
||||||
|
checkbox.checked = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Config.config[option] = reverse ? !checkbox.checked : checkbox.checked;
|
Config.config[option] = reverse ? !checkbox.checked : checkbox.checked;
|
||||||
|
|
||||||
// See if anything extra must be run
|
// See if anything extra must be run
|
||||||
@@ -164,6 +173,9 @@ async function init() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
case "react-CategoryChooserComponent":
|
||||||
|
new CategoryChooser(optionsElements[i]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
95
src/popup.ts
95
src/popup.ts
@@ -1,6 +1,7 @@
|
|||||||
import Config from "./config";
|
import Config from "./config";
|
||||||
|
|
||||||
import Utils from "./utils";
|
import Utils from "./utils";
|
||||||
|
import { SponsorTime } from "./types";
|
||||||
var utils = new Utils();
|
var utils = new Utils();
|
||||||
|
|
||||||
interface MessageListener {
|
interface MessageListener {
|
||||||
@@ -56,7 +57,6 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||||||
"showNoticeAgain",
|
"showNoticeAgain",
|
||||||
"optionsButton",
|
"optionsButton",
|
||||||
// More controls
|
// More controls
|
||||||
"clearTimes",
|
|
||||||
"submitTimes",
|
"submitTimes",
|
||||||
"reportAnIssue",
|
"reportAnIssue",
|
||||||
// sponsorTimesContributions
|
// sponsorTimesContributions
|
||||||
@@ -82,9 +82,6 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||||||
// discordButtons
|
// discordButtons
|
||||||
"discordButtonContainer",
|
"discordButtonContainer",
|
||||||
"hideDiscordButton",
|
"hideDiscordButton",
|
||||||
// submitTimesInfoMessage
|
|
||||||
"submitTimesInfoMessageContainer",
|
|
||||||
"submitTimesInfoMessage",
|
|
||||||
// Username
|
// Username
|
||||||
"setUsernameContainer",
|
"setUsernameContainer",
|
||||||
"setUsernameButton",
|
"setUsernameButton",
|
||||||
@@ -108,7 +105,6 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||||||
PageElements.unwhitelistChannel.addEventListener("click", unwhitelistChannel);
|
PageElements.unwhitelistChannel.addEventListener("click", unwhitelistChannel);
|
||||||
PageElements.disableSkipping.addEventListener("click", () => toggleSkipping(true));
|
PageElements.disableSkipping.addEventListener("click", () => toggleSkipping(true));
|
||||||
PageElements.enableSkipping.addEventListener("click", () => toggleSkipping(false));
|
PageElements.enableSkipping.addEventListener("click", () => toggleSkipping(false));
|
||||||
PageElements.clearTimes.addEventListener("click", clearTimes);
|
|
||||||
PageElements.submitTimes.addEventListener("click", submitTimes);
|
PageElements.submitTimes.addEventListener("click", submitTimes);
|
||||||
PageElements.showNoticeAgain.addEventListener("click", showNoticeAgain);
|
PageElements.showNoticeAgain.addEventListener("click", showNoticeAgain);
|
||||||
PageElements.setUsernameButton.addEventListener("click", setUsernameButton);
|
PageElements.setUsernameButton.addEventListener("click", setUsernameButton);
|
||||||
@@ -263,8 +259,6 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||||||
|
|
||||||
sponsorTimes = sponsorTimesStorage;
|
sponsorTimes = sponsorTimesStorage;
|
||||||
|
|
||||||
displaySponsorTimes();
|
|
||||||
|
|
||||||
//show submission section
|
//show submission section
|
||||||
PageElements.submissionSection.style.display = "unset";
|
PageElements.submissionSection.style.display = "unset";
|
||||||
|
|
||||||
@@ -279,7 +273,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function infoFound(request) {
|
function infoFound(request: {found: boolean, sponsorTimes: SponsorTime[], hiddenSponsorTimes: number[]}) {
|
||||||
if(chrome.runtime.lastError) {
|
if(chrome.runtime.lastError) {
|
||||||
//This page doesn't have the injected content script, or at least not yet
|
//This page doesn't have the injected content script, or at least not yet
|
||||||
displayNoVideo();
|
displayNoVideo();
|
||||||
@@ -363,28 +357,14 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||||||
|
|
||||||
updateStartTimeChosen();
|
updateStartTimeChosen();
|
||||||
|
|
||||||
//display video times on screen
|
|
||||||
displaySponsorTimes();
|
|
||||||
|
|
||||||
//show submission section
|
//show submission section
|
||||||
PageElements.submissionSection.style.display = "unset";
|
PageElements.submissionSection.style.display = "unset";
|
||||||
|
|
||||||
showSubmitTimesIfNecessary();
|
showSubmitTimesIfNecessary();
|
||||||
}
|
}
|
||||||
|
|
||||||
//display the video times from the array
|
|
||||||
function displaySponsorTimes() {
|
|
||||||
//remove all children
|
|
||||||
while (PageElements.sponsorMessageTimes.firstChild) {
|
|
||||||
PageElements.sponsorMessageTimes.removeChild(PageElements.sponsorMessageTimes.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
//add sponsor times
|
|
||||||
PageElements.sponsorMessageTimes.appendChild(getSponsorTimesMessageDiv(sponsorTimes));
|
|
||||||
}
|
|
||||||
|
|
||||||
//display the video times from the array at the top, in a different section
|
//display the video times from the array at the top, in a different section
|
||||||
function displayDownloadedSponsorTimes(request) {
|
function displayDownloadedSponsorTimes(request: {found: boolean, sponsorTimes: SponsorTime[], hiddenSponsorTimes: number[]}) {
|
||||||
if (request.sponsorTimes != undefined) {
|
if (request.sponsorTimes != undefined) {
|
||||||
//set it to the message
|
//set it to the message
|
||||||
if (PageElements.downloadedSponsorMessageTimes.innerText != chrome.i18n.getMessage("channelWhitelisted")) {
|
if (PageElements.downloadedSponsorMessageTimes.innerText != chrome.i18n.getMessage("channelWhitelisted")) {
|
||||||
@@ -403,11 +383,11 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||||||
extraInfo = " (hidden)";
|
extraInfo = " (hidden)";
|
||||||
}
|
}
|
||||||
|
|
||||||
sponsorTimeButton.innerText = getFormattedTime(request.sponsorTimes[i][0]) + " to " + getFormattedTime(request.sponsorTimes[i][1]) + extraInfo;
|
sponsorTimeButton.innerText = getFormattedTime(request.sponsorTimes[i].segment[0]) + " to " + getFormattedTime(request.sponsorTimes[i].segment[1]) + extraInfo;
|
||||||
|
|
||||||
let votingButtons = document.createElement("div");
|
let votingButtons = document.createElement("div");
|
||||||
|
|
||||||
let UUID = request.UUIDs[i];
|
let UUID = request.sponsorTimes[i].UUID;
|
||||||
|
|
||||||
//thumbs up and down buttons
|
//thumbs up and down buttons
|
||||||
let voteButtonsContainer = document.createElement("div");
|
let voteButtonsContainer = document.createElement("div");
|
||||||
@@ -451,12 +431,12 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//get the message that visually displays the video times
|
//get the message that visually displays the video times
|
||||||
function getSponsorTimesMessage(sponsorTimes) {
|
function getSponsorTimesMessage(sponsorTimes: SponsorTime[]) {
|
||||||
let sponsorTimesMessage = "";
|
let sponsorTimesMessage = "";
|
||||||
|
|
||||||
for (let i = 0; i < sponsorTimes.length; i++) {
|
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||||
for (let s = 0; s < sponsorTimes[i].length; s++) {
|
for (let s = 0; s < sponsorTimes[i].segment.length; s++) {
|
||||||
let timeMessage = getFormattedTime(sponsorTimes[i][s]);
|
let timeMessage = getFormattedTime(sponsorTimes[i].segment[s]);
|
||||||
//if this is an end time
|
//if this is an end time
|
||||||
if (s == 1) {
|
if (s == 1) {
|
||||||
timeMessage = " to " + timeMessage;
|
timeMessage = " to " + timeMessage;
|
||||||
@@ -692,8 +672,6 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (closeEditMode) {
|
if (closeEditMode) {
|
||||||
displaySponsorTimes();
|
|
||||||
|
|
||||||
showSubmitTimesIfNecessary();
|
showSubmitTimesIfNecessary();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -721,9 +699,6 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||||||
//save this
|
//save this
|
||||||
Config.config.sponsorTimes.set(currentVideoID, sponsorTimes);
|
Config.config.sponsorTimes.set(currentVideoID, sponsorTimes);
|
||||||
|
|
||||||
//update display
|
|
||||||
displaySponsorTimes();
|
|
||||||
|
|
||||||
//if they are all removed
|
//if they are all removed
|
||||||
if (sponsorTimes.length == 0) {
|
if (sponsorTimes.length == 0) {
|
||||||
//update chrome tab
|
//update chrome tab
|
||||||
@@ -753,67 +728,17 @@ async function runThePopup(messageListener?: MessageListener) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearTimes() {
|
function submitTimes() {
|
||||||
//send new sponsor time state to tab
|
|
||||||
if (sponsorTimes.length > 0) {
|
if (sponsorTimes.length > 0) {
|
||||||
messageHandler.query({
|
|
||||||
active: true,
|
|
||||||
currentWindow: true
|
|
||||||
}, function(tabs) {
|
|
||||||
messageHandler.sendMessage(tabs[0].id, {
|
|
||||||
message: "changeStartSponsorButton",
|
|
||||||
showStartSponsor: true,
|
|
||||||
uploadButtonVisible: false
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//reset sponsorTimes
|
|
||||||
sponsorTimes = [];
|
|
||||||
|
|
||||||
Config.config.sponsorTimes.set(currentVideoID, sponsorTimes);
|
|
||||||
messageHandler.query({
|
messageHandler.query({
|
||||||
active: true,
|
active: true,
|
||||||
currentWindow: true
|
currentWindow: true
|
||||||
}, tabs => {
|
}, tabs => {
|
||||||
messageHandler.sendMessage(
|
messageHandler.sendMessage(
|
||||||
tabs[0].id,
|
tabs[0].id,
|
||||||
{message: "sponsorDataChanged"}
|
{message: 'submitTimes'},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
displaySponsorTimes();
|
|
||||||
|
|
||||||
//hide submission section
|
|
||||||
document.getElementById("submissionSection").style.display = "none";
|
|
||||||
|
|
||||||
resetStartTimeChosen();
|
|
||||||
}
|
|
||||||
|
|
||||||
function submitTimes() {
|
|
||||||
//make info message say loading
|
|
||||||
PageElements.submitTimesInfoMessage.innerText = chrome.i18n.getMessage("Loading");
|
|
||||||
PageElements.submitTimesInfoMessageContainer.style.display = "unset";
|
|
||||||
|
|
||||||
if (sponsorTimes.length > 0) {
|
|
||||||
chrome.runtime.sendMessage({
|
|
||||||
message: "submitTimes",
|
|
||||||
videoID: currentVideoID
|
|
||||||
}, function(response) {
|
|
||||||
if (response != undefined) {
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
//hide loading message
|
|
||||||
PageElements.submitTimesInfoMessageContainer.style.display = "none";
|
|
||||||
|
|
||||||
clearTimes();
|
|
||||||
} else {
|
|
||||||
document.getElementById("submitTimesInfoMessage").innerText = utils.getErrorMessage(response.statusCode);
|
|
||||||
document.getElementById("submitTimesInfoMessageContainer").style.display = "unset";
|
|
||||||
|
|
||||||
PageElements.submitTimesInfoMessageContainer.style.display = "unset";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
src/render/CategoryChooser.tsx
Normal file
15
src/render/CategoryChooser.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as ReactDOM from "react-dom";
|
||||||
|
import CategoryChooserComponent from "../components/CategoryChooserComponent";
|
||||||
|
|
||||||
|
class CategoryChooser {
|
||||||
|
|
||||||
|
constructor(element: Element) {
|
||||||
|
ReactDOM.render(
|
||||||
|
<CategoryChooserComponent/>,
|
||||||
|
element
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CategoryChooser;
|
||||||
50
src/render/SkipNotice.tsx
Normal file
50
src/render/SkipNotice.tsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as ReactDOM from "react-dom";
|
||||||
|
|
||||||
|
import SkipNoticeComponent from "../components/SkipNoticeComponent";
|
||||||
|
|
||||||
|
class SkipNotice {
|
||||||
|
UUID: string;
|
||||||
|
autoSkip: boolean;
|
||||||
|
// Contains functions and variables from the content script needed by the skip notice
|
||||||
|
contentContainer: () => any;
|
||||||
|
|
||||||
|
constructor(UUID: string, autoSkip: boolean = false, contentContainer) {
|
||||||
|
this.UUID = UUID;
|
||||||
|
this.autoSkip = autoSkip;
|
||||||
|
this.contentContainer = contentContainer;
|
||||||
|
|
||||||
|
//get reference node
|
||||||
|
let referenceNode = document.getElementById("player-container-id")
|
||||||
|
|| document.getElementById("movie_player") || document.querySelector("#player-container .video-js");
|
||||||
|
if (referenceNode == null) {
|
||||||
|
//for embeds
|
||||||
|
let player = document.getElementById("player");
|
||||||
|
referenceNode = player.firstChild as HTMLElement;
|
||||||
|
let index = 1;
|
||||||
|
|
||||||
|
//find the child that is the video player (sometimes it is not the first)
|
||||||
|
while (!referenceNode.classList.contains("html5-video-player") || !referenceNode.classList.contains("ytp-embed")) {
|
||||||
|
referenceNode = player.children[index] as HTMLElement;
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
|
||||||
|
//this is the suffix added at the end of every id
|
||||||
|
let idSuffix = this.UUID + amountOfPreviousNotices;
|
||||||
|
|
||||||
|
let noticeElement = document.createElement("div");
|
||||||
|
noticeElement.id = "sponsorSkipNoticeContainer" + idSuffix;
|
||||||
|
|
||||||
|
referenceNode.prepend(noticeElement);
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<SkipNoticeComponent UUID={UUID} autoSkip={autoSkip} contentContainer={contentContainer} />,
|
||||||
|
noticeElement
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SkipNotice;
|
||||||
56
src/render/SubmissionNotice.tsx
Normal file
56
src/render/SubmissionNotice.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as ReactDOM from "react-dom";
|
||||||
|
|
||||||
|
import SubmissionNoticeComponent from "../components/SubmissionNoticeComponent";
|
||||||
|
|
||||||
|
class SubmissionNotice {
|
||||||
|
// Contains functions and variables from the content script needed by the skip notice
|
||||||
|
contentContainer: () => any;
|
||||||
|
|
||||||
|
callback: () => any;
|
||||||
|
|
||||||
|
noticeRef: React.MutableRefObject<SubmissionNoticeComponent>;
|
||||||
|
|
||||||
|
constructor(contentContainer: () => any, callback: () => any) {
|
||||||
|
this.noticeRef = React.createRef();
|
||||||
|
|
||||||
|
this.contentContainer = contentContainer;
|
||||||
|
this.callback = callback;
|
||||||
|
|
||||||
|
//get reference node
|
||||||
|
let referenceNode = document.getElementById("player-container-id")
|
||||||
|
|| document.getElementById("movie_player") || document.querySelector("#player-container .video-js");
|
||||||
|
if (referenceNode == null) {
|
||||||
|
//for embeds
|
||||||
|
let player = document.getElementById("player");
|
||||||
|
referenceNode = player.firstChild as HTMLElement;
|
||||||
|
let index = 1;
|
||||||
|
|
||||||
|
//find the child that is the video player (sometimes it is not the first)
|
||||||
|
while (!referenceNode.classList.contains("html5-video-player") || !referenceNode.classList.contains("ytp-embed")) {
|
||||||
|
referenceNode = player.children[index] as HTMLElement;
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let noticeElement = document.createElement("div");
|
||||||
|
noticeElement.id = "submissionNoticeContainer";
|
||||||
|
|
||||||
|
referenceNode.prepend(noticeElement);
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<SubmissionNoticeComponent
|
||||||
|
contentContainer={contentContainer}
|
||||||
|
callback={callback}
|
||||||
|
ref={this.noticeRef} />,
|
||||||
|
noticeElement
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.noticeRef.current.forceUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SubmissionNotice;
|
||||||
52
src/types.ts
52
src/types.ts
@@ -1,7 +1,55 @@
|
|||||||
interface videoDurationResponse {
|
import SubmissionNotice from "./render/SubmissionNotice";
|
||||||
|
import SkipNoticeComponent from "./components/SkipNoticeComponent";
|
||||||
|
|
||||||
|
interface ContentContainer {
|
||||||
|
(): {
|
||||||
|
vote: (type: any, UUID: any, skipNotice?: SkipNoticeComponent) => void,
|
||||||
|
dontShowNoticeAgain: () => void,
|
||||||
|
unskipSponsorTime: (UUID: any) => void,
|
||||||
|
sponsorTimes: SponsorTime[],
|
||||||
|
sponsorTimesSubmitting: SponsorTime[],
|
||||||
|
hiddenSponsorTimes: number[],
|
||||||
|
v: HTMLVideoElement,
|
||||||
|
sponsorVideoID,
|
||||||
|
reskipSponsorTime: (UUID: any) => void,
|
||||||
|
updatePreviewBar: () => void,
|
||||||
|
onMobileYouTube: boolean,
|
||||||
|
sponsorSubmissionNotice: SubmissionNotice,
|
||||||
|
resetSponsorSubmissionNotice: () => void,
|
||||||
|
changeStartSponsorButton: (showStartSponsor: any, uploadButtonVisible: any) => Promise<boolean>,
|
||||||
|
previewTime: (time: number) => void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VideoDurationResponse {
|
||||||
duration: number;
|
duration: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CategorySkipOption {
|
||||||
|
ShowOverlay,
|
||||||
|
ManualSkip,
|
||||||
|
AutoSkip
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CategorySelection {
|
||||||
|
name: string;
|
||||||
|
option: CategorySkipOption
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SponsorTime {
|
||||||
|
segment: number[];
|
||||||
|
UUID: string;
|
||||||
|
|
||||||
|
category: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type VideoID = string;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
videoDurationResponse
|
VideoDurationResponse,
|
||||||
|
ContentContainer,
|
||||||
|
CategorySelection,
|
||||||
|
CategorySkipOption,
|
||||||
|
SponsorTime,
|
||||||
|
VideoID
|
||||||
};
|
};
|
||||||
107
src/utils.ts
107
src/utils.ts
@@ -1,4 +1,7 @@
|
|||||||
import Config from "./config";
|
import Config from "./config";
|
||||||
|
import { CategorySelection, SponsorTime } from "./types";
|
||||||
|
|
||||||
|
import * as CompileConfig from "../config.json";
|
||||||
|
|
||||||
class Utils {
|
class Utils {
|
||||||
|
|
||||||
@@ -154,6 +157,42 @@ class Utils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets just the timestamps from a sponsorTimes array
|
||||||
|
*
|
||||||
|
* @param sponsorTimes
|
||||||
|
*/
|
||||||
|
getSegmentsFromSponsorTimes(sponsorTimes: SponsorTime[]): number[][] {
|
||||||
|
let segments: number[][] = [];
|
||||||
|
for (const sponsorTime of sponsorTimes) {
|
||||||
|
segments.push(sponsorTime.segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSponsorIndexFromUUID(sponsorTimes: SponsorTime[], UUID: string): number {
|
||||||
|
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||||
|
if (sponsorTimes[i].UUID === UUID) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSponsorTimeFromUUID(sponsorTimes: SponsorTime[], UUID: string): SponsorTime {
|
||||||
|
return sponsorTimes[this.getSponsorIndexFromUUID(sponsorTimes, UUID)];
|
||||||
|
}
|
||||||
|
|
||||||
|
getCategorySelection(category: string): CategorySelection {
|
||||||
|
for (const selection of Config.config.categorySelections) {
|
||||||
|
if (selection.name === category) {
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
localizeHtmlPage() {
|
localizeHtmlPage() {
|
||||||
//Localize by replacing __MSG_***__ meta tags
|
//Localize by replacing __MSG_***__ meta tags
|
||||||
var objects = document.getElementsByClassName("sponsorBlockPageBody")[0].children;
|
var objects = document.getElementsByClassName("sponsorBlockPageBody")[0].children;
|
||||||
@@ -230,6 +269,39 @@ class Utils {
|
|||||||
return errorMessage;
|
return errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to the SponsorBlock server with address added as a query
|
||||||
|
*
|
||||||
|
* @param type The request type. "GET", "POST", etc.
|
||||||
|
* @param address The address to add to the SponsorBlock server address
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
async asyncRequestToServer(type: string, address: string, data = {}) {
|
||||||
|
let serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
|
||||||
|
|
||||||
|
// If GET, convert JSON to parameters
|
||||||
|
if (type.toLowerCase() === "get") {
|
||||||
|
for (const key in data) {
|
||||||
|
let seperator = address.includes("?") ? "&" : "?";
|
||||||
|
let value = (typeof(data[key]) === "string") ? data[key]: JSON.stringify(data[key]);
|
||||||
|
address += seperator + key + "=" + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(serverAddress + address, {
|
||||||
|
method: type,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
redirect: 'follow',
|
||||||
|
body: data ? JSON.stringify(data) : null
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a request to the SponsorBlock server with address added as a query
|
* Sends a request to the SponsorBlock server with address added as a query
|
||||||
*
|
*
|
||||||
@@ -240,7 +312,9 @@ class Utils {
|
|||||||
sendRequestToServer(type: string, address: string, callback?: (xmlhttp: XMLHttpRequest, err: boolean) => any) {
|
sendRequestToServer(type: string, address: string, callback?: (xmlhttp: XMLHttpRequest, err: boolean) => any) {
|
||||||
let xmlhttp = new XMLHttpRequest();
|
let xmlhttp = new XMLHttpRequest();
|
||||||
|
|
||||||
xmlhttp.open(type, Config.config.serverAddress + address, true);
|
let serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
|
||||||
|
|
||||||
|
xmlhttp.open(type, serverAddress + address, true);
|
||||||
|
|
||||||
if (callback != undefined) {
|
if (callback != undefined) {
|
||||||
xmlhttp.onreadystatechange = function () {
|
xmlhttp.onreadystatechange = function () {
|
||||||
@@ -256,6 +330,37 @@ class Utils {
|
|||||||
xmlhttp.send();
|
xmlhttp.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFormattedMinutes(seconds: number) {
|
||||||
|
return Math.floor(seconds / 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormattedSeconds(seconds: number) {
|
||||||
|
return seconds % 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormattedTime(seconds: number, precise?: boolean) {
|
||||||
|
let minutes = Math.floor(seconds / 60);
|
||||||
|
let secondsNum: number = seconds - minutes * 60;
|
||||||
|
if (!precise) {
|
||||||
|
secondsNum = Math.floor(secondsNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
let secondsDisplay: string = String(secondsNum.toFixed(3));
|
||||||
|
|
||||||
|
if (secondsNum < 10) {
|
||||||
|
//add a zero
|
||||||
|
secondsDisplay = "0" + secondsDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
let formatted = minutes + ":" + secondsDisplay;
|
||||||
|
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRawSeconds(minutes: number, seconds: number): number {
|
||||||
|
return minutes * 60 + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this Firefox (web-extensions)
|
* Is this Firefox (web-extensions)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"outDir": "dist/js",
|
"outDir": "dist/js",
|
||||||
"noEmitOnError": true,
|
"noEmitOnError": true,
|
||||||
"typeRoots": [ "node_modules/@types" ],
|
"typeRoots": [ "node_modules/@types" ],
|
||||||
"resolveJsonModule": true
|
"resolveJsonModule": true,
|
||||||
|
"jsx": "react"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user