Merge pull request #332 from ajayyy/react

Categories Improvements
This commit is contained in:
Ajay Ramachandran
2020-05-10 23:09:40 -04:00
committed by GitHub
15 changed files with 407 additions and 66 deletions

View File

@@ -28,7 +28,7 @@ jobs:
- run: mkdir ./builds - run: mkdir ./builds
- uses: montudor/action-zip@v0.1.0 - uses: montudor/action-zip@v0.1.0
with: with:
args: zip -qq -r ./builds/ChromeExtension.zip ./dist args: zip -qq -r ./builds/ChromeExtension.zip ./dist/*
# Create Firefox artifacts # Create Firefox artifacts
- name: Create Firefox artifacts - name: Create Firefox artifacts
@@ -39,7 +39,7 @@ jobs:
path: dist path: dist
- uses: montudor/action-zip@v0.1.0 - uses: montudor/action-zip@v0.1.0
with: 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) # Create Beta artifacts (Builds with the name changed to beta)
- name: Create Chrome Beta artifacts - name: Create Chrome Beta artifacts
@@ -50,7 +50,7 @@ jobs:
path: dist path: dist
- uses: montudor/action-zip@v0.1.0 - uses: montudor/action-zip@v0.1.0
with: with:
args: zip -qq -r ./builds/ChromeExtensionBeta.zip ./dist args: zip -qq -r ./builds/ChromeExtensionBeta.zip ./dist/*
- name: Create Firefox Beta artifacts - name: Create Firefox Beta artifacts
run: npm run build:firefox -- --env.stream=beta run: npm run build:firefox -- --env.stream=beta
@@ -60,7 +60,24 @@ jobs:
path: dist path: dist
- uses: montudor/action-zip@v0.1.0 - uses: montudor/action-zip@v0.1.0
with: 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 # Upload each release asset
- name: Upload to release - name: Upload to release
@@ -70,6 +87,13 @@ jobs:
name: ChromeExtension.zip name: ChromeExtension.zip
path: ./builds/ChromeExtension.zip path: ./builds/ChromeExtension.zip
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
- 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 - name: Upload to release
uses: Shopify/upload-to-release@master uses: Shopify/upload-to-release@master
with: with:
@@ -77,4 +101,11 @@ jobs:
name: FirefoxExtension.zip name: FirefoxExtension.zip
path: ./builds/FirefoxExtension.zip path: ./builds/FirefoxExtension.zip
repo-token: ${{ secrets.GITHUB_TOKEN }} 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 }}

View File

@@ -2,5 +2,5 @@
"serverAddress": "https://sponsor.ajay.app", "serverAddress": "https://sponsor.ajay.app",
"testingServerAddress": "https://sponsor.ajay.app/test", "testingServerAddress": "https://sponsor.ajay.app/test",
"serverAddressComment": "This specifies the default SponsorBlock server to conect to", "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"]
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "__MSG_fullName__", "name": "__MSG_fullName__",
"short_name": "__MSG_Name__", "short_name": "__MSG_Name__",
"version": "1.2.27", "version": "1.2.28",
"default_locale": "en", "default_locale": "en",
"description": "__MSG_Description__", "description": "__MSG_Description__",
"content_scripts": [{ "content_scripts": [{

View File

@@ -32,6 +32,7 @@
}, },
"scripts": { "scripts": {
"web-run": "npm run web-run:chrome", "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: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", "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", "build": "npm run build:chrome",

View File

@@ -351,7 +351,7 @@
"message": "Support Invidious" "message": "Support Invidious"
}, },
"supportInvidiousDescription": { "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": { "optionsInfo": {
"message": "Enable Invidious support, disable autoskip, hide buttons and more." "message": "Enable Invidious support, disable autoskip, hide buttons and more."
@@ -490,19 +490,22 @@
"message": "Sponsor" "message": "Sponsor"
}, },
"category_intro": { "category_intro": {
"message": "Intro" "message": "Intro Animation"
}, },
"category_outro": { "category_outro": {
"message": "Outro" "message": "Endcards/Credits"
}, },
"category_interaction": { "category_interaction": {
"message": "Interaction (Redundant Like, Subscribe, Follow, etc.)" "message": "Interaction Reminder (Subscribe)"
}, },
"category_selfpromo": { "category_selfpromo": {
"message": "Self-Promotion and Merchandise" "message": "Self-Promotion and Merchandise"
}, },
"category_offtopic": { "category_music_offtopic": {
"message": "Offtopic tangent (Subjective)" "message": "Music: Non-Music Section"
},
"category_livestream_messages": {
"message": "Livestream: Donation/Message Readings"
}, },
"disable": { "disable": {
"message": "Disable" "message": "Disable"
@@ -554,5 +557,14 @@
}, },
"forceChannelCheckPopup": { "forceChannelCheckPopup": {
"message": "Consider Enabling Force Channel Check Before Skipping Sponsors" "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."
} }
} }

View File

@@ -11,11 +11,28 @@
z-index: 40; z-index: 40;
} }
.sbHidden {
display: none !important;
}
.previewbar { .previewbar {
display: inline-block; display: inline-block;
height: 100%; height: 100%;
} }
/* Preview Bar page hacks */
.sbTooltipTwoTitleThumbnailOffset {
bottom: -5px !important;
}
.sbTooltipOneTitleThumbnailOffset {
bottom: 10px !important;
}
/* */
.popup { .popup {
z-index: 10; z-index: 10;
width: 100%; width: 100%;

View File

@@ -135,7 +135,7 @@
<span id="sponsorTimesSkipsDoneDisplay" class="popupElement"> <span id="sponsorTimesSkipsDoneDisplay" class="popupElement">
0 0
</span> </span>
<span id="sponsorTimesSkipsDoneEndWord" class="popupElement">__MSG_Segments__</span> (since February). <span id="sponsorTimesSkipsDoneEndWord" class="popupElement">__MSG_Segments__</span>
</div> </div>
<div id="sponsorTimeSavedContainer" class="popupElement" style="display: none"> <div id="sponsorTimeSavedContainer" class="popupElement" style="display: none">

View File

@@ -47,7 +47,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
//this allows the callback to be called later //this allows the callback to be called later
return true; return true;
case "submitVote": case "submitVote":
submitVote(request.type, request.UUID, callback); submitVote(request.type, request.UUID, request.category, callback);
//this allows the callback to be called later //this allows the callback to be called later
return true; 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; let userID = Config.config.userID;
if (userID == undefined || userID === "undefined") { if (userID == undefined || userID === "undefined") {
@@ -156,8 +156,10 @@ function submitVote(type, UUID, callback) {
Config.config.userID = userID; Config.config.userID = userID;
} }
let typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
//publish this vote //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) { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
callback({ callback({
successType: 1 successType: 1

View File

@@ -1,4 +1,5 @@
import * as React from "react"; import * as React from "react";
import * as CompileConfig from "../../config.json";
import Config from "../config" import Config from "../config"
import { ContentContainer, SponsorHideType } from "../types"; import { ContentContainer, SponsorHideType } from "../types";
@@ -19,16 +20,19 @@ export interface SkipNoticeProps {
} }
export interface SkipNoticeState { export interface SkipNoticeState {
noticeTitle: string, noticeTitle: string;
messages: string[], messages: string[];
countdownTime: number, countdownTime: number;
maxCountdownTime: () => number; maxCountdownTime: () => number;
countdownText: string, countdownText: string;
unskipText: string, unskipText: string;
unskipCallback: () => void unskipCallback: () => void;
downvoting: boolean;
choosingCategory: boolean;
} }
class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeState> { class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeState> {
@@ -43,10 +47,15 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
idSuffix: any; idSuffix: any;
noticeRef: React.MutableRefObject<NoticeComponent>; noticeRef: React.MutableRefObject<NoticeComponent>;
categoryOptionRef: React.RefObject<HTMLSelectElement>;
// Used to update on config change
configListener: () => void;
constructor(props: SkipNoticeProps) { constructor(props: SkipNoticeProps) {
super(props); super(props);
this.noticeRef = React.createRef(); this.noticeRef = React.createRef();
this.categoryOptionRef = React.createRef();
this.UUID = props.UUID; this.UUID = props.UUID;
this.autoSkip = props.autoSkip; this.autoSkip = props.autoSkip;
@@ -83,7 +92,10 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
countdownText: null, countdownText: null,
unskipText: chrome.i18n.getMessage("unskip"), unskipText: chrome.i18n.getMessage("unskip"),
unskipCallback: this.unskip.bind(this) unskipCallback: this.unskip.bind(this),
downvoting: false,
choosingCategory: false
} }
if (!this.autoSkip) { if (!this.autoSkip) {
@@ -115,7 +127,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
timed={true} timed={true}
maxCountdownTime={this.state.maxCountdownTime} maxCountdownTime={this.state.maxCountdownTime}
ref={this.noticeRef} ref={this.noticeRef}
closeListener={this.props.closeListener}> closeListener={() => this.closeListener()}>
{(Config.config.audioNotificationOnSkip) && <audio ref={(source) => { this.audio = source; }}> {(Config.config.audioNotificationOnSkip) && <audio ref={(source) => { this.audio = source; }}>
<source src={chrome.extension.getURL("icons/beep.ogg")} type="audio/ogg"></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 */} {/* Text Boxes */}
{this.getMessageBoxes()} {this.getMessageBoxes()}
{/* Last Row */} {/* Bottom Row */}
<tr id={"sponsorSkipNoticeSecondRow" + this.idSuffix}> <tr id={"sponsorSkipNoticeSecondRow" + this.idSuffix}>
{/* Vote Button Container */} {/* Vote Button Container */}
@@ -145,7 +157,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
className="sponsorSkipObject voteButton" className="sponsorSkipObject voteButton"
src={chrome.extension.getURL("icons/report.png")} src={chrome.extension.getURL("icons/report.png")}
title={chrome.i18n.getMessage("reportButtonInfo")} title={chrome.i18n.getMessage("reportButtonInfo")}
onClick={() => this.contentContainer().vote(0, this.UUID, this)}> onClick={() => this.adjustDownvotingState(true)}>
</img> </img>
@@ -174,6 +186,54 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
} }
</tr> </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> </NoticeComponent>
); );
} }
@@ -202,6 +262,70 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
return elements; 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() { unskip() {
this.contentContainer().unskipSponsorTime(this.UUID); 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.addVoteButtonInfo(chrome.i18n.getMessage("voted"));
this.setNoticeInfoMessage(chrome.i18n.getMessage("hitGoBack")); this.setNoticeInfoMessage(chrome.i18n.getMessage("hitGoBack"));
this.adjustDownvotingState(false);
//remove this sponsor from the sponsors looked up // Change the sponsor locally
//find which one it is let sponsorTime = utils.getSponsorTimeFromUUID(this.contentContainer().sponsorTimes, this.UUID);
for (let i = 0; i < this.contentContainer().sponsorTimes.length; i++) { if (sponsorTime) {
if (this.contentContainer().sponsorTimes[i].UUID == this.UUID) { if (type === 0) {
//this one is the one to hide sponsorTime.hidden = SponsorHideType.Downvoted;
} else if (category) {
//add this as a hidden sponsorTime sponsorTime.category = category;
this.contentContainer().sponsorTimes[i].hidden = SponsorHideType.Downvoted;
this.contentContainer().updatePreviewBar();
break;
} }
this.contentContainer().updatePreviewBar();
} }
} }
@@ -316,6 +440,12 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
//show button again //show button again
document.getElementById("sponsorTimesDownvoteButtonsContainer" + this.idSuffix).style.removeProperty("display"); document.getElementById("sponsorTimesDownvoteButtonsContainer" + this.idSuffix).style.removeProperty("display");
} }
closeListener() {
this.clearConfigListener();
this.props.closeListener();
}
} }
export default SkipNoticeComponent; export default SkipNoticeComponent;

View File

@@ -29,6 +29,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
categoryOptionRef: React.RefObject<HTMLSelectElement>; categoryOptionRef: React.RefObject<HTMLSelectElement>;
configUpdateListener: () => void;
constructor(props: SponsorTimeEditProps) { constructor(props: SponsorTimeEditProps) {
super(props); super(props);
@@ -49,7 +51,16 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
}); });
// Add as a config listener // 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() { render() {
@@ -61,6 +72,15 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
style.marginTop = "15px"; 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 // Create time display
let timeDisplay: JSX.Element; let timeDisplay: JSX.Element;
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index]; 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} <input id={"submittingTimeMinutes0" + this.idSuffix}
className="sponsorTimeEdit sponsorTimeEditMinutes" className="sponsorTimeEdit sponsorTimeEditMinutes"
ref={oldYouTubeDarkStyles}
type="number" type="number"
value={this.state.sponsorTimeEdits[0][0]} value={this.state.sponsorTimeEdits[0][0]}
onChange={(e) => { onChange={(e) => {
@@ -90,6 +111,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
<input id={"submittingTimeSeconds0" + this.idSuffix} <input id={"submittingTimeSeconds0" + this.idSuffix}
className="sponsorTimeEdit sponsorTimeEditSeconds" className="sponsorTimeEdit sponsorTimeEditSeconds"
ref={oldYouTubeDarkStyles}
type="number" type="number"
value={this.state.sponsorTimeEdits[0][1]} value={this.state.sponsorTimeEdits[0][1]}
onChange={(e) => { onChange={(e) => {
@@ -106,6 +128,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
<input id={"submittingTimeMinutes1" + this.idSuffix} <input id={"submittingTimeMinutes1" + this.idSuffix}
className="sponsorTimeEdit sponsorTimeEditMinutes" className="sponsorTimeEdit sponsorTimeEditMinutes"
ref={oldYouTubeDarkStyles}
type="text" type="text"
value={this.state.sponsorTimeEdits[1][0]} value={this.state.sponsorTimeEdits[1][0]}
onChange={(e) => { onChange={(e) => {
@@ -118,6 +141,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
<input id={"submittingTimeSeconds1" + this.idSuffix} <input id={"submittingTimeSeconds1" + this.idSuffix}
className="sponsorTimeEdit sponsorTimeEditSeconds" className="sponsorTimeEdit sponsorTimeEditSeconds"
ref={oldYouTubeDarkStyles}
type="text" type="text"
value={this.state.sponsorTimeEdits[1][1]} value={this.state.sponsorTimeEdits[1][1]}
onChange={(e) => { onChange={(e) => {
@@ -236,7 +260,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
} }
setTimeToNow(index: number) { setTimeToNow(index: number) {
this.setTimeTo(index, this.props.contentContainer().v.currentTime); this.setTimeTo(index, this.props.contentContainer().getRoughCurrentTime());
} }
setTimeToEnd() { setTimeToEnd() {

View File

@@ -93,13 +93,6 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
<td className="sponsorSkipNoticeRightSection" <td className="sponsorSkipNoticeRightSection"
style={{position: "relative"}}> style={{position: "relative"}}>
{/* Cancel Button */}
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
onClick={this.cancel.bind(this)}>
{chrome.i18n.getMessage("cancel")}
</button>
{/* Submit Button */} {/* Submit Button */}
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton" <button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
onClick={this.submit.bind(this)}> onClick={this.submit.bind(this)}>
@@ -167,6 +160,18 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
ref.current.saveEditTimes(); 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.props.callback();
this.cancel(); this.cancel();

View File

@@ -123,7 +123,7 @@ var Config: SBObject = {
hideUploadButtonPlayerControls: false, hideUploadButtonPlayerControls: false,
hideDiscordLaunches: 0, hideDiscordLaunches: 0,
hideDiscordLink: false, hideDiscordLink: false,
invidiousInstances: ["invidio.us", "invidiou.sh", "invidious.snopyta.org"], invidiousInstances: ["invidio.us", "invidious.snopyta.org"],
autoUpvote: true, autoUpvote: true,
supportInvidious: false, supportInvidious: false,
serverAddress: CompileConfig.serverAddress, serverAddress: CompileConfig.serverAddress,
@@ -254,7 +254,7 @@ async function migrateOldFormats() {
// Channel URLS // Channel URLS
if (Config.config.whitelistedChannels.length > 0 && 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[] = []; let newChannelList: string[] = [];
for (const item of Config.config.whitelistedChannels) { for (const item of Config.config.whitelistedChannels) {
if (item != null) { if (item != null) {

View File

@@ -114,7 +114,9 @@ var skipNoticeContentContainer: ContentContainer = () => ({
sponsorSubmissionNotice: submissionNotice, sponsorSubmissionNotice: submissionNotice,
resetSponsorSubmissionNotice, resetSponsorSubmissionNotice,
changeStartSponsorButton, changeStartSponsorButton,
previewTime previewTime,
videoInfo,
getRoughCurrentTime
}); });
//get messages from the background script and the popup //get messages from the background script and the popup
@@ -178,7 +180,7 @@ function messageListener(request: any, sender: any, sendResponse: (response: any
return return
case "getCurrentTime": case "getCurrentTime":
sendResponse({ sendResponse({
currentTime: video.currentTime currentTime: getRoughCurrentTime()
}); });
break; break;
@@ -461,6 +463,7 @@ function cancelSponsorSchedule(): void {
*/ */
function startSponsorSchedule(includeIntersectingSegments: boolean = false, currentTime?: number): void { function startSponsorSchedule(includeIntersectingSegments: boolean = false, currentTime?: number): void {
cancelSponsorSchedule(); cancelSponsorSchedule();
if (video.paused) return; if (video.paused) return;
if (Config.config.disableSkipping || channelWhitelisted || (channelID === null && Config.config.forceChannelCheck)){ 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 currentSkip = skipInfo.array[skipInfo.index];
let skipTime: number[] = [currentSkip.segment[0], skipInfo.array[skipInfo.endIndex].segment[1]]; let skipTime: number[] = [currentSkip.segment[0], skipInfo.array[skipInfo.endIndex].segment[1]];
let timeUntilSponsor = skipTime[0] - currentTime; let timeUntilSponsor = skipTime[0] - currentTime;
let videoID = sponsorVideoID;
// Don't skip if this category should not be skipped // Don't skip if this category should not be skipped
if (utils.getCategorySelection(currentSkip.category).option === CategorySkipOption.ShowOverlay) return; if (utils.getCategorySelection(currentSkip.category).option === CategorySkipOption.ShowOverlay) return;
@@ -486,7 +490,7 @@ function startSponsorSchedule(includeIntersectingSegments: boolean = false, curr
let forcedSkipTime: number = null; let forcedSkipTime: number = null;
let forcedIncludeIntersectingSegments = false; let forcedIncludeIntersectingSegments = false;
if (incorrectVideoIDCheck()) return; if (incorrectVideoIDCheck(videoID)) return;
if (video.currentTime >= skipTime[0] && video.currentTime < skipTime[1]) { if (video.currentTime >= skipTime[0] && video.currentTime < skipTime[1]) {
skipToTime(video, skipInfo.endIndex, skipInfo.array, skipInfo.openNotice); 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 * 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); let currentVideoID = getYouTubeVideoID(document.URL);
if (currentVideoID !== sponsorVideoID) { if (currentVideoID !== (videoID || sponsorVideoID)) {
// Something has really gone wrong // 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] The videoID recorded when trying to skip is different than what it should be.");
console.error("[SponsorBlock] VideoID recorded: " + sponsorVideoID + ". Actual VideoID: " + currentVideoID); 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 //auto-upvote this sponsor
if (Config.config.trackViewCount && autoSkip && Config.config.autoUpvote) { 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; 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() { function startSponsorClicked() {
//it can't update to this info yet //it can't update to this info yet
closeInfoMenu(); closeInfoMenu();
@@ -1126,11 +1160,11 @@ function startSponsorClicked() {
//add to sponsorTimes //add to sponsorTimes
if (sponsorTimesSubmitting.length > 0 && sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment.length < 2) { if (sponsorTimesSubmitting.length > 0 && sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment.length < 2) {
//it is an end time //it is an end time
sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment[1] = video.currentTime; sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment[1] = getRoughCurrentTime();
} else { } else {
//it is a start time //it is a start time
sponsorTimesSubmitting.push({ sponsorTimesSubmitting.push({
segment: [video.currentTime], segment: [getRoughCurrentTime()],
UUID: null, UUID: null,
// Default to sponsor // Default to sponsor
category: "sponsor" category: "sponsor"
@@ -1306,7 +1340,7 @@ function clearSponsorTimes() {
} }
//if skipNotice is null, it will not affect the UI //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) { if (skipNotice !== null && skipNotice !== undefined) {
//add loading info //add loading info
skipNotice.addVoteButtonInfo.bind(skipNotice)("Loading...") skipNotice.addVoteButtonInfo.bind(skipNotice)("Loading...")
@@ -1319,7 +1353,7 @@ function vote(type, UUID, skipNotice?: SkipNoticeComponent) {
if (sponsorIndex == -1 || sponsorTimes[sponsorIndex].UUID === null) return; if (sponsorIndex == -1 || sponsorTimes[sponsorIndex].UUID === null) return;
// See if the local time saved count and skip count should be saved // 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; let factor = 1;
if (type == 0) { if (type == 0) {
factor = -1; factor = -1;
@@ -1336,15 +1370,16 @@ function vote(type, UUID, skipNotice?: SkipNoticeComponent) {
chrome.runtime.sendMessage({ chrome.runtime.sendMessage({
message: "submitVote", message: "submitVote",
type: type, type: type,
UUID: UUID UUID: UUID,
category: category
}, function(response) { }, function(response) {
if (response != undefined) { if (response != undefined) {
//see if it was a success or failure //see if it was a success or failure
if (skipNotice != null) { if (skipNotice != null) {
if (response.successType == 1 || (response.successType == -1 && response.statusCode == 429)) { if (response.successType == 1 || (response.successType == -1 && response.statusCode == 429)) {
//success (treat rate limits as a success) //success (treat rate limits as a success)
if (type == 0) { if (type === 0 || category) {
skipNotice.afterDownvote.bind(skipNotice)(); skipNotice.afterDownvote.bind(skipNotice)(type, category);
} }
} else if (response.successType == 0) { } else if (response.successType == 0) {
//failure: duplicate vote //failure: duplicate vote

View File

@@ -50,11 +50,11 @@ let barTypes = {
color: "#bfbf35", color: "#bfbf35",
opacity: "0.7" opacity: "0.7"
}, },
"offtopic": { "music_offtopic": {
color: "#ff9900", color: "#ff9900",
opacity: "0.7" opacity: "0.7"
}, },
"preview-offtopic": { "preview-music_offtopic": {
color: "#a6634a", color: "#a6634a",
opacity: "0.7" opacity: "0.7"
} }
@@ -65,6 +65,9 @@ class PreviewBar {
parent: any; parent: any;
onMobileYouTube: boolean; onMobileYouTube: boolean;
timestamps: number[][];
types: string;
constructor(parent, onMobileYouTube) { constructor(parent, onMobileYouTube) {
this.container = document.createElement('ul'); this.container = document.createElement('ul');
this.container.id = 'previewbar'; this.container.id = 'previewbar';
@@ -73,6 +76,82 @@ class PreviewBar {
this.onMobileYouTube = onMobileYouTube; this.onMobileYouTube = onMobileYouTube;
this.updatePosition(parent); 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) { updatePosition(parent) {
@@ -109,6 +188,9 @@ class PreviewBar {
return; return;
} }
this.timestamps = timestamps;
this.types = types;
// to avoid rounding error resulting in width more than 100% // to avoid rounding error resulting in width more than 100%
duration = Math.floor(duration * 100) / 100; duration = Math.floor(duration * 100) / 100;
let width; let width;

View File

@@ -3,7 +3,7 @@ import SkipNoticeComponent from "./components/SkipNoticeComponent";
interface ContentContainer { interface ContentContainer {
(): { (): {
vote: (type: any, UUID: any, skipNotice?: SkipNoticeComponent) => void, vote: (type: any, UUID: any, category?: string, skipNotice?: SkipNoticeComponent) => void,
dontShowNoticeAgain: () => void, dontShowNoticeAgain: () => void,
unskipSponsorTime: (UUID: any) => void, unskipSponsorTime: (UUID: any) => void,
sponsorTimes: SponsorTime[], sponsorTimes: SponsorTime[],
@@ -16,7 +16,9 @@ interface ContentContainer {
sponsorSubmissionNotice: SubmissionNotice, sponsorSubmissionNotice: SubmissionNotice,
resetSponsorSubmissionNotice: () => void, resetSponsorSubmissionNotice: () => void,
changeStartSponsorButton: (showStartSponsor: any, uploadButtonVisible: any) => Promise<boolean>, changeStartSponsorButton: (showStartSponsor: any, uploadButtonVisible: any) => Promise<boolean>,
previewTime: (time: number) => void previewTime: (time: number) => void,
videoInfo: any,
getRoughCurrentTime: () => number
} }
} }