Merge branch 'master' into settings

This commit is contained in:
Ajay Ramachandran
2022-01-24 23:27:52 -05:00
committed by GitHub
8 changed files with 102 additions and 94 deletions

View File

@@ -13,7 +13,7 @@
"preview": ["skip", "mute"],
"filler": ["skip", "mute"],
"music_offtopic": ["skip"],
"poi_highlight": ["skip"]
"poi_highlight": ["poi"]
},
"wikiLinks": {
"sponsor": "https://wiki.sponsor.ajay.app/w/Sponsor",

View File

@@ -243,7 +243,7 @@
"message": "All Faded Skip Notices"
},
"longDescription": {
"message": "SponsorBlock lets you skip over sponsors, intros, outros, subscription reminders, and other annoying parts of YouTube videos. SponsorBlock is a crowdsourced browser extension that let's anyone submit the start and end time's of sponsored segments and other segments of YouTube videos. Once one person submits this information, everyone else with this extension will skip right over the sponsored segment. You can also skip over non music sections of music videos.",
"message": "SponsorBlock lets you skip over sponsors, intros, outros, subscription reminders, and other annoying parts of YouTube videos. SponsorBlock is a crowdsourced browser extension that lets anyone submit the start and end times of sponsored segments and other segments of YouTube videos. Once one person submits this information, everyone else with this extension will skip right over the sponsored segment. You can also skip over non music sections of music videos.",
"description": "Full description of the extension on the store pages."
},
"website": {
@@ -432,7 +432,7 @@
"message": "Skip notice duration (seconds):"
},
"skipNoticeDurationDescription": {
"message": "The skip notice will stay on screen for at least this long. For manual skipping, it may be visible for longer."
"message": "The skip notice will stay on screen for at least this many seconds. For manual skipping, it may be visible for longer."
},
"shortCheck": {
"message": "The following submission is shorter than your minimum duration option. This could mean that this is already submitted, and just being ignored due to this option. Are you sure you would like to submit?"

View File

@@ -1,12 +1,11 @@
import * as React from "react";
import * as CompileConfig from "../../config.json";
import Config from "../config"
import { Category, ContentContainer, CategoryActionType, SponsorHideType, SponsorTime, NoticeVisbilityMode, ActionType, SponsorSourceType, SegmentUUID } from "../types";
import { Category, ContentContainer, SponsorHideType, SponsorTime, NoticeVisbilityMode, ActionType, SponsorSourceType, SegmentUUID } from "../types";
import NoticeComponent from "./NoticeComponent";
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
import Utils from "../utils";
const utils = new Utils();
import { getCategoryActionType, getSkippingText } from "../utils/categoryUtils";
import { keybindToString } from "../utils/configUtils";
@@ -327,7 +326,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
getSkipButton(): JSX.Element {
if (this.state.showSkipButton && (this.segments.length > 1
|| getCategoryActionType(this.segments[0].category) !== CategoryActionType.POI
|| this.segments[0].actionType !== ActionType.Poi
|| this.props.unskipTime)) {
const style: React.CSSProperties = {
@@ -548,7 +547,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
getCategoryOptions(): React.ReactElement[] {
const elements = [];
const categories = (CompileConfig.categoryList.filter((cat => getCategoryActionType(cat as Category) === CategoryActionType.Skippable))) as Category[];
const categories = (CompileConfig.categoryList.filter((cat => CompileConfig.categorySupport[cat].includes(ActionType.Skip)))) as Category[];
for (const category of categories) {
elements.push(
<option value={category}
@@ -602,7 +601,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}
getUnskippedModeInfo(index: number, buttonText: string): SkipNoticeState {
const changeCountdown = getCategoryActionType(this.segments[index].category) === CategoryActionType.Skippable;
const changeCountdown = this.segments[index].actionType !== ActionType.Poi;
const maxCountdownTime = changeCountdown ? () => {
const sponsorTime = this.segments[index];

View File

@@ -1,9 +1,8 @@
import * as React from "react";
import * as CompileConfig from "../../config.json";
import Config from "../config";
import { ActionType, Category, CategoryActionType, ContentContainer, SponsorTime } from "../types";
import { ActionType, Category, ContentContainer, SponsorTime } from "../types";
import Utils from "../utils";
import { getCategoryActionType } from "../utils/categoryUtils";
import SubmissionNoticeComponent from "./SubmissionNoticeComponent";
import { RectangleTooltip } from "../render/RectangleTooltip";
@@ -38,8 +37,9 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
configUpdateListener: () => void;
previousSkipType: CategoryActionType;
timeBeforeChangingToPOI: number; // Initialized when first selecting POI
previousSkipType: ActionType;
// Used when selecting POI or Full
timesBeforeChanging: number[] = [];
fullVideoWarningShown = false;
constructor(props: SponsorTimeEditProps) {
@@ -50,7 +50,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
this.idSuffix = this.props.idSuffix;
this.previousSkipType = CategoryActionType.Skippable;
this.previousSkipType = ActionType.Skip;
this.state = {
editing: false,
sponsorTimeEdits: [null, null],
@@ -129,7 +129,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
onWheel={(e) => {this.changeTimesWhenScrolling(0, e, sponsorTime)}}>
</input>
{getCategoryActionType(sponsorTime.category) === CategoryActionType.Skippable ? (
{sponsorTime.actionType !== ActionType.Poi ? (
<span>
<span>
{" " + chrome.i18n.getMessage("to") + " "}
@@ -167,7 +167,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
className="sponsorTimeDisplay"
onClick={this.toggleEditTime.bind(this)}>
{utils.getFormattedTime(segment[0], true) +
((!isNaN(segment[1]) && getCategoryActionType(sponsorTime.category) === CategoryActionType.Skippable)
((!isNaN(segment[1]) && sponsorTime.actionType !== ActionType.Poi)
? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segment[1], true) : "")}
</div>
);
@@ -202,13 +202,13 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
{/* Action Type */}
{CompileConfig.categorySupport[sponsorTime.category] &&
(CompileConfig.categorySupport[sponsorTime.category]?.length > 1
|| CompileConfig.categorySupport[sponsorTime.category]?.[0] !== "skip") ? (
|| CompileConfig.categorySupport[sponsorTime.category]?.[0] === ActionType.Full) ? (
<div style={{position: "relative"}}>
<select id={"sponsorTimeActionTypes" + this.idSuffix}
className="sponsorTimeEditSelector sponsorTimeActionTypes"
defaultValue={sponsorTime.actionType}
ref={this.actionTypeOptionRef}
onChange={() => this.saveEditTimes()}>
onChange={(e) => this.actionTypeSelectionChange(e)}>
{this.getActionTypeOptions(sponsorTime)}
</select>
</div>
@@ -224,7 +224,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
{chrome.i18n.getMessage("delete")}
</span>
{(!isNaN(segment[1]) && getCategoryActionType(sponsorTime.category) === CategoryActionType.Skippable) ? (
{(!isNaN(segment[1]) && ![ActionType.Poi, ActionType.Full].includes(sponsorTime.actionType)) ? (
<span id={"sponsorTimePreviewButton" + this.idSuffix}
className="sponsorTimeEditButton"
onClick={(e) => this.previewTime(e.ctrlKey, e.shiftKey)}>
@@ -261,7 +261,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
if (0 < difference && difference< 0.5) this.showScrollToEditToolTip();
sponsorTimeEdits[index] = targetValue;
if (index === 0 && getCategoryActionType(sponsorTime.category) === CategoryActionType.POI) sponsorTimeEdits[1] = targetValue;
if (index === 0 && sponsorTime.actionType === ActionType.Poi) sponsorTimeEdits[1] = targetValue;
this.setState({sponsorTimeEdits});
this.saveEditTimes();
@@ -290,7 +290,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
timeAsNumber = 0;
}
sponsorTimeEdits[index] = utils.getFormattedTime(timeAsNumber, true);
if (getCategoryActionType(sponsorTime.category) === CategoryActionType.POI) sponsorTimeEdits[1] = sponsorTimeEdits[0];
if (sponsorTime.actionType === ActionType.Poi) sponsorTimeEdits[1] = sponsorTimeEdits[0];
this.setState({sponsorTimeEdits});
this.saveEditTimes();
@@ -380,21 +380,51 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
return;
}
if (getCategoryActionType(event.target.value as Category) === CategoryActionType.POI) {
if (this.previousSkipType === CategoryActionType.Skippable) this.timeBeforeChangingToPOI = utils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[1]);
const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
this.handleReplacingLostTimes(event.target.value as Category, sponsorTime.actionType);
this.saveEditTimes();
}
actionTypeSelectionChange(event: React.ChangeEvent<HTMLSelectElement>): void {
const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
this.handleReplacingLostTimes(sponsorTime.category, event.target.value as ActionType);
this.saveEditTimes();
}
private handleReplacingLostTimes(category: Category, actionType: ActionType): void {
if (CompileConfig.categorySupport[category]?.includes(ActionType.Poi)) {
if (this.previousSkipType !== ActionType.Poi) {
this.timesBeforeChanging = [null, utils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[1])];
}
this.setTimeTo(1, null);
this.props.contentContainer().updateEditButtonsOnPlayer();
if (this.props.contentContainer().sponsorTimesSubmitting
.some((segment, i) => segment.category === event.target.value && i !== this.props.index)) {
.some((segment, i) => segment.category === category && i !== this.props.index)) {
alert(chrome.i18n.getMessage("poiOnlyOneSegment"));
}
} else if (getCategoryActionType(event.target.value as Category) === CategoryActionType.Skippable && this.previousSkipType === CategoryActionType.POI) {
this.setTimeTo(1, this.timeBeforeChangingToPOI);
this.previousSkipType = ActionType.Poi;
} else if (CompileConfig.categorySupport[category]?.length === 1
&& CompileConfig.categorySupport[category]?.[0] === ActionType.Full) {
if (this.previousSkipType !== ActionType.Full) {
this.timesBeforeChanging = [utils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[0]), utils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[1])];
}
this.previousSkipType = getCategoryActionType(event.target.value as Category);
this.saveEditTimes();
this.previousSkipType = ActionType.Full;
} else if (CompileConfig.categorySupport[category]?.includes(ActionType.Skip)
&& ![ActionType.Poi, ActionType.Full].includes(this.getNextActionType(category, actionType)) && this.previousSkipType !== ActionType.Skip) {
if (this.timesBeforeChanging[0]) {
this.setTimeTo(0, this.timesBeforeChanging[0]);
}
if (this.timesBeforeChanging[1]) {
this.setTimeTo(1, this.timesBeforeChanging[1]);
}
this.previousSkipType = ActionType.Skip;
}
}
getActionTypeOptions(sponsorTime: SponsorTime): React.ReactElement[] {
@@ -429,7 +459,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
if (time === null) time = sponsorTime.segment[0];
sponsorTime.segment[index] = time;
if (getCategoryActionType(sponsorTime.category) === CategoryActionType.POI) sponsorTime.segment[1] = time;
if (sponsorTime.actionType === ActionType.Poi) sponsorTime.segment[1] = time;
this.setState({
sponsorTimeEdits: this.getFormattedSponsorTimesEdits(sponsorTime)
@@ -477,9 +507,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
sponsorTimesSubmitting[this.props.index].category = category;
const inputActionType = this.actionTypeOptionRef?.current?.value as ActionType;
const actionType = inputActionType && CompileConfig.categorySupport[category]?.includes(inputActionType) ? inputActionType as ActionType
: CompileConfig.categorySupport[category]?.[0] ?? ActionType.Skip;
sponsorTimesSubmitting[this.props.index].actionType = actionType;
sponsorTimesSubmitting[this.props.index].actionType = this.getNextActionType(category, inputActionType);
Config.config.segmentTimes.set(this.props.contentContainer().sponsorVideoID, sponsorTimesSubmitting);
@@ -492,6 +520,11 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
}
}
private getNextActionType(category: Category, actionType: ActionType): ActionType {
return actionType && CompileConfig.categorySupport[category]?.includes(actionType) ? actionType
: CompileConfig.categorySupport[category]?.[0] ?? ActionType.Skip
}
previewTime(ctrlPressed = false, shiftPressed = false): void {
const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
const index = this.props.index;

View File

@@ -1,5 +1,5 @@
import Config from "./config";
import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, VideoInfo, StorageChangesObject, CategoryActionType, ChannelIDInfo, ChannelIDStatus, SponsorSourceType, SegmentUUID, Category, SkipToTimeParams, ToggleSkippable, ActionType, ScheduledTime } from "./types";
import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, VideoInfo, StorageChangesObject, ChannelIDInfo, ChannelIDStatus, SponsorSourceType, SegmentUUID, Category, SkipToTimeParams, ToggleSkippable, ActionType, ScheduledTime } from "./types";
import { ContentContainer, Keybind } from "./types";
import Utils from "./utils";
@@ -13,7 +13,6 @@ import SkipNoticeComponent from "./components/SkipNoticeComponent";
import SubmissionNotice from "./render/SubmissionNotice";
import { Message, MessageResponse, VoteResponse } from "./messageTypes";
import * as Chat from "./js-components/chat";
import { getCategoryActionType } from "./utils/categoryUtils";
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
import { getStartTimeFromUrl } from "./utils/urlParser";
import { findValidElement, getControls, getHashParams, isVisible } from "./utils/pageUtils";
@@ -97,10 +96,6 @@ window.addEventListener("DOMContentLoaded", () => utils.waitForElement(".ytp-inl
addPageListeners();
addHotkeyListener();
//the amount of times the sponsor lookup has retried
//this only happens if there is an error
let sponsorLookupRetries = 0;
/** Segments created by the user which have not yet been submitted. */
let sponsorTimesSubmitting: SponsorTime[] = [];
@@ -238,7 +233,6 @@ function resetValues() {
//reset sponsor times
sponsorTimes = null;
sponsorLookupRetries = 0;
sponsorSkipped = [];
videoInfo = null;
@@ -735,7 +729,7 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) {
if (Config.config.minDuration !== 0) {
for (let i = 0; i < sponsorTimes.length; i++) {
if (sponsorTimes[i].segment[1] - sponsorTimes[i].segment[0] < Config.config.minDuration
&& getCategoryActionType(sponsorTimes[i].category) !== CategoryActionType.POI) {
&& sponsorTimes[i].actionType !== ActionType.Poi) {
sponsorTimes[i].hidden = SponsorHideType.MinimumDuration;
}
}
@@ -761,8 +755,6 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) {
//otherwise the listener can handle it
updatePreviewBar();
}
sponsorLookupRetries = 0;
} else if (response?.status === 404) {
retryFetch();
}
@@ -771,7 +763,7 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) {
}
function getEnabledActionTypes(): ActionType[] {
const actionTypes = [ActionType.Skip];
const actionTypes = [ActionType.Skip, ActionType.Poi];
if (Config.config.muteSegments) {
actionTypes.push(ActionType.Mute);
}
@@ -839,8 +831,6 @@ function retryFetch(): void {
sponsorsLookup(sponsorVideoID);
}
}, 10000 + Math.random() * 30000);
sponsorLookupRetries = 0;
}
/**
@@ -856,7 +846,7 @@ function startSkipScheduleCheckingForStartSponsors() {
let startingSegment: SponsorTime = null;
for (const time of sponsorTimes) {
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime
&& getCategoryActionType(time.category) === CategoryActionType.Skippable) {
&& time.actionType !== ActionType.Poi) {
startingSegmentTime = time.segment[0];
startingSegment = time;
found = true;
@@ -866,7 +856,7 @@ function startSkipScheduleCheckingForStartSponsors() {
if (!found) {
for (const time of sponsorTimesSubmitting) {
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime
&& getCategoryActionType(time.category) === CategoryActionType.Skippable) {
&& time.actionType !== ActionType.Poi) {
startingSegmentTime = time.segment[0];
startingSegment = time;
found = true;
@@ -877,7 +867,7 @@ function startSkipScheduleCheckingForStartSponsors() {
// For highlight category
const poiSegments = sponsorTimes
.filter((time) => time.segment[1] > video.currentTime && getCategoryActionType(time.category) === CategoryActionType.POI)
.filter((time) => time.segment[1] > video.currentTime && time.actionType === ActionType.Poi)
.sort((a, b) => b.segment[0] - a.segment[0]);
for (const time of poiSegments) {
const skipOption = utils.getCategorySelection(time.category)?.option;
@@ -1020,7 +1010,7 @@ function updatePreviewBar(): void {
category: segment.category,
unsubmitted: false,
actionType: segment.actionType,
showLarger: getCategoryActionType(segment.category) === CategoryActionType.POI
showLarger: segment.actionType === ActionType.Poi
});
});
}
@@ -1031,7 +1021,7 @@ function updatePreviewBar(): void {
category: segment.category,
unsubmitted: true,
actionType: segment.actionType,
showLarger: getCategoryActionType(segment.category) === CategoryActionType.POI
showLarger: segment.actionType === ActionType.Poi
});
});
@@ -1204,7 +1194,7 @@ function getStartTimes(sponsorTimes: SponsorTime[], includeIntersectingSegments:
|| (includeIntersectingSegments && possibleTimes[i].scheduledTime < minimum && possibleTimes[i].segment[1] > minimum)))
&& (!onlySkippableSponsors || shouldSkip(possibleTimes[i]))
&& (!hideHiddenSponsors || possibleTimes[i].hidden === SponsorHideType.Visible)
&& getCategoryActionType(possibleTimes[i].category) === CategoryActionType.Skippable) {
&& possibleTimes[i].actionType !== ActionType.Poi) {
scheduledTimes.push(possibleTimes[i].scheduledTime);
includedTimes.push(possibleTimes[i]);
@@ -1258,6 +1248,7 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
if ((autoSkip || sponsorTimesSubmitting.some((time) => time.segment === skippingSegments[0].segment))
&& v.currentTime !== skipTime[1]) {
switch(skippingSegments[0].actionType) {
case ActionType.Poi:
case ActionType.Skip: {
// Fix for looped videos not working when skipping to the end #426
// for some reason you also can't skip to 1 second before the end
@@ -1290,7 +1281,7 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
if (!autoSkip
&& skippingSegments.length === 1
&& getCategoryActionType(skippingSegments[0].category) === CategoryActionType.POI) {
&& skippingSegments[0].actionType === ActionType.Poi) {
skipButtonControlBar.enable(skippingSegments[0]);
if (onMobileYouTube || Config.config.skipKeybind == null) skipButtonControlBar.setShowKeybindHint(false);
@@ -1381,7 +1372,7 @@ function createButton(baseID: string, title: string, callback: () => void, image
function shouldAutoSkip(segment: SponsorTime): boolean {
return utils.getCategorySelection(segment.category)?.option === CategorySkipOption.AutoSkip ||
(Config.config.autoSkipOnMusicVideos && sponsorTimes?.some((s) => s.category === "music_offtopic")
&& getCategoryActionType(segment.category) === CategoryActionType.Skippable);
&& segment.actionType !== ActionType.Poi);
}
function shouldSkip(segment: SponsorTime): boolean {

View File

@@ -1,10 +1,9 @@
import Config from "./config";
import Utils from "./utils";
import { SponsorTime, SponsorHideType, CategoryActionType, ActionType } from "./types";
import { SponsorTime, SponsorHideType, ActionType } from "./types";
import { Message, MessageResponse, IsInfoFoundMessageResponse } from "./messageTypes";
import { showDonationLink } from "./utils/configUtils";
import { getCategoryActionType } from "./utils/categoryUtils";
import { AnimationUtils } from "./utils/animationUtils";
import { GenericUtils } from "./utils/genericUtils";
const utils = new Utils();
@@ -411,7 +410,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
segmentTimeFromToNode.innerText = chrome.i18n.getMessage("full");
} else {
segmentTimeFromToNode.innerText = utils.getFormattedTime(segmentTimes[i].segment[0], true) +
(getCategoryActionType(segmentTimes[i].category) !== CategoryActionType.POI
(segmentTimes[i].actionType !== ActionType.Poi
? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segmentTimes[i].segment[1], true)
: "");
}

View File

@@ -52,15 +52,11 @@ export enum SponsorHideType {
MinimumDuration
}
export enum CategoryActionType {
Skippable = "", // Strings are used to find proper language configs
POI = "_POI"
}
export enum ActionType {
Skip = "skip",
Mute = "mute",
Full = "full"
Full = "full",
Poi = "poi"
}
export const ActionTypes = [ActionType.Skip, ActionType.Mute];

View File

@@ -1,11 +1,10 @@
import { ActionType, Category, CategoryActionType, SponsorTime } from "../types";
import { ActionType, Category, SponsorTime } from "../types";
export function getSkippingText(segments: SponsorTime[], autoSkip: boolean): string {
const categoryName = chrome.i18n.getMessage(segments.length > 1 ? "multipleSegments"
: "category_" + segments[0].category + "_short") || chrome.i18n.getMessage("category_" + segments[0].category);
if (autoSkip) {
let messageId = "";
if (getCategoryActionType(segments[0].category) === CategoryActionType.Skippable) {
switch (segments[0].actionType) {
case ActionType.Skip:
messageId = "skipped";
@@ -13,15 +12,14 @@ export function getSkippingText(segments: SponsorTime[], autoSkip: boolean): str
case ActionType.Mute:
messageId = "muted";
break;
}
} else {
case ActionType.Poi:
messageId = "skipped_to_category";
break;
}
return chrome.i18n.getMessage(messageId).replace("{0}", categoryName);
} else {
let messageId = "";
if (getCategoryActionType(segments[0].category) === CategoryActionType.Skippable) {
switch (segments[0].actionType) {
case ActionType.Skip:
messageId = "skip_category";
@@ -29,23 +27,15 @@ export function getSkippingText(segments: SponsorTime[], autoSkip: boolean): str
case ActionType.Mute:
messageId = "mute_category";
break;
}
} else {
case ActionType.Poi:
messageId = "skip_to_category";
break;
}
return chrome.i18n.getMessage(messageId).replace("{0}", categoryName);
}
}
export function getCategoryActionType(category: Category): CategoryActionType {
if (category.startsWith("poi_")) {
return CategoryActionType.POI;
} else {
return CategoryActionType.Skippable;
}
}
export function getCategorySuffix(category: Category): string {
if (category.startsWith("poi_")) {
return "_POI";