aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/release.yml39
-rw-r--r--config.json.example2
-rw-r--r--manifest/manifest.json2
-rw-r--r--package.json1
-rw-r--r--public/_locales/en/messages.json24
-rw-r--r--public/content.css17
-rw-r--r--public/popup.html2
-rw-r--r--src/background.ts8
-rw-r--r--src/components/SkipNoticeComponent.tsx174
-rw-r--r--src/components/SponsorTimeEditComponent.tsx28
-rw-r--r--src/components/SubmissionNoticeComponent.tsx19
-rw-r--r--src/config.ts4
-rw-r--r--src/content.ts61
-rw-r--r--src/js-components/previewBar.ts86
-rw-r--r--src/types.ts6
15 files changed, 407 insertions, 66 deletions
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 2437a778..1043661f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -28,7 +28,7 @@ jobs:
- run: mkdir ./builds
- uses: montudor/[email protected]
with:
- args: zip -qq -r ./builds/ChromeExtension.zip ./dist
+ args: zip -qq -r ./builds/ChromeExtension.zip ./dist/*
# Create Firefox artifacts
- name: Create Firefox artifacts
@@ -39,7 +39,7 @@ jobs:
path: dist
- uses: montudor/[email protected]
with:
- args: zip -qq -r ./builds/FirefoxExtension.zip ./dist
+ args: zip -qq -r ./builds/FirefoxExtension.zip ./dist/*
# Create Beta artifacts (Builds with the name changed to beta)
- name: Create Chrome Beta artifacts
@@ -50,7 +50,7 @@ jobs:
path: dist
- uses: montudor/[email protected]
with:
- args: zip -qq -r ./builds/ChromeExtensionBeta.zip ./dist
+ args: zip -qq -r ./builds/ChromeExtensionBeta.zip ./dist/*
- name: Create Firefox Beta artifacts
run: npm run build:firefox -- --env.stream=beta
@@ -60,7 +60,24 @@ jobs:
path: dist
- uses: montudor/[email protected]
with:
- args: zip -qq -r ./builds/FirefoxExtensionBeta.zip ./dist
+ args: zip -qq -r ./builds/FirefoxExtensionBeta.zip ./dist/*
+
+ # Create Firefox Signed Beta version
+ - name: Create Firefox Signed Beta artifacts
+ run: npm run web-sign
+ env:
+ WEB_EXT_API_KEY: ${{ secrets.WEB_EXT_API_KEY }}
+ WEB_EXT_API_SECRET: ${{ secrets.WEB_EXT_API_SECRET }}
+ - name: Install rename
+ run: sudo apt-get install rename
+ - name: Install signed file
+ run: cd ./web-ext-artifacts
+ run: rename 's/.*/FirefoxSignedInstaller.xpi/' *
+ run: cd ..
+ - uses: actions/upload-artifact@v1
+ with:
+ name: FirefoxExtensionSigned.xpi
+ path: ./web-ext-artifacts/FirefoxSignedInstaller.xpi
# Upload each release asset
- name: Upload to release
@@ -73,8 +90,22 @@ jobs:
- name: Upload to release
uses: Shopify/upload-to-release@master
with:
+ args: builds/ChromeExtensionBeta.zip
+ name: ChromeExtensionBeta.zip
+ path: ./builds/ChromeExtensionBeta.zip
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ - name: Upload to release
+ uses: Shopify/upload-to-release@master
+ with:
args: builds/FirefoxExtension.zip
name: FirefoxExtension.zip
path: ./builds/FirefoxExtension.zip
repo-token: ${{ secrets.GITHUB_TOKEN }}
+ - name: Upload to release
+ uses: Shopify/upload-to-release@master
+ with:
+ args: web-ext-artifacts/FirefoxSignedInstaller.xpi
+ name: FirefoxSignedInstaller.xpi
+ path: ./web-ext-artifacts/FirefoxSignedInstaller.xpi
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/config.json.example b/config.json.example
index 0919d30e..aad02a3c 100644
--- a/config.json.example
+++ b/config.json.example
@@ -2,5 +2,5 @@
"serverAddress": "https://sponsor.ajay.app",
"testingServerAddress": "https://sponsor.ajay.app/test",
"serverAddressComment": "This specifies the default SponsorBlock server to conect to",
- "categoryList": ["sponsor", "intro", "outro", "interaction", "selfpromo", "offtopic"]
+ "categoryList": ["sponsor", "intro", "outro", "interaction", "selfpromo", "music_offtopic"]
}
diff --git a/manifest/manifest.json b/manifest/manifest.json
index 5b302f1f..7a41a671 100644
--- a/manifest/manifest.json
+++ b/manifest/manifest.json
@@ -1,7 +1,7 @@
{
"name": "__MSG_fullName__",
"short_name": "__MSG_Name__",
- "version": "1.2.27",
+ "version": "1.2.28",
"default_locale": "en",
"description": "__MSG_Description__",
"content_scripts": [{
diff --git a/package.json b/package.json
index c778277c..bccb8af8 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
},
"scripts": {
"web-run": "npm run web-run:chrome",
+ "web-sign": "web-ext sign -s dist",
"web-run:firefox": "cd dist && web-ext run --start-url https://addons.mozilla.org/firefox/addon/ublock-origin/",
"web-run:chrome": "cd dist && web-ext run --start-url https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm -t chromium",
"build": "npm run build:chrome",
diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json
index 086ce0d5..864b957b 100644
--- a/public/_locales/en/messages.json
+++ b/public/_locales/en/messages.json
@@ -351,7 +351,7 @@
"message": "Support Invidious"
},
"supportInvidiousDescription": {
- "message": "Invidious (invidio.us) is a third party YouTube client. To enable support, you must accept the extra permissions. This does NOT work in incongnito on Chrome and other Chromium variants."
+ "message": "Invidious (invidio.us) is a third party YouTube client. To enable support, you must accept the extra permissions. This does NOT work in incognito on Chrome and other Chromium variants."
},
"optionsInfo": {
"message": "Enable Invidious support, disable autoskip, hide buttons and more."
@@ -490,19 +490,22 @@
"message": "Sponsor"
},
"category_intro": {
- "message": "Intro"
+ "message": "Intro Animation"
},
"category_outro": {
- "message": "Outro"
+ "message": "Endcards/Credits"
},
"category_interaction": {
- "message": "Interaction (Redundant Like, Subscribe, Follow, etc.)"
+ "message": "Interaction Reminder (Subscribe)"
},
"category_selfpromo": {
"message": "Self-Promotion and Merchandise"
},
- "category_offtopic": {
- "message": "Offtopic tangent (Subjective)"
+ "category_music_offtopic": {
+ "message": "Music: Non-Music Section"
+ },
+ "category_livestream_messages": {
+ "message": "Livestream: Donation/Message Readings"
},
"disable": {
"message": "Disable"
@@ -554,5 +557,14 @@
},
"forceChannelCheckPopup": {
"message": "Consider Enabling Force Channel Check Before Skipping Sponsors"
+ },
+ "downvoteDescription": {
+ "message": "Incorrect"
+ },
+ "incorrectCategory": {
+ "message": "Wrong Category"
+ },
+ "nonMusicCategoryOnMusic": {
+ "message": "This video is categorized as music. Are you sure you would like to submit segments with non-music categories? Unless this video is not actually music, you should not be submitting this segment. Please read the guidelines if you are confused."
}
}
diff --git a/public/content.css b/public/content.css
index 51213bd2..66838a9f 100644
--- a/public/content.css
+++ b/public/content.css
@@ -11,11 +11,28 @@
z-index: 40;
}
+.sbHidden {
+ display: none !important;
+}
+
+
.previewbar {
display: inline-block;
height: 100%;
}
+/* Preview Bar page hacks */
+
+.sbTooltipTwoTitleThumbnailOffset {
+ bottom: -5px !important;
+}
+
+.sbTooltipOneTitleThumbnailOffset {
+ bottom: 10px !important;
+}
+
+/* */
+
.popup {
z-index: 10;
width: 100%;
diff --git a/public/popup.html b/public/popup.html
index 3fdb3a73..bede3db5 100644
--- a/public/popup.html
+++ b/public/popup.html
@@ -135,7 +135,7 @@
<span id="sponsorTimesSkipsDoneDisplay" class="popupElement">
0
</span>
- <span id="sponsorTimesSkipsDoneEndWord" class="popupElement">__MSG_Segments__</span> (since February).
+ <span id="sponsorTimesSkipsDoneEndWord" class="popupElement">__MSG_Segments__</span>
</div>
<div id="sponsorTimeSavedContainer" class="popupElement" style="display: none">
diff --git a/src/background.ts b/src/background.ts
index b204b64c..74382357 100644
--- a/src/background.ts
+++ b/src/background.ts
@@ -47,7 +47,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
//this allows the callback to be called later
return true;
case "submitVote":
- submitVote(request.type, request.UUID, callback);
+ submitVote(request.type, request.UUID, request.category, callback);
//this allows the callback to be called later
return true;
@@ -147,7 +147,7 @@ function addSponsorTime(time, videoID, callback) {
});
}
-function submitVote(type, UUID, callback) {
+function submitVote(type, UUID, category, callback) {
let userID = Config.config.userID;
if (userID == undefined || userID === "undefined") {
@@ -156,8 +156,10 @@ function submitVote(type, UUID, callback) {
Config.config.userID = userID;
}
+ let typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
+
//publish this vote
- utils.sendRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + "&type=" + type, function(xmlhttp, error) {
+ utils.sendRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection, function(xmlhttp, error) {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
callback({
successType: 1
diff --git a/src/components/SkipNoticeComponent.tsx b/src/components/SkipNoticeComponent.tsx
index 8d53efbb..b4399e65 100644
--- a/src/components/SkipNoticeComponent.tsx
+++ b/src/components/SkipNoticeComponent.tsx
@@ -1,4 +1,5 @@
import * as React from "react";
+import * as CompileConfig from "../../config.json";
import Config from "../config"
import { ContentContainer, SponsorHideType } from "../types";
@@ -19,16 +20,19 @@ export interface SkipNoticeProps {
}
export interface SkipNoticeState {
- noticeTitle: string,
+ noticeTitle: string;
- messages: string[],
+ messages: string[];
- countdownTime: number,
+ countdownTime: number;
maxCountdownTime: () => number;
- countdownText: string,
+ countdownText: string;
- unskipText: string,
- unskipCallback: () => void
+ unskipText: string;
+ unskipCallback: () => void;
+
+ downvoting: boolean;
+ choosingCategory: boolean;
}
class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeState> {
@@ -43,10 +47,15 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
idSuffix: any;
noticeRef: React.MutableRefObject<NoticeComponent>;
+ categoryOptionRef: React.RefObject<HTMLSelectElement>;
+
+ // Used to update on config change
+ configListener: () => void;
constructor(props: SkipNoticeProps) {
super(props);
this.noticeRef = React.createRef();
+ this.categoryOptionRef = React.createRef();
this.UUID = props.UUID;
this.autoSkip = props.autoSkip;
@@ -83,7 +92,10 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
countdownText: null,
unskipText: chrome.i18n.getMessage("unskip"),
- unskipCallback: this.unskip.bind(this)
+ unskipCallback: this.unskip.bind(this),
+
+ downvoting: false,
+ choosingCategory: false
}
if (!this.autoSkip) {
@@ -115,7 +127,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
timed={true}
maxCountdownTime={this.state.maxCountdownTime}
ref={this.noticeRef}
- closeListener={this.props.closeListener}>
+ closeListener={() => this.closeListener()}>
{(Config.config.audioNotificationOnSkip) && <audio ref={(source) => { this.audio = source; }}>
<source src={chrome.extension.getURL("icons/beep.ogg")} type="audio/ogg"></source>
@@ -124,7 +136,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
{/* Text Boxes */}
{this.getMessageBoxes()}
- {/* Last Row */}
+ {/* Bottom Row */}
<tr id={"sponsorSkipNoticeSecondRow" + this.idSuffix}>
{/* Vote Button Container */}
@@ -145,7 +157,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
className="sponsorSkipObject voteButton"
src={chrome.extension.getURL("icons/report.png")}
title={chrome.i18n.getMessage("reportButtonInfo")}
- onClick={() => this.contentContainer().vote(0, this.UUID, this)}>
+ onClick={() => this.adjustDownvotingState(true)}>
</img>
@@ -174,6 +186,54 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}
</tr>
+ {/* Downvote Options Row */}
+ {this.state.downvoting &&
+ <tr id={"sponsorSkipNoticeDownvoteOptionsRow" + this.idSuffix}>
+ <td id={"sponsorTimesDownvoteOptionsContainer" + this.idSuffix}>
+
+ {/* Normal downvote */}
+ <button className="sponsorSkipObject sponsorSkipNoticeButton"
+ onClick={() => this.contentContainer().vote(0, this.UUID, undefined, this)}>
+ {chrome.i18n.getMessage("downvoteDescription")}
+ </button>
+
+ {/* Category vote */}
+ {Config.config.testingServer &&
+ <button className="sponsorSkipObject sponsorSkipNoticeButton"
+ onClick={() => this.openCategoryChooser()}>
+
+ {chrome.i18n.getMessage("incorrectCategory")}
+ </button>
+ }
+ </td>
+
+ </tr>
+ }
+
+ {/* Category Chooser Row */}
+ {this.state.choosingCategory &&
+ <tr id={"sponsorSkipNoticeCategoryChooserRow" + this.idSuffix}>
+ <td>
+ {/* Category Selector */}
+ <select id={"sponsorTimeCategories" + this.idSuffix}
+ className="sponsorTimeCategories"
+ defaultValue={utils.getSponsorTimeFromUUID(this.props.contentContainer().sponsorTimes, this.props.UUID).category}
+ ref={this.categoryOptionRef}
+ onChange={this.categorySelectionChange.bind(this)}>
+
+ {this.getCategoryOptions()}
+ </select>
+
+ {/* Submit Button */}
+ <button className="sponsorSkipObject sponsorSkipNoticeButton"
+ onClick={() => this.contentContainer().vote(undefined, this.UUID, this.categoryOptionRef.current.value, this)}>
+
+ {chrome.i18n.getMessage("submit")}
+ </button>
+ </td>
+ </tr>
+ }
+
</NoticeComponent>
);
}
@@ -202,6 +262,70 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
return elements;
}
+ adjustDownvotingState(value: boolean) {
+ if (!value) this.clearConfigListener();
+
+ this.setState({
+ downvoting: value,
+ choosingCategory: false
+ });
+ }
+
+ clearConfigListener() {
+ if (this.configListener) {
+ Config.configListeners.splice(Config.configListeners.indexOf(this.configListener), 1);
+ this.configListener = null;
+ }
+ }
+
+ openCategoryChooser() {
+ // Add as a config listener
+ this.configListener = () => this.forceUpdate();
+ Config.configListeners.push(this.configListener);
+
+ this.setState({
+ choosingCategory: true,
+ downvoting: false
+ });
+ }
+
+ getCategoryOptions() {
+ let elements = [];
+
+ for (const category of Config.config.categorySelections) {
+ elements.push(
+ <option value={category.name}
+ key={category.name}>
+ {chrome.i18n.getMessage("category_" + category.name)}
+ </option>
+ );
+ }
+
+ if (elements.length < CompileConfig.categoryList.length) {
+ // Add show more button
+ elements.push(
+ <option value={"moreCategories"}
+ key={"moreCategories"}>
+ {chrome.i18n.getMessage("moreCategories")}
+ </option>
+ );
+ }
+
+ return elements;
+ }
+
+ categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>) {
+ // See if show more categories was pressed
+ if (event.target.value === "moreCategories") {
+ // Open options page
+ chrome.runtime.sendMessage({"message": "openConfig"});
+
+ // Reset option to original
+ event.target.value = utils.getSponsorTimeFromUUID(this.props.contentContainer().sponsorTimes, this.props.UUID).category;
+ return;
+ }
+ }
+
unskip() {
this.contentContainer().unskipSponsorTime(this.UUID);
@@ -258,22 +382,22 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}
}
- afterDownvote() {
+ afterDownvote(type: number, category: string) {
this.addVoteButtonInfo(chrome.i18n.getMessage("voted"));
this.setNoticeInfoMessage(chrome.i18n.getMessage("hitGoBack"));
+
+ this.adjustDownvotingState(false);
- //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().sponsorTimes[i].hidden = SponsorHideType.Downvoted;
-
- this.contentContainer().updatePreviewBar();
- break;
+ // Change the sponsor locally
+ let sponsorTime = utils.getSponsorTimeFromUUID(this.contentContainer().sponsorTimes, this.UUID);
+ if (sponsorTime) {
+ if (type === 0) {
+ sponsorTime.hidden = SponsorHideType.Downvoted;
+ } else if (category) {
+ sponsorTime.category = category;
}
+
+ this.contentContainer().updatePreviewBar();
}
}
@@ -316,6 +440,12 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
//show button again
document.getElementById("sponsorTimesDownvoteButtonsContainer" + this.idSuffix).style.removeProperty("display");
}
+
+ closeListener() {
+ this.clearConfigListener();
+
+ this.props.closeListener();
+ }
}
export default SkipNoticeComponent; \ No newline at end of file
diff --git a/src/components/SponsorTimeEditComponent.tsx b/src/components/SponsorTimeEditComponent.tsx
index 07f20cba..e8530dde 100644
--- a/src/components/SponsorTimeEditComponent.tsx
+++ b/src/components/SponsorTimeEditComponent.tsx
@@ -29,6 +29,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
categoryOptionRef: React.RefObject<HTMLSelectElement>;
+ configUpdateListener: () => void;
+
constructor(props: SponsorTimeEditProps) {
super(props);
@@ -49,7 +51,16 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
});
// Add as a config listener
- Config.configListeners.push(this.configUpdate.bind(this));
+ if (!this.configUpdateListener) {
+ this.configUpdateListener = () => this.configUpdate();
+ Config.configListeners.push(this.configUpdate.bind(this));
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.configUpdateListener) {
+ Config.configListeners.splice(Config.configListeners.indexOf(this.configUpdate.bind(this)));
+ }
}
render() {
@@ -61,6 +72,15 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
style.marginTop = "15px";
}
+ // This method is required to get !important
+ // https://stackoverflow.com/a/45669262/1985387
+ let oldYouTubeDarkStyles = (node) => {
+ if (node) {
+ node.style.setProperty("color", "black", "important");
+ node.style.setProperty("text-shadow", "none", "important");
+ }
+ };
+
// Create time display
let timeDisplay: JSX.Element;
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
@@ -78,6 +98,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
<input id={"submittingTimeMinutes0" + this.idSuffix}
className="sponsorTimeEdit sponsorTimeEditMinutes"
+ ref={oldYouTubeDarkStyles}
type="number"
value={this.state.sponsorTimeEdits[0][0]}
onChange={(e) => {
@@ -90,6 +111,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
<input id={"submittingTimeSeconds0" + this.idSuffix}
className="sponsorTimeEdit sponsorTimeEditSeconds"
+ ref={oldYouTubeDarkStyles}
type="number"
value={this.state.sponsorTimeEdits[0][1]}
onChange={(e) => {
@@ -106,6 +128,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
<input id={"submittingTimeMinutes1" + this.idSuffix}
className="sponsorTimeEdit sponsorTimeEditMinutes"
+ ref={oldYouTubeDarkStyles}
type="text"
value={this.state.sponsorTimeEdits[1][0]}
onChange={(e) => {
@@ -118,6 +141,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
<input id={"submittingTimeSeconds1" + this.idSuffix}
className="sponsorTimeEdit sponsorTimeEditSeconds"
+ ref={oldYouTubeDarkStyles}
type="text"
value={this.state.sponsorTimeEdits[1][1]}
onChange={(e) => {
@@ -236,7 +260,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
}
setTimeToNow(index: number) {
- this.setTimeTo(index, this.props.contentContainer().v.currentTime);
+ this.setTimeTo(index, this.props.contentContainer().getRoughCurrentTime());
}
setTimeToEnd() {
diff --git a/src/components/SubmissionNoticeComponent.tsx b/src/components/SubmissionNoticeComponent.tsx
index a9943479..de7d4df9 100644
--- a/src/components/SubmissionNoticeComponent.tsx
+++ b/src/components/SubmissionNoticeComponent.tsx
@@ -93,13 +93,6 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
<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)}>
@@ -167,6 +160,18 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
ref.current.saveEditTimes();
}
+ // Check if any non music categories are being used on a music video
+ if (this.contentContainer().videoInfo.microformat.playerMicroformatRenderer.category === "Music") {
+ let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
+ for (const sponsorTime of sponsorTimesSubmitting) {
+ if (!sponsorTime.category.startsWith("music_")) {
+ if (!confirm(chrome.i18n.getMessage("nonMusicCategoryOnMusic"))) return;
+
+ break;
+ }
+ }
+ }
+
this.props.callback();
this.cancel();
diff --git a/src/config.ts b/src/config.ts
index 70263d5a..c6d285f2 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -123,7 +123,7 @@ var Config: SBObject = {
hideUploadButtonPlayerControls: false,
hideDiscordLaunches: 0,
hideDiscordLink: false,
- invidiousInstances: ["invidio.us", "invidiou.sh", "invidious.snopyta.org"],
+ invidiousInstances: ["invidio.us", "invidious.snopyta.org"],
autoUpvote: true,
supportInvidious: false,
serverAddress: CompileConfig.serverAddress,
@@ -254,7 +254,7 @@ async function migrateOldFormats() {
// Channel URLS
if (Config.config.whitelistedChannels.length > 0 &&
- (Config.config.whitelistedChannels[0].includes("/") || Config.config.whitelistedChannels[0] == null)) {
+ (Config.config.whitelistedChannels[0] == null || Config.config.whitelistedChannels[0].includes("/"))) {
let newChannelList: string[] = [];
for (const item of Config.config.whitelistedChannels) {
if (item != null) {
diff --git a/src/content.ts b/src/content.ts
index 758a5bb4..42f8dc42 100644
--- a/src/content.ts
+++ b/src/content.ts
@@ -114,7 +114,9 @@ var skipNoticeContentContainer: ContentContainer = () => ({
sponsorSubmissionNotice: submissionNotice,
resetSponsorSubmissionNotice,
changeStartSponsorButton,
- previewTime
+ previewTime,
+ videoInfo,
+ getRoughCurrentTime
});
//get messages from the background script and the popup
@@ -178,7 +180,7 @@ function messageListener(request: any, sender: any, sendResponse: (response: any
return
case "getCurrentTime":
sendResponse({
- currentTime: video.currentTime
+ currentTime: getRoughCurrentTime()
});
break;
@@ -461,6 +463,7 @@ function cancelSponsorSchedule(): void {
*/
function startSponsorSchedule(includeIntersectingSegments: boolean = false, currentTime?: number): void {
cancelSponsorSchedule();
+
if (video.paused) return;
if (Config.config.disableSkipping || channelWhitelisted || (channelID === null && Config.config.forceChannelCheck)){
@@ -478,6 +481,7 @@ function startSponsorSchedule(includeIntersectingSegments: boolean = false, curr
let currentSkip = skipInfo.array[skipInfo.index];
let skipTime: number[] = [currentSkip.segment[0], skipInfo.array[skipInfo.endIndex].segment[1]];
let timeUntilSponsor = skipTime[0] - currentTime;
+ let videoID = sponsorVideoID;
// Don't skip if this category should not be skipped
if (utils.getCategorySelection(currentSkip.category).option === CategorySkipOption.ShowOverlay) return;
@@ -486,7 +490,7 @@ function startSponsorSchedule(includeIntersectingSegments: boolean = false, curr
let forcedSkipTime: number = null;
let forcedIncludeIntersectingSegments = false;
- if (incorrectVideoIDCheck()) return;
+ if (incorrectVideoIDCheck(videoID)) return;
if (video.currentTime >= skipTime[0] && video.currentTime < skipTime[1]) {
skipToTime(video, skipInfo.endIndex, skipInfo.array, skipInfo.openNotice);
@@ -515,9 +519,9 @@ function startSponsorSchedule(includeIntersectingSegments: boolean = false, curr
*
* TODO: Remove this bug catching if statement when the bug is found
*/
-function incorrectVideoIDCheck(): boolean {
+function incorrectVideoIDCheck(videoID?: string): boolean {
let currentVideoID = getYouTubeVideoID(document.URL);
- if (currentVideoID !== sponsorVideoID) {
+ if (currentVideoID !== (videoID || sponsorVideoID)) {
// Something has really gone wrong
console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be.");
console.error("[SponsorBlock] VideoID recorded: " + sponsorVideoID + ". Actual VideoID: " + currentVideoID);
@@ -967,7 +971,7 @@ function skipToTime(v: HTMLVideoElement, index: number, sponsorTimes: SponsorTim
//auto-upvote this sponsor
if (Config.config.trackViewCount && autoSkip && Config.config.autoUpvote) {
- vote(1, currentUUID, null);
+ vote(1, currentUUID);
}
}
@@ -1117,6 +1121,36 @@ async function updateVisibilityOfPlayerControlsButton(): Promise<boolean> {
return createdButtons;
}
+/**
+ * Used for submitting. This will use the HTML displayed number when required as the video's
+ * current time is out of date while scrubbing or at the end of the video. This is not needed
+ * for sponsor skipping as the video is not playing during these times.
+ */
+function getRoughCurrentTime(): number {
+ let htmlCurrentTimeString = document.querySelector(".ytp-time-current").textContent;
+ let htmlDurationString = document.querySelector(".ytp-time-duration").textContent;
+
+ // Used to check if endscreen content is visible
+ let endScreenContent = document.querySelector(".ytp-endscreen-content");
+ // Used to check autoplay display
+ let autoPlayDisplay: HTMLDivElement = document.querySelector(".ytp-upnext");
+
+ if (htmlCurrentTimeString == htmlDurationString
+ || endScreenContent.childElementCount > 0 || autoPlayDisplay.style.display !== "none") {
+ // At the end of the video
+ return video.duration;
+ }
+
+ let htmlCurrentTimeSections = htmlCurrentTimeString.split(":")[0];
+ let htmlCurrentTime: number = parseInt(htmlCurrentTimeSections[0]) * 60 + parseInt(htmlCurrentTimeSections[1]);
+
+ if (Math.abs(video.currentTime - htmlCurrentTime) > 3) {
+ return htmlCurrentTime;
+ } else {
+ return video.currentTime;
+ }
+}
+
function startSponsorClicked() {
//it can't update to this info yet
closeInfoMenu();
@@ -1126,11 +1160,11 @@ function startSponsorClicked() {
//add to sponsorTimes
if (sponsorTimesSubmitting.length > 0 && sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment.length < 2) {
//it is an end time
- sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment[1] = video.currentTime;
+ sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment[1] = getRoughCurrentTime();
} else {
//it is a start time
sponsorTimesSubmitting.push({
- segment: [video.currentTime],
+ segment: [getRoughCurrentTime()],
UUID: null,
// Default to sponsor
category: "sponsor"
@@ -1306,7 +1340,7 @@ function clearSponsorTimes() {
}
//if skipNotice is null, it will not affect the UI
-function vote(type, UUID, skipNotice?: SkipNoticeComponent) {
+function vote(type: number, UUID: string, category?: string, skipNotice?: SkipNoticeComponent) {
if (skipNotice !== null && skipNotice !== undefined) {
//add loading info
skipNotice.addVoteButtonInfo.bind(skipNotice)("Loading...")
@@ -1319,7 +1353,7 @@ function vote(type, UUID, skipNotice?: SkipNoticeComponent) {
if (sponsorIndex == -1 || sponsorTimes[sponsorIndex].UUID === null) return;
// See if the local time saved count and skip count should be saved
- if (type == 0 && sponsorSkipped[sponsorIndex] || type == 1 && !sponsorSkipped[sponsorIndex]) {
+ if (type === 0 && sponsorSkipped[sponsorIndex] || type === 1 && !sponsorSkipped[sponsorIndex]) {
let factor = 1;
if (type == 0) {
factor = -1;
@@ -1336,15 +1370,16 @@ function vote(type, UUID, skipNotice?: SkipNoticeComponent) {
chrome.runtime.sendMessage({
message: "submitVote",
type: type,
- UUID: UUID
+ UUID: UUID,
+ category: category
}, function(response) {
if (response != undefined) {
//see if it was a success or failure
if (skipNotice != null) {
if (response.successType == 1 || (response.successType == -1 && response.statusCode == 429)) {
//success (treat rate limits as a success)
- if (type == 0) {
- skipNotice.afterDownvote.bind(skipNotice)();
+ if (type === 0 || category) {
+ skipNotice.afterDownvote.bind(skipNotice)(type, category);
}
} else if (response.successType == 0) {
//failure: duplicate vote
diff --git a/src/js-components/previewBar.ts b/src/js-components/previewBar.ts
index 1faabbda..6793e42d 100644
--- a/src/js-components/previewBar.ts
+++ b/src/js-components/previewBar.ts
@@ -50,11 +50,11 @@ let barTypes = {
color: "#bfbf35",
opacity: "0.7"
},
- "offtopic": {
+ "music_offtopic": {
color: "#ff9900",
opacity: "0.7"
},
- "preview-offtopic": {
+ "preview-music_offtopic": {
color: "#a6634a",
opacity: "0.7"
}
@@ -65,6 +65,9 @@ class PreviewBar {
parent: any;
onMobileYouTube: boolean;
+ timestamps: number[][];
+ types: string;
+
constructor(parent, onMobileYouTube) {
this.container = document.createElement('ul');
this.container.id = 'previewbar';
@@ -73,6 +76,82 @@ class PreviewBar {
this.onMobileYouTube = onMobileYouTube;
this.updatePosition(parent);
+
+ this.setupHoverText();
+ }
+
+ setupHoverText() {
+ let seekBar = document.querySelector(".ytp-progress-bar-container");
+
+ // Create label placeholder
+ let tooltipTextWrapper = document.querySelector(".ytp-tooltip-text-wrapper");
+ let titleTooltip = document.querySelector(".ytp-tooltip-title");
+ let categoryTooltip = document.createElement("div");
+ categoryTooltip.className = "sbHidden ytp-tooltip-title";
+ categoryTooltip.id = "sponsor-block-category-tooltip"
+
+ tooltipTextWrapper.insertBefore(categoryTooltip, titleTooltip.nextSibling);
+
+ let mouseOnSeekBar = false;
+
+ seekBar.addEventListener("mouseenter", (event) => {
+ mouseOnSeekBar = true;
+ });
+
+ seekBar.addEventListener("mouseleave", (event) => {
+ mouseOnSeekBar = false;
+ categoryTooltip.classList.add("sbHidden");
+ });
+
+ const observer = new MutationObserver(() => {
+ if (!mouseOnSeekBar) return;
+
+ let tooltips = document.querySelectorAll(".ytp-tooltip-text");
+ for (const tooltip of tooltips) {
+ let splitData = tooltip.textContent.split(":");
+ if (splitData.length === 2 && !isNaN(parseInt(splitData[0])) && !isNaN(parseInt(splitData[1]))) {
+ // Add label
+ let timeInSeconds = parseInt(splitData[0]) * 60 + parseInt(splitData[1]);
+
+ // Find category at that location
+ let category = null;
+ for (let i = 0; i < this.timestamps.length; i++) {
+ if (this.timestamps[i][0] < timeInSeconds && this.timestamps[i][1] > timeInSeconds){
+ category = this.types[i];
+ }
+ }
+
+ if (category === null && !categoryTooltip.classList.contains("sbHidden")) {
+ categoryTooltip.classList.add("sbHidden");
+ tooltipTextWrapper.classList.remove("sbTooltipTwoTitleThumbnailOffset");
+ tooltipTextWrapper.classList.remove("sbTooltipOneTitleThumbnailOffset");
+ } else if (category !== null) {
+ categoryTooltip.classList.remove("sbHidden");
+ categoryTooltip.textContent = chrome.i18n.getMessage("category_" + category)
+ || (chrome.i18n.getMessage("preview") + " " + chrome.i18n.getMessage("category_" + category.split("preview-")[1]));
+
+ // There is a title now
+ tooltip.classList.remove("ytp-tooltip-text-no-title");
+
+ // Add the correct offset for the number of titles there are
+ if (titleTooltip.textContent !== "") {
+ if (!tooltipTextWrapper.classList.contains("sbTooltipTwoTitleThumbnailOffset")) {
+ tooltipTextWrapper.classList.add("sbTooltipTwoTitleThumbnailOffset");
+ }
+ } else if (!tooltipTextWrapper.classList.contains("sbTooltipOneTitleThumbnailOffset")) {
+ tooltipTextWrapper.classList.add("sbTooltipOneTitleThumbnailOffset");
+ }
+ }
+
+ break;
+ }
+ }
+ });
+
+ observer.observe(tooltipTextWrapper, {
+ childList: true,
+ subtree: true
+ });
}
updatePosition(parent) {
@@ -109,6 +188,9 @@ class PreviewBar {
return;
}
+ this.timestamps = timestamps;
+ this.types = types;
+
// to avoid rounding error resulting in width more than 100%
duration = Math.floor(duration * 100) / 100;
let width;
diff --git a/src/types.ts b/src/types.ts
index befa1fc0..fab2f1db 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -3,7 +3,7 @@ import SkipNoticeComponent from "./components/SkipNoticeComponent";
interface ContentContainer {
(): {
- vote: (type: any, UUID: any, skipNotice?: SkipNoticeComponent) => void,
+ vote: (type: any, UUID: any, category?: string, skipNotice?: SkipNoticeComponent) => void,
dontShowNoticeAgain: () => void,
unskipSponsorTime: (UUID: any) => void,
sponsorTimes: SponsorTime[],
@@ -16,7 +16,9 @@ interface ContentContainer {
sponsorSubmissionNotice: SubmissionNotice,
resetSponsorSubmissionNotice: () => void,
changeStartSponsorButton: (showStartSponsor: any, uploadButtonVisible: any) => Promise<boolean>,
- previewTime: (time: number) => void
+ previewTime: (time: number) => void,
+ videoInfo: any,
+ getRoughCurrentTime: () => number
}
}