mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-07 12:07:11 +03:00
Merge branch 'master' of https://github.com/ajayyy/SponsorBlock into pr/tech234a/2187
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "__MSG_fullName__",
|
"name": "__MSG_fullName__",
|
||||||
"short_name": "SponsorBlock",
|
"short_name": "SponsorBlock",
|
||||||
"version": "5.11",
|
"version": "5.11.7",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"description": "__MSG_Description__",
|
"description": "__MSG_Description__",
|
||||||
"homepage_url": "https://sponsor.ajay.app",
|
"homepage_url": "https://sponsor.ajay.app",
|
||||||
|
|||||||
@@ -373,6 +373,7 @@ input[type='number'] {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
min-width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch input {
|
.switch input {
|
||||||
|
|||||||
@@ -472,6 +472,16 @@
|
|||||||
<div class="inline"></div>
|
<div class="inline"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div data-type="keybind-change" data-sync="upvoteKeybind">
|
||||||
|
<label class="optionLabel">__MSG_setUpvoteKeybind__:</label>
|
||||||
|
<div class="inline"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div data-type="keybind-change" data-sync="downvoteKeybind">
|
||||||
|
<label class="optionLabel">__MSG_setDownvoteKeybind__:</label>
|
||||||
|
<div class="inline"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="import" class="option-group hidden">
|
<div id="import" class="option-group hidden">
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
|
|||||||
chrome.tabs.create({url: chrome.runtime.getURL(request.url)});
|
chrome.tabs.create({url: chrome.runtime.getURL(request.url)});
|
||||||
return false;
|
return false;
|
||||||
case "submitVote":
|
case "submitVote":
|
||||||
submitVote(request.type, request.UUID, request.category).then(callback);
|
submitVote(request.type, request.UUID, request.category, request.videoID).then(callback);
|
||||||
|
|
||||||
//this allows the callback to be called later
|
//this allows the callback to be called later
|
||||||
return true;
|
return true;
|
||||||
@@ -214,7 +214,7 @@ async function unregisterFirefoxContentScript(id: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitVote(type: number, UUID: string, category: string) {
|
async function submitVote(type: number, UUID: string, category: string, videoID: string) {
|
||||||
let userID = Config.config.userID;
|
let userID = Config.config.userID;
|
||||||
|
|
||||||
if (userID == undefined || userID === "undefined") {
|
if (userID == undefined || userID === "undefined") {
|
||||||
@@ -226,7 +226,7 @@ async function submitVote(type: number, UUID: string, category: string) {
|
|||||||
const typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
|
const typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection);
|
const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&videoID=" + videoID + "&userID=" + userID + typeSection);
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as CompileConfig from "../../config.json";
|
import * as CompileConfig from "../../config.json";
|
||||||
import Config from "../config"
|
import Config from "../config"
|
||||||
import { Category, ContentContainer, SponsorTime, NoticeVisbilityMode, ActionType, SponsorSourceType, SegmentUUID } from "../types";
|
import { Category, ContentContainer, SponsorTime, NoticeVisibilityMode, ActionType, SponsorSourceType, SegmentUUID } from "../types";
|
||||||
import NoticeComponent from "./NoticeComponent";
|
import NoticeComponent from "./NoticeComponent";
|
||||||
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
|
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
|
||||||
import Utils from "../utils";
|
import Utils from "../utils";
|
||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
import { getSkippingText, getUpcomingText } from "../utils/categoryUtils";
|
import { getSkippingText, getUpcomingText, getVoteText } from "../utils/categoryUtils";
|
||||||
|
|
||||||
import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
|
import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
|
||||||
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
|
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
|
||||||
@@ -29,6 +29,7 @@ export interface SkipNoticeProps {
|
|||||||
autoSkip: boolean;
|
autoSkip: boolean;
|
||||||
startReskip?: boolean;
|
startReskip?: boolean;
|
||||||
upcomingNotice?: boolean;
|
upcomingNotice?: boolean;
|
||||||
|
voteNotice?: boolean;
|
||||||
// Contains functions and variables from the content script needed by the skip notice
|
// Contains functions and variables from the content script needed by the skip notice
|
||||||
contentContainer: ContentContainer;
|
contentContainer: ContentContainer;
|
||||||
|
|
||||||
@@ -102,7 +103,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
this.autoSkip = props.autoSkip;
|
this.autoSkip = props.autoSkip;
|
||||||
this.contentContainer = props.contentContainer;
|
this.contentContainer = props.contentContainer;
|
||||||
|
|
||||||
const noticeTitle = !this.props.upcomingNotice ? getSkippingText(this.segments, this.props.autoSkip) : getUpcomingText(this.segments);
|
const noticeTitle = this.props.voteNotice ? getVoteText(this.segments) : !this.props.upcomingNotice ? getSkippingText(this.segments, this.props.autoSkip) : getUpcomingText(this.segments);
|
||||||
|
|
||||||
const previousSkipNotices = document.querySelectorAll(".sponsorSkipNoticeParent:not(.sponsorSkipUpcomingNotice)");
|
const previousSkipNotices = document.querySelectorAll(".sponsorSkipNoticeParent:not(.sponsorSkipUpcomingNotice)");
|
||||||
this.amountOfPreviousNotices = previousSkipNotices.length;
|
this.amountOfPreviousNotices = previousSkipNotices.length;
|
||||||
@@ -186,8 +187,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
idSuffix={this.idSuffix}
|
idSuffix={this.idSuffix}
|
||||||
fadeIn={this.props.fadeIn}
|
fadeIn={this.props.fadeIn}
|
||||||
fadeOut={!this.props.upcomingNotice}
|
fadeOut={!this.props.upcomingNotice}
|
||||||
startFaded={Config.config.noticeVisibilityMode >= NoticeVisbilityMode.FadedForAll
|
startFaded={Config.config.noticeVisibilityMode >= NoticeVisibilityMode.FadedForAll
|
||||||
|| (Config.config.noticeVisibilityMode >= NoticeVisbilityMode.FadedForAutoSkip && this.autoSkip)}
|
|| (Config.config.noticeVisibilityMode >= NoticeVisibilityMode.FadedForAutoSkip && this.autoSkip)}
|
||||||
timed={true}
|
timed={true}
|
||||||
maxCountdownTime={this.state.maxCountdownTime}
|
maxCountdownTime={this.state.maxCountdownTime}
|
||||||
style={noticeStyle}
|
style={noticeStyle}
|
||||||
@@ -242,6 +243,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Copy and Downvote Button */}
|
{/* Copy and Downvote Button */}
|
||||||
|
{
|
||||||
|
!this.props.voteNotice &&
|
||||||
<div id={"sponsorTimesDownvoteButtonsContainerCopyDownvote" + this.idSuffix}
|
<div id={"sponsorTimesDownvoteButtonsContainerCopyDownvote" + this.idSuffix}
|
||||||
className="voteButton"
|
className="voteButton"
|
||||||
style={{marginLeft: "5px"}}
|
style={{marginLeft: "5px"}}
|
||||||
@@ -251,6 +254,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
|| this.state.choosingCategory === true
|
|| this.state.choosingCategory === true
|
||||||
? this.selectedColor : this.unselectedColor} />
|
? this.selectedColor : this.unselectedColor} />
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
:
|
:
|
||||||
@@ -278,11 +282,11 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
{/* Unskip/Skip Button */}
|
{/* Unskip/Skip Button */}
|
||||||
{!this.props.smaller || this.segments[0].actionType === ActionType.Mute
|
{!this.props.voteNotice && (!this.props.smaller || this.segments[0].actionType === ActionType.Mute)
|
||||||
? this.getSkipButton(1) : null}
|
? this.getSkipButton(1) : null}
|
||||||
|
|
||||||
{/* Never show button */}
|
{/* Never show button */}
|
||||||
{!this.autoSkip || this.props.startReskip ? "" :
|
{!this.autoSkip || this.props.startReskip || this.props.voteNotice ? "" :
|
||||||
<td className="sponsorSkipNoticeRightSection"
|
<td className="sponsorSkipNoticeRightSection"
|
||||||
key={1}>
|
key={1}>
|
||||||
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
|
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
|
||||||
@@ -374,7 +378,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
style.minWidth = "100px";
|
style.minWidth = "100px";
|
||||||
}
|
}
|
||||||
|
|
||||||
const showSkipButton = (buttonIndex !== 0 || this.props.smaller || this.segments[0].actionType === ActionType.Mute) && !this.props.upcomingNotice;
|
const showSkipButton = (buttonIndex !== 0 || this.props.smaller || !this.props.voteNotice || this.segments[0].actionType === ActionType.Mute) && !this.props.upcomingNotice;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="sponsorSkipNoticeUnskipSection" style={{ visibility: !showSkipButton ? "hidden" : null }}>
|
<span className="sponsorSkipNoticeUnskipSection" style={{ visibility: !showSkipButton ? "hidden" : null }}>
|
||||||
@@ -624,14 +628,17 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
unskip(buttonIndex: number, index: number, forceSeek: boolean): void {
|
unskip(buttonIndex: number, index: number, forceSeek: boolean): void {
|
||||||
this.contentContainer().unskipSponsorTime(this.segments[index], this.props.unskipTime, forceSeek);
|
this.contentContainer().unskipSponsorTime(this.segments[index], this.props.unskipTime, forceSeek, this.props.voteNotice);
|
||||||
|
|
||||||
this.unskippedMode(buttonIndex, index, SkipButtonState.Redo);
|
this.unskippedMode(buttonIndex, index, this.segments[0].actionType === ActionType.Poi ? SkipButtonState.Undo : SkipButtonState.Redo);
|
||||||
}
|
}
|
||||||
|
|
||||||
reskip(buttonIndex: number, index: number, forceSeek: boolean): void {
|
reskip(buttonIndex: number, index: number, forceSeek: boolean): void {
|
||||||
this.contentContainer().reskipSponsorTime(this.segments[index], forceSeek);
|
this.contentContainer().reskipSponsorTime(this.segments[index], forceSeek);
|
||||||
|
this.reskippedMode(buttonIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
reskippedMode(buttonIndex: number): void {
|
||||||
const skipButtonStates = this.state.skipButtonStates;
|
const skipButtonStates = this.state.skipButtonStates;
|
||||||
skipButtonStates[buttonIndex] = SkipButtonState.Undo;
|
skipButtonStates[buttonIndex] = SkipButtonState.Undo;
|
||||||
|
|
||||||
@@ -661,7 +668,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
getUnskippedModeInfo(buttonIndex: number, index: number, skipButtonState: SkipButtonState): SkipNoticeState {
|
getUnskippedModeInfo(buttonIndex: number, index: number, skipButtonState: SkipButtonState): SkipNoticeState {
|
||||||
const changeCountdown = this.segments[index].actionType !== ActionType.Poi;
|
const changeCountdown = !this.props.voteNotice && this.segments[index].actionType !== ActionType.Poi;
|
||||||
|
|
||||||
const maxCountdownTime = changeCountdown ?
|
const maxCountdownTime = changeCountdown ?
|
||||||
this.getFullDurationCountdown(index) : this.state.maxCountdownTime;
|
this.getFullDurationCountdown(index) : this.state.maxCountdownTime;
|
||||||
@@ -697,7 +704,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
getFullDurationCountdown(index: number): () => number {
|
getFullDurationCountdown(index: number): () => number {
|
||||||
return () => {
|
return () => {
|
||||||
const sponsorTime = this.segments[index];
|
const sponsorTime = this.segments[index];
|
||||||
const duration = Math.round((sponsorTime.segment[1] - getCurrentTime()) * (1 / getVideo().playbackRate));
|
const duration = Math.round((sponsorTime.segment[1] - getCurrentTime()) * (1 / (getVideo()?.playbackRate ?? 1)));
|
||||||
|
|
||||||
return Math.max(duration, Config.config.skipNoticeDuration);
|
return Math.max(duration, Config.config.skipNoticeDuration);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -146,7 +146,9 @@ class KeybindDialogComponent extends React.Component<KeybindDialogProps, Keybind
|
|||||||
this.props.option !== "actuallySubmitKeybind" && this.equals(Config.config['actuallySubmitKeybind']) ||
|
this.props.option !== "actuallySubmitKeybind" && this.equals(Config.config['actuallySubmitKeybind']) ||
|
||||||
this.props.option !== "previewKeybind" && this.equals(Config.config['previewKeybind']) ||
|
this.props.option !== "previewKeybind" && this.equals(Config.config['previewKeybind']) ||
|
||||||
this.props.option !== "closeSkipNoticeKeybind" && this.equals(Config.config['closeSkipNoticeKeybind']) ||
|
this.props.option !== "closeSkipNoticeKeybind" && this.equals(Config.config['closeSkipNoticeKeybind']) ||
|
||||||
this.props.option !== "startSponsorKeybind" && this.equals(Config.config['startSponsorKeybind']))
|
this.props.option !== "startSponsorKeybind" && this.equals(Config.config['startSponsorKeybind']) ||
|
||||||
|
this.props.option !== "downvoteKeybind" && this.equals(Config.config['downvoteKeybind']) ||
|
||||||
|
this.props.option !== "upvoteKeybind" && this.equals(Config.config['upvoteKeybind']))
|
||||||
return {message: chrome.i18n.getMessage("keyAlreadyUsed"), blocking: true};
|
return {message: chrome.i18n.getMessage("keyAlreadyUsed"), blocking: true};
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as CompileConfig from "../config.json";
|
import * as CompileConfig from "../config.json";
|
||||||
import * as invidiousList from "../ci/invidiouslist.json";
|
import * as invidiousList from "../ci/invidiouslist.json";
|
||||||
import { Category, CategorySelection, CategorySkipOption, NoticeVisbilityMode, PreviewBarOption, SponsorTime, VideoID, SponsorHideType } from "./types";
|
import { Category, CategorySelection, CategorySkipOption, NoticeVisibilityMode, PreviewBarOption, SponsorTime, VideoID, SponsorHideType } from "./types";
|
||||||
import { Keybind, ProtoConfig, keybindEquals } from "../maze-utils/src/config";
|
import { Keybind, ProtoConfig, keybindEquals } from "../maze-utils/src/config";
|
||||||
import { HashedValue } from "../maze-utils/src/hash";
|
import { HashedValue } from "../maze-utils/src/hash";
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ interface SBConfig {
|
|||||||
trackDownvotesInPrivate: boolean;
|
trackDownvotesInPrivate: boolean;
|
||||||
dontShowNotice: boolean;
|
dontShowNotice: boolean;
|
||||||
showUpcomingNotice: boolean;
|
showUpcomingNotice: boolean;
|
||||||
noticeVisibilityMode: NoticeVisbilityMode;
|
noticeVisibilityMode: NoticeVisibilityMode;
|
||||||
hideVideoPlayerControls: boolean;
|
hideVideoPlayerControls: boolean;
|
||||||
hideInfoButtonPlayerControls: boolean;
|
hideInfoButtonPlayerControls: boolean;
|
||||||
hideDeleteButtonPlayerControls: boolean;
|
hideDeleteButtonPlayerControls: boolean;
|
||||||
@@ -97,6 +97,8 @@ interface SBConfig {
|
|||||||
nextChapterKeybind: Keybind;
|
nextChapterKeybind: Keybind;
|
||||||
previousChapterKeybind: Keybind;
|
previousChapterKeybind: Keybind;
|
||||||
closeSkipNoticeKeybind: Keybind;
|
closeSkipNoticeKeybind: Keybind;
|
||||||
|
upvoteKeybind: Keybind;
|
||||||
|
downvoteKeybind: Keybind;
|
||||||
|
|
||||||
// What categories should be skipped
|
// What categories should be skipped
|
||||||
categorySelections: CategorySelection[];
|
categorySelections: CategorySelection[];
|
||||||
@@ -295,7 +297,7 @@ const syncDefaults = {
|
|||||||
trackDownvotesInPrivate: false,
|
trackDownvotesInPrivate: false,
|
||||||
dontShowNotice: false,
|
dontShowNotice: false,
|
||||||
showUpcomingNotice: false,
|
showUpcomingNotice: false,
|
||||||
noticeVisibilityMode: NoticeVisbilityMode.FadedForAutoSkip,
|
noticeVisibilityMode: NoticeVisibilityMode.FadedForAutoSkip,
|
||||||
hideVideoPlayerControls: false,
|
hideVideoPlayerControls: false,
|
||||||
hideInfoButtonPlayerControls: false,
|
hideInfoButtonPlayerControls: false,
|
||||||
hideDeleteButtonPlayerControls: false,
|
hideDeleteButtonPlayerControls: false,
|
||||||
@@ -356,6 +358,8 @@ const syncDefaults = {
|
|||||||
nextChapterKeybind: { key: "ArrowRight", ctrl: true },
|
nextChapterKeybind: { key: "ArrowRight", ctrl: true },
|
||||||
previousChapterKeybind: { key: "ArrowLeft", ctrl: true },
|
previousChapterKeybind: { key: "ArrowLeft", ctrl: true },
|
||||||
closeSkipNoticeKeybind: { key: "Backspace" },
|
closeSkipNoticeKeybind: { key: "Backspace" },
|
||||||
|
downvoteKeybind: { key: "h", shift: true },
|
||||||
|
upvoteKeybind: { key: "g", shift: true },
|
||||||
|
|
||||||
categorySelections: [{
|
categorySelections: [{
|
||||||
name: "sponsor" as Category,
|
name: "sponsor" as Category,
|
||||||
@@ -466,7 +470,11 @@ const syncDefaults = {
|
|||||||
"preview-filler": {
|
"preview-filler": {
|
||||||
color: "#2E0066",
|
color: "#2E0066",
|
||||||
opacity: "0.7"
|
opacity: "0.7"
|
||||||
}
|
},
|
||||||
|
"chapter": {
|
||||||
|
color: "#fff",
|
||||||
|
opacity: "0"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -307,7 +307,8 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
|||||||
for (const segment of importedSegments) {
|
for (const segment of importedSegments) {
|
||||||
if (!sponsorTimesSubmitting.some(
|
if (!sponsorTimesSubmitting.some(
|
||||||
(s) => Math.abs(s.segment[0] - segment.segment[0]) < 1
|
(s) => Math.abs(s.segment[0] - segment.segment[0]) < 1
|
||||||
&& Math.abs(s.segment[1] - segment.segment[1]) < 1)) {
|
&& Math.abs(s.segment[1] - segment.segment[1]) < 1
|
||||||
|
&& s.description === segment.description)) {
|
||||||
const hasChaptersPermission = (Config.config.showCategoryWithoutPermission
|
const hasChaptersPermission = (Config.config.showCategoryWithoutPermission
|
||||||
|| Config.config.permissions["chapter"]);
|
|| Config.config.permissions["chapter"]);
|
||||||
if (segment.category === "chapter" && (!utils.getCategorySelection("chapter") || !hasChaptersPermission)) {
|
if (segment.category === "chapter" && (!utils.getCategorySelection("chapter") || !hasChaptersPermission)) {
|
||||||
@@ -1406,7 +1407,7 @@ function updatePreviewBar(): void {
|
|||||||
showLarger: segment.actionType === ActionType.Poi,
|
showLarger: segment.actionType === ActionType.Poi,
|
||||||
description: segment.description,
|
description: segment.description,
|
||||||
source: segment.source,
|
source: segment.source,
|
||||||
requiredSegment: requiredSegment && (segment.UUID === requiredSegment || segment.UUID?.startsWith(requiredSegment)),
|
requiredSegment: requiredSegment && (segment.UUID === requiredSegment || segment.UUID?.startsWith(requiredSegment) || requiredSegment.startsWith(segment.UUID)),
|
||||||
selectedSegment: selectedSegment && segment.UUID === selectedSegment
|
selectedSegment: selectedSegment && segment.UUID === selectedSegment
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1684,7 +1685,7 @@ function sendTelemetryAndCount(skippingSegments: SponsorTime[], secondsSkipped:
|
|||||||
counted = true;
|
counted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fullSkip) asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
|
if (fullSkip) asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID + "&videoID=" + getVideoID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1784,7 +1785,7 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
|
|||||||
if (autoSkip || isSubmittingSegment) sendTelemetryAndCount(skippingSegments, skipTime[1] - skipTime[0], true);
|
if (autoSkip || isSubmittingSegment) sendTelemetryAndCount(skippingSegments, skipTime[1] - skipTime[0], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSkipNotice(skippingSegments: SponsorTime[], autoSkip: boolean, unskipTime: number, startReskip: boolean) {
|
function createSkipNotice(skippingSegments: SponsorTime[], autoSkip: boolean, unskipTime: number, startReskip: boolean, voteNotice = false) {
|
||||||
for (const skipNotice of skipNotices) {
|
for (const skipNotice of skipNotices) {
|
||||||
if (skippingSegments.length === skipNotice.segments.length
|
if (skippingSegments.length === skipNotice.segments.length
|
||||||
&& skippingSegments.every((segment) => skipNotice.segments.some((s) => s.UUID === segment.UUID))) {
|
&& skippingSegments.every((segment) => skipNotice.segments.some((s) => s.UUID === segment.UUID))) {
|
||||||
@@ -1798,7 +1799,7 @@ function createSkipNotice(skippingSegments: SponsorTime[], autoSkip: boolean, un
|
|||||||
const newSkipNotice = new SkipNotice(skippingSegments, autoSkip, skipNoticeContentContainer, () => {
|
const newSkipNotice = new SkipNotice(skippingSegments, autoSkip, skipNoticeContentContainer, () => {
|
||||||
upcomingNotice?.close();
|
upcomingNotice?.close();
|
||||||
upcomingNotice = null;
|
upcomingNotice = null;
|
||||||
}, unskipTime, startReskip, upcomingNoticeShown);
|
}, unskipTime, startReskip, upcomingNoticeShown, voteNotice);
|
||||||
if (isOnMobileYouTube() || Config.config.skipKeybind == null) newSkipNotice.setShowKeybindHint(false);
|
if (isOnMobileYouTube() || Config.config.skipKeybind == null) newSkipNotice.setShowKeybindHint(false);
|
||||||
skipNotices.push(newSkipNotice);
|
skipNotices.push(newSkipNotice);
|
||||||
|
|
||||||
@@ -1817,13 +1818,13 @@ function createUpcomingNotice(skippingSegments: SponsorTime[], timeLeft: number,
|
|||||||
upcomingNotice = new UpcomingNotice(skippingSegments, skipNoticeContentContainer, timeLeft / 1000, autoSkip);
|
upcomingNotice = new UpcomingNotice(skippingSegments, skipNoticeContentContainer, timeLeft / 1000, autoSkip);
|
||||||
}
|
}
|
||||||
|
|
||||||
function unskipSponsorTime(segment: SponsorTime, unskipTime: number = null, forceSeek = false) {
|
function unskipSponsorTime(segment: SponsorTime, unskipTime: number = null, forceSeek = false, voteNotice = false) {
|
||||||
if (segment.actionType === ActionType.Mute) {
|
if (segment.actionType === ActionType.Mute) {
|
||||||
getVideo().muted = false;
|
getVideo().muted = false;
|
||||||
videoMuted = false;
|
videoMuted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forceSeek || segment.actionType === ActionType.Skip) {
|
if (forceSeek || segment.actionType === ActionType.Skip || voteNotice) {
|
||||||
//add a tiny bit of time to make sure it is not skipped again
|
//add a tiny bit of time to make sure it is not skipped again
|
||||||
setCurrentTime(unskipTime ?? segment.segment[0] + 0.001);
|
setCurrentTime(unskipTime ?? segment.segment[0] + 0.001);
|
||||||
}
|
}
|
||||||
@@ -2282,7 +2283,8 @@ async function voteAsync(type: number, UUID: SegmentUUID, category?: Category):
|
|||||||
message: "submitVote",
|
message: "submitVote",
|
||||||
type: type,
|
type: type,
|
||||||
UUID: UUID,
|
UUID: UUID,
|
||||||
category: category
|
category: category,
|
||||||
|
videoID: getVideoID()
|
||||||
}, (response) => {
|
}, (response) => {
|
||||||
if (response.successType === 1) {
|
if (response.successType === 1) {
|
||||||
// Change the sponsor locally
|
// Change the sponsor locally
|
||||||
@@ -2545,6 +2547,23 @@ function previousChapter(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleKeybindVote(type: number): Promise<void>{
|
||||||
|
let lastSkipNotice = skipNotices[0]?.skipNoticeRef.current;
|
||||||
|
lastSkipNotice?.onMouseEnter();
|
||||||
|
|
||||||
|
if (!lastSkipNotice) {
|
||||||
|
const lastSegment = [...sponsorTimes].reverse()?.find((s) => s.source == SponsorSourceType.Server && (s.segment[0] <= getCurrentTime() && getCurrentTime() - (s.segment[1] || s.segment[0]) <= Config.config.skipNoticeDuration));
|
||||||
|
if (!lastSegment) return;
|
||||||
|
|
||||||
|
createSkipNotice([lastSegment], shouldAutoSkip(lastSegment), lastSegment?.segment[0] + 0.001,false, true);
|
||||||
|
lastSkipNotice = await skipNotices[0].waitForSkipNoticeRef();
|
||||||
|
lastSkipNotice?.reskippedMode(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vote(type,lastSkipNotice?.segments[0]?.UUID, undefined, lastSkipNotice);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
function addHotkeyListener(): void {
|
function addHotkeyListener(): void {
|
||||||
document.addEventListener("keydown", hotkeyListener);
|
document.addEventListener("keydown", hotkeyListener);
|
||||||
|
|
||||||
@@ -2588,6 +2607,8 @@ function hotkeyListener(e: KeyboardEvent): void {
|
|||||||
const openSubmissionMenuKey = Config.config.submitKeybind;
|
const openSubmissionMenuKey = Config.config.submitKeybind;
|
||||||
const nextChapterKey = Config.config.nextChapterKeybind;
|
const nextChapterKey = Config.config.nextChapterKeybind;
|
||||||
const previousChapterKey = Config.config.previousChapterKeybind;
|
const previousChapterKey = Config.config.previousChapterKeybind;
|
||||||
|
const upvoteKey = Config.config.upvoteKeybind;
|
||||||
|
const downvoteKey = Config.config.downvoteKeybind;
|
||||||
|
|
||||||
if (keybindEquals(key, skipKey)) {
|
if (keybindEquals(key, skipKey)) {
|
||||||
if (activeSkipKeybindElement) {
|
if (activeSkipKeybindElement) {
|
||||||
@@ -2631,6 +2652,12 @@ function hotkeyListener(e: KeyboardEvent): void {
|
|||||||
if (sponsorTimes.length > 0) e.stopPropagation();
|
if (sponsorTimes.length > 0) e.stopPropagation();
|
||||||
previousChapter();
|
previousChapter();
|
||||||
return;
|
return;
|
||||||
|
} else if (keybindEquals(key, upvoteKey)) {
|
||||||
|
handleKeybindVote(1);
|
||||||
|
return;
|
||||||
|
} else if (keybindEquals(key, downvoteKey)) {
|
||||||
|
handleKeybindVote(0);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
src/popup.ts
17
src/popup.ts
@@ -681,9 +681,22 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
uuidButton.className = "voteButton";
|
uuidButton.className = "voteButton";
|
||||||
uuidButton.src = chrome.runtime.getURL("icons/clipboard.svg");
|
uuidButton.src = chrome.runtime.getURL("icons/clipboard.svg");
|
||||||
uuidButton.title = chrome.i18n.getMessage("copySegmentID");
|
uuidButton.title = chrome.i18n.getMessage("copySegmentID");
|
||||||
uuidButton.addEventListener("click", () => {
|
uuidButton.addEventListener("click", async () => {
|
||||||
copyToClipboard(UUID);
|
|
||||||
const stopAnimation = AnimationUtils.applyLoadingAnimation(uuidButton, 0.3);
|
const stopAnimation = AnimationUtils.applyLoadingAnimation(uuidButton, 0.3);
|
||||||
|
|
||||||
|
if (UUID.length > 60) {
|
||||||
|
copyToClipboard(UUID);
|
||||||
|
} else {
|
||||||
|
const segmentIDData = await asyncRequestToServer("GET", "/api/segmentID", {
|
||||||
|
UUID: UUID,
|
||||||
|
videoID: currentVideoID
|
||||||
|
});
|
||||||
|
|
||||||
|
if (segmentIDData.ok && segmentIDData.responseText) {
|
||||||
|
copyToClipboard(segmentIDData.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
stopAnimation();
|
stopAnimation();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import Utils from "../utils";
|
|||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
|
|
||||||
import SkipNoticeComponent from "../components/SkipNoticeComponent";
|
import SkipNoticeComponent from "../components/SkipNoticeComponent";
|
||||||
import { SponsorTime, ContentContainer, NoticeVisbilityMode } from "../types";
|
import { SponsorTime, ContentContainer, NoticeVisibilityMode } from "../types";
|
||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
import { SkipNoticeAction } from "../utils/noticeUtils";
|
import { SkipNoticeAction } from "../utils/noticeUtils";
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ class SkipNotice {
|
|||||||
skipNoticeRef: React.MutableRefObject<SkipNoticeComponent>;
|
skipNoticeRef: React.MutableRefObject<SkipNoticeComponent>;
|
||||||
root: Root;
|
root: Root;
|
||||||
|
|
||||||
constructor(segments: SponsorTime[], autoSkip = false, contentContainer: ContentContainer, componentDidMount: () => void, unskipTime: number = null, startReskip = false, upcomingNoticeShown: boolean) {
|
constructor(segments: SponsorTime[], autoSkip = false, contentContainer: ContentContainer, componentDidMount: () => void, unskipTime: number = null, startReskip = false, upcomingNoticeShown: boolean, voteNotice = false) {
|
||||||
this.skipNoticeRef = React.createRef();
|
this.skipNoticeRef = React.createRef();
|
||||||
|
|
||||||
this.segments = segments;
|
this.segments = segments;
|
||||||
@@ -42,18 +42,18 @@ class SkipNotice {
|
|||||||
this.noticeElement.id = "sponsorSkipNoticeContainer" + idSuffix;
|
this.noticeElement.id = "sponsorSkipNoticeContainer" + idSuffix;
|
||||||
|
|
||||||
referenceNode.prepend(this.noticeElement);
|
referenceNode.prepend(this.noticeElement);
|
||||||
|
|
||||||
this.root = createRoot(this.noticeElement);
|
this.root = createRoot(this.noticeElement);
|
||||||
this.root.render(
|
this.root.render(
|
||||||
<SkipNoticeComponent segments={segments}
|
<SkipNoticeComponent segments={segments}
|
||||||
autoSkip={autoSkip}
|
autoSkip={autoSkip}
|
||||||
startReskip={startReskip}
|
startReskip={startReskip}
|
||||||
|
voteNotice={voteNotice}
|
||||||
contentContainer={contentContainer}
|
contentContainer={contentContainer}
|
||||||
ref={this.skipNoticeRef}
|
ref={this.skipNoticeRef}
|
||||||
closeListener={() => this.close()}
|
closeListener={() => this.close()}
|
||||||
smaller={Config.config.noticeVisibilityMode >= NoticeVisbilityMode.MiniForAll
|
smaller={!voteNotice && (Config.config.noticeVisibilityMode >= NoticeVisibilityMode.MiniForAll
|
||||||
|| (Config.config.noticeVisibilityMode >= NoticeVisbilityMode.MiniForAutoSkip && autoSkip)}
|
|| (Config.config.noticeVisibilityMode >= NoticeVisibilityMode.MiniForAutoSkip && autoSkip))}
|
||||||
fadeIn={!upcomingNoticeShown}
|
fadeIn={!upcomingNoticeShown && !voteNotice}
|
||||||
unskipTime={unskipTime}
|
unskipTime={unskipTime}
|
||||||
componentDidMount={componentDidMount} />
|
componentDidMount={componentDidMount} />
|
||||||
);
|
);
|
||||||
@@ -81,6 +81,26 @@ class SkipNotice {
|
|||||||
unmutedListener(time: number): void {
|
unmutedListener(time: number): void {
|
||||||
this.skipNoticeRef?.current?.unmutedListener(time);
|
this.skipNoticeRef?.current?.unmutedListener(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async waitForSkipNoticeRef(): Promise<SkipNoticeComponent> {
|
||||||
|
const waitForRef = () => new Promise<SkipNoticeComponent>((resolve) => {
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
if (this.skipNoticeRef.current) {
|
||||||
|
observer.disconnect();
|
||||||
|
resolve(this.skipNoticeRef.current);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.getElementsByClassName("sponsorSkipNoticeContainer")[0], { childList: true, subtree: true});
|
||||||
|
|
||||||
|
if (this.skipNoticeRef.current) {
|
||||||
|
observer.disconnect();
|
||||||
|
resolve(this.skipNoticeRef.current);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.skipNoticeRef?.current || await waitForRef();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SkipNotice;
|
export default SkipNotice;
|
||||||
@@ -6,7 +6,7 @@ export interface ContentContainer {
|
|||||||
(): {
|
(): {
|
||||||
vote: (type: number, UUID: SegmentUUID, category?: Category, skipNotice?: SkipNoticeComponent) => void;
|
vote: (type: number, UUID: SegmentUUID, category?: Category, skipNotice?: SkipNoticeComponent) => void;
|
||||||
dontShowNoticeAgain: () => void;
|
dontShowNoticeAgain: () => void;
|
||||||
unskipSponsorTime: (segment: SponsorTime, unskipTime: number, forceSeek?: boolean) => void;
|
unskipSponsorTime: (segment: SponsorTime, unskipTime: number, forceSeek?: boolean, voteNotice?: boolean) => void;
|
||||||
sponsorTimes: SponsorTime[];
|
sponsorTimes: SponsorTime[];
|
||||||
sponsorTimesSubmitting: SponsorTime[];
|
sponsorTimesSubmitting: SponsorTime[];
|
||||||
skipNotices: SkipNotice[];
|
skipNotices: SkipNotice[];
|
||||||
@@ -219,7 +219,7 @@ export interface ToggleSkippable {
|
|||||||
setShowKeybindHint: (show: boolean) => void;
|
setShowKeybindHint: (show: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum NoticeVisbilityMode {
|
export enum NoticeVisibilityMode {
|
||||||
FullSize = 0,
|
FullSize = 0,
|
||||||
MiniForAutoSkip = 1,
|
MiniForAutoSkip = 1,
|
||||||
MiniForAll = 2,
|
MiniForAll = 2,
|
||||||
|
|||||||
15
src/utils.ts
15
src/utils.ts
@@ -5,6 +5,7 @@ import { getHash, HashedValue } from "../maze-utils/src/hash";
|
|||||||
import { waitFor } from "../maze-utils/src";
|
import { waitFor } from "../maze-utils/src";
|
||||||
import { findValidElementFromSelector } from "../maze-utils/src/dom";
|
import { findValidElementFromSelector } from "../maze-utils/src/dom";
|
||||||
import { isSafari } from "../maze-utils/src/config";
|
import { isSafari } from "../maze-utils/src/config";
|
||||||
|
import { asyncRequestToServer } from "./utils/requests";
|
||||||
|
|
||||||
export default class Utils {
|
export default class Utils {
|
||||||
|
|
||||||
@@ -198,7 +199,7 @@ export default class Utils {
|
|||||||
|
|
||||||
getSponsorIndexFromUUID(sponsorTimes: SponsorTime[], UUID: string): number {
|
getSponsorIndexFromUUID(sponsorTimes: SponsorTime[], UUID: string): number {
|
||||||
for (let i = 0; i < sponsorTimes.length; i++) {
|
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||||
if (sponsorTimes[i].UUID === UUID) {
|
if (sponsorTimes[i].UUID && (sponsorTimes[i].UUID.startsWith(UUID) || UUID.startsWith(sponsorTimes[i].UUID))) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,6 +284,17 @@ export default class Utils {
|
|||||||
if ((chrome.extension.inIncognitoContext && !Config.config.trackDownvotesInPrivate)
|
if ((chrome.extension.inIncognitoContext && !Config.config.trackDownvotesInPrivate)
|
||||||
|| !Config.config.trackDownvotes) return;
|
|| !Config.config.trackDownvotes) return;
|
||||||
|
|
||||||
|
if (segmentUUID.length < 60) {
|
||||||
|
const segmentIDData = await asyncRequestToServer("GET", "/api/segmentID", {
|
||||||
|
UUID: segmentUUID,
|
||||||
|
videoID
|
||||||
|
});
|
||||||
|
|
||||||
|
if (segmentIDData.ok && segmentIDData.responseText) {
|
||||||
|
segmentUUID = segmentIDData.responseText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const hashedVideoID = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue;
|
const hashedVideoID = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue;
|
||||||
const UUIDHash = await getHash(segmentUUID, 1);
|
const UUIDHash = await getHash(segmentUUID, 1);
|
||||||
|
|
||||||
@@ -309,6 +321,7 @@ export default class Utils {
|
|||||||
|
|
||||||
allDownvotes[hashedVideoID] = currentVideoData;
|
allDownvotes[hashedVideoID] = currentVideoData;
|
||||||
}
|
}
|
||||||
|
console.log(allDownvotes)
|
||||||
|
|
||||||
const entries = Object.entries(allDownvotes);
|
const entries = Object.entries(allDownvotes);
|
||||||
if (entries.length > 10000) {
|
if (entries.length > 10000) {
|
||||||
|
|||||||
@@ -44,6 +44,14 @@ export function getUpcomingText(segments: SponsorTime[]): string {
|
|||||||
return chrome.i18n.getMessage(messageId).replace("{0}", categoryName);
|
return chrome.i18n.getMessage(messageId).replace("{0}", categoryName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getVoteText(segments: SponsorTime[]): string {
|
||||||
|
const categoryName = chrome.i18n.getMessage(segments.length > 1 ? "multipleSegments"
|
||||||
|
: "category_" + segments[0].category + "_short") || chrome.i18n.getMessage("category_" + segments[0].category);
|
||||||
|
|
||||||
|
const messageId = "voted_on";
|
||||||
|
return chrome.i18n.getMessage(messageId).replace("{0}", categoryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function getCategorySuffix(category: Category): string {
|
export function getCategorySuffix(category: Category): string {
|
||||||
if (category.startsWith("poi_")) {
|
if (category.startsWith("poi_")) {
|
||||||
|
|||||||
@@ -34,9 +34,12 @@ function exportTime(segment: SponsorTime): string {
|
|||||||
|
|
||||||
export function importTimes(data: string, videoDuration: number): SponsorTime[] {
|
export function importTimes(data: string, videoDuration: number): SponsorTime[] {
|
||||||
const lines = data.split("\n");
|
const lines = data.split("\n");
|
||||||
|
const timeRegex = /(?:((?:\d+:)?\d+:\d+)+(?:\.\d+)?)|(?:\d+(?=s| second))/g;
|
||||||
|
const anyLineHasTime = lines.some((line) => timeRegex.test(line));
|
||||||
|
|
||||||
const result: SponsorTime[] = [];
|
const result: SponsorTime[] = [];
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const match = line.match(/(?:((?:\d+:)?\d+:\d+)+(?:\.\d+)?)|(?:\d+(?=s| second))/g);
|
const match = line.match(timeRegex);
|
||||||
if (match) {
|
if (match) {
|
||||||
const startTime = getFormattedTimeToSeconds(match[0]);
|
const startTime = getFormattedTimeToSeconds(match[0]);
|
||||||
if (startTime !== null) {
|
if (startTime !== null) {
|
||||||
@@ -71,6 +74,18 @@ export function importTimes(data: string, videoDuration: number): SponsorTime[]
|
|||||||
|
|
||||||
result.push(segment);
|
result.push(segment);
|
||||||
}
|
}
|
||||||
|
} else if (!anyLineHasTime) {
|
||||||
|
// Adding chapters with placeholder times
|
||||||
|
const segment: SponsorTime = {
|
||||||
|
segment: [0, 0],
|
||||||
|
category: "chapter" as Category,
|
||||||
|
actionType: ActionType.Chapter,
|
||||||
|
description: line,
|
||||||
|
source: SponsorSourceType.Local,
|
||||||
|
UUID: generateUserID() as SegmentUUID
|
||||||
|
};
|
||||||
|
|
||||||
|
result.push(segment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,9 +50,11 @@ async function fetchSegmentsForVideo(videoID: VideoID): Promise<SegmentResponse>
|
|||||||
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
|
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
|
||||||
|
|
||||||
const hashPrefix = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue;
|
const hashPrefix = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue;
|
||||||
|
const hasDownvotedSegments = !!Config.local.downvotedSegments[hashPrefix];
|
||||||
const response = await asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
|
const response = await asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
|
||||||
categories: CompileConfig.categoryList,
|
categories: CompileConfig.categoryList,
|
||||||
actionTypes: ActionTypes,
|
actionTypes: ActionTypes,
|
||||||
|
trimUUIDs: hasDownvotedSegments ? null : 5,
|
||||||
...extraRequestData
|
...extraRequestData
|
||||||
}, {
|
}, {
|
||||||
"X-CLIENT-NAME": `${chrome.runtime.id}/v${chrome.runtime.getManifest().version}`
|
"X-CLIENT-NAME": `${chrome.runtime.id}/v${chrome.runtime.getManifest().version}`
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { isOnInvidious, parseYouTubeVideoIDFromURL } from "../../maze-utils/src/video";
|
import { isOnInvidious, parseYouTubeVideoIDFromURL } from "../../maze-utils/src/video";
|
||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
import { getVideoLabel } from "./videoLabels";
|
import { getHasStartSegment, getVideoLabel } from "./videoLabels";
|
||||||
import { getThumbnailSelector, setThumbnailListener } from "../../maze-utils/src/thumbnailManagement";
|
import { getThumbnailSelector, setThumbnailListener } from "../../maze-utils/src/thumbnailManagement";
|
||||||
import { VideoID } from "../types";
|
import { VideoID } from "../types";
|
||||||
import { getSegmentsForVideo } from "./segmentData";
|
import { getSegmentsForVideo } from "./segmentData";
|
||||||
@@ -58,10 +58,27 @@ function thumbnailHoverListener(e: MouseEvent) {
|
|||||||
if (!thumbnail) return;
|
if (!thumbnail) return;
|
||||||
|
|
||||||
// Pre-fetch data for this video
|
// Pre-fetch data for this video
|
||||||
|
let fetched = false;
|
||||||
|
const preFetch = async () => {
|
||||||
|
fetched = true;
|
||||||
const videoID = extractVideoID(thumbnail);
|
const videoID = extractVideoID(thumbnail);
|
||||||
if (videoID) {
|
if (videoID && await getHasStartSegment(videoID)) {
|
||||||
void getSegmentsForVideo(videoID, false);
|
void getSegmentsForVideo(videoID, false);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
const timeout = setTimeout(preFetch, 100);
|
||||||
|
const onMouseDown = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
if (!fetched) {
|
||||||
|
preFetch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
e.target.addEventListener("mousedown", onMouseDown, { once: true });
|
||||||
|
e.target.addEventListener("mouseleave", () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
e.target.removeEventListener("mousedown", onMouseDown);
|
||||||
|
}, { once: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLink(thumbnail: HTMLImageElement): HTMLAnchorElement | null {
|
function getLink(thumbnail: HTMLImageElement): HTMLAnchorElement | null {
|
||||||
|
|||||||
@@ -6,9 +6,14 @@ import { asyncRequestToServer } from "./requests";
|
|||||||
|
|
||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
|
|
||||||
|
interface VideoLabelsCacheData {
|
||||||
|
category: Category;
|
||||||
|
hasStartSegment: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface LabelCacheEntry {
|
export interface LabelCacheEntry {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
videos: Record<VideoID, Category>;
|
videos: Record<VideoID, VideoLabelsCacheData>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const labelCache: Record<string, LabelCacheEntry> = {};
|
const labelCache: Record<string, LabelCacheEntry> = {};
|
||||||
@@ -21,7 +26,7 @@ async function getLabelHashBlock(hashPrefix: string): Promise<LabelCacheEntry |
|
|||||||
return cachedEntry;
|
return cachedEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await asyncRequestToServer("GET", `/api/videoLabels/${hashPrefix}`);
|
const response = await asyncRequestToServer("GET", `/api/videoLabels/${hashPrefix}?hasStartSegment=true`);
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
// No video labels or server down
|
// No video labels or server down
|
||||||
labelCache[hashPrefix] = {
|
labelCache[hashPrefix] = {
|
||||||
@@ -36,7 +41,10 @@ async function getLabelHashBlock(hashPrefix: string): Promise<LabelCacheEntry |
|
|||||||
|
|
||||||
const newEntry: LabelCacheEntry = {
|
const newEntry: LabelCacheEntry = {
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
videos: Object.fromEntries(data.map(video => [video.videoID, video.segments[0].category])),
|
videos: Object.fromEntries(data.map(video => [video.videoID, {
|
||||||
|
category: video.segments[0]?.category,
|
||||||
|
hasStartSegment: video.hasStartSegment
|
||||||
|
}])),
|
||||||
};
|
};
|
||||||
labelCache[hashPrefix] = newEntry;
|
labelCache[hashPrefix] = newEntry;
|
||||||
|
|
||||||
@@ -55,11 +63,11 @@ async function getLabelHashBlock(hashPrefix: string): Promise<LabelCacheEntry |
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getVideoLabel(videoID: VideoID): Promise<Category | null> {
|
export async function getVideoLabel(videoID: VideoID): Promise<Category | null> {
|
||||||
const prefix = (await getHash(videoID, 1)).slice(0, 3);
|
const prefix = (await getHash(videoID, 1)).slice(0, 4);
|
||||||
const result = await getLabelHashBlock(prefix);
|
const result = await getLabelHashBlock(prefix);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
const category = result.videos[videoID];
|
const category = result.videos[videoID]?.category;
|
||||||
if (category && utils.getCategorySelection(category).option !== CategorySkipOption.Disabled) {
|
if (category && utils.getCategorySelection(category).option !== CategorySkipOption.Disabled) {
|
||||||
return category;
|
return category;
|
||||||
} else {
|
} else {
|
||||||
@@ -69,3 +77,14 @@ export async function getVideoLabel(videoID: VideoID): Promise<Category | null>
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getHasStartSegment(videoID: VideoID): Promise<boolean | null> {
|
||||||
|
const prefix = (await getHash(videoID, 1)).slice(0, 4);
|
||||||
|
const result = await getLabelHashBlock(prefix);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return result?.videos[videoID]?.hasStartSegment ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user