ultrawidify/src/common/interfaces/SettingsInterface.ts

414 lines
19 KiB
TypeScript
Raw Normal View History

import { Action } from '../../../node_modules/vuex/types/index'
import AntiGradientMode from '../enums/AntiGradientMode.enum'
import AspectRatioType from '../enums/AspectRatioType.enum'
import CropModePersistence from '../enums/CropModePersistence.enum'
import ExtensionMode from '../enums/ExtensionMode.enum'
import StretchType from '../enums/StretchType.enum'
import VideoAlignmentType from '../enums/VideoAlignmentType.enum'
export interface KeyboardShortcutInterface {
key?: string,
code?: string,
ctrlKey?: boolean,
metaKey?: boolean,
altKey?: boolean,
shiftKey?: boolean,
onKeyUp?: boolean,
onKeyDown?: boolean,
onMouseMove?: boolean,
}
interface ActionScopeInterface {
show: boolean,
label?: string, // example override, takes precedence over default label
shortcut?: KeyboardShortcutInterface[],
}
2021-10-25 23:11:34 +02:00
interface RestrictionsSettings {
disableOnSmallPlayers?: boolean; // Whether ultrawidify should disable itself when the player is small
minAllowedWidth?: number; // if player is less than this many px wide, ultrawidify will disable itself
minAllowedHeight?: number; // if player is less than this many px tall, ultrawidify will disable itself
onlyAllowInFullscreen?: boolean; // if enabled, ultrawidify will be disabled when not in full screen regardless of what previous two options say
2022-11-22 01:28:04 +01:00
onlyAllowAutodetectionInFullScreen?: boolean; // if enabled, autodetection will only start once in full screen
2021-10-25 23:11:34 +02:00
}
interface ExtensionEnvironmentSettingsInterface {
normal: ExtensionMode,
theater: ExtensionMode,
fullscreen: ExtensionMode,
}
export interface CommandInterface {
action: string,
label: string,
comment?: string,
arguments?: any,
shortcut?: KeyboardShortcutInterface,
internalOnly?: boolean,
actionId?: string,
}
2023-07-15 04:03:32 +02:00
export type SettingsReloadComponent = 'PlayerData' | 'VideoData';
export type SettingsReloadFlags = true | SettingsReloadComponent;
2024-10-19 16:04:20 +02:00
export interface AardSettings {
disabledReason: string, // if automatic aspect ratio has been disabled, show reason
allowedMisaligned: number, // top and bottom letterbox thickness can differ by this much.
// Any more and we don't adjust ar.
allowedArVariance: number, // amount by which old ar can differ from the new (1 = 100%)
timers: { // autodetection frequency
playing: number, // while playing
paused: number, // while paused
error: number, // after error
minimumTimeout: number,
tickrate: number, // 1 tick every this many milliseconds
},
autoDisable: { // settings for automatically disabling the extension
maxExecutionTime: number, // if execution time of main autodetect loop exceeds this many milliseconds,
// we disable it.
consecutiveTimeoutCount: number, // we only do it if it happens this many consecutive times
// FOR FUTURE USE
consecutiveArResets: number // if aspect ratio reverts immediately after AR change is applied, we disable everything
},
canvasDimensions: {
blackframeCanvas: { // smaller than sample canvas, blackframe canvas is used to recon for black frames
// it's not used to detect aspect ratio by itself, so it can be tiny af
width: number,
height: number,
},
sampleCanvas: { // size of image sample for detecting aspect ratio. Bigger size means more accurate results,
// at the expense of performance
width: number,
height: number,
},
},
// NOTE: Black Frame is currently not in use.
blackframe: {
sufficientColorVariance: number, // calculate difference between average intensity and pixel, for every pixel for every color
// component. Average intensity is normalized to where 0 is black and 1 is biggest value for
// that component. If sum of differences between normalized average intensity and normalized
// component varies more than this % between color components, we can afford to use less strict
// cumulative threshold.
cumulativeThresholdLax: number,
cumulativeThresholdStrict: number,// if we add values of all pixels together and get more than this, the frame is bright enough.
// (note: blackframe is 16x9 px -> 144px total. cumulative threshold can be reached fast)
blackPixelsCondition: number, // How much pixels must be black (1 all, 0 none) before we consider frame as black. Takes
// precedence over cumulative threshold: if blackPixelsCondition is met, the frame is dark
// regardless of whether cumulative threshold has been reached.
},
// Used by old aspect ratio detection algorithm. Pls remove.
blackbar: {
blackLevel: number, // everything darker than 10/255 across all RGB components is considered black by
// default. blackLevel can decrease if we detect darker black.
threshold: number, // if pixel is darker than the sum of black level and this value, we count it as black
// on 0-255. Needs to be fairly high (8 might not cut it) due to compression
// artifacts in the video itself
frameThreshold: number, // threshold, but when doing blackframe test
imageThreshold: number, // in order to detect pixel as "not black", the pixel must be brighter than
// the sum of black level, threshold and this value.
gradientThreshold: number, // When trying to determine thickness of the black bars, we take 2 values: position of
// the last pixel that's darker than our threshold, and position of the first pixel that's
// brighter than our image threshold. If positions are more than this many pixels apart,
// we assume we aren't looking at letterbox and thus don't correct the aspect ratio.
gradientSampleSize: number, // How far do we look to find the gradient
maxGradient: number, // if two neighboring pixels in gradientSampleSize differ by more than this, then we aren't
// looking at a gradient
gradientNegativeTreshold: number,
gradientMaxSD: number, // reserved for future use
antiGradientMode: AntiGradientMode
},
// Also not in use, probs.
variableBlackbarThresholdOptions: { // In case of poor bitrate videos, jpeg artifacts may cause us issues
// FOR FUTURE USE
enabled: boolean, // allow increasing blackbar threshold
disableArDetectOnMax: boolean, // disable autodetection when threshold goes over max blackbar threshold
maxBlackbarThreshold: number, // max threshold (don't increase past this)
thresholdStep: number, // when failing to set aspect ratio, increase threshold by this much
increaseAfterConsecutiveResets: number // increase if AR resets this many times in a row
},
blackLevels: {
defaultBlack: number, // By default, pixels darker than this are considered black.
// (If detection algorithm detects darker blacks, black is considered darkest detected pixel)
blackTolerance: number, // If pixel is more than this much brighter than blackLevel, it's considered not black
// It is not considered a valid image detection if gradient detection is enabled
imageDelta: number, // When gradient detection is enabled, pixels this much brighter than black skip gradient detection
}
sampling: {
edgePosition: number; // % of width (max 0.33). Pixels up to this far away from either edge may contain logo.
staticCols: number, // we take a column at [0-n]/n-th parts along the width and sample it
randomCols: number, // we add this many randomly selected columns to the static columns
staticRows: number, // forms grid with staticSampleCols. Determined in the same way. For black frame checks,
},
guardLine: { // all pixels on the guardline need to be black, or else we trigger AR recalculation
// (if AR fails to be recalculated, we reset AR)
enabled: boolean,
ignoreEdgeMargin: number, // we ignore anything that pokes over the black line this close to the edge
// (relative to width of the sample)
imageTestThreshold: number, // when testing for image, this much pixels must be over blackbarThreshold
edgeTolerancePx: number, // black edge violation is performed this far from reported 'last black pixel'
edgeTolerancePercent: null // unused. same as above, except use % of canvas height instead of pixels
},
arSwitchLimiter: { // to be implemented
switches: number, // we can switch this many times
period: number // per this period
},
// pls deprecate and move things used
edgeDetection: {
slopeTestWidth: number,
gradientTestSamples: number, // we check this many pixels below (or above) the suspected edge to check for gradient
gradientTestBlackThreshold: number, // if pixel in test sample is brighter than that, we aren't looking at gradient
gradientTestDeltaThreshold: number, // if delta between two adjacent pixels in gradient test exceeds this, it's not gradient
gradientTestMinDelta: number, // if last pixels of the test sample is less than this brighter than the first -> not gradient
2024-10-21 01:08:03 +02:00
thresholds: {
2024-10-23 01:23:37 +02:00
edgeDetectionLimit: number, // during scanning of the edge, quit after edge gets detected at this many points
minQualitySingleEdge: number, // At least one of the detected must reach this quality
minQualitySecondEdge: number, // The other edge must reach this quality (must be smaller or equal to single edge quality)
2024-10-21 01:08:03 +02:00
}
maxLetterboxOffset: 0.1, // Upper and lower letterbox can be different by this many (% of height)
// Previous iteration variables VVVV
2024-10-19 16:04:20 +02:00
sampleWidth: number, // we take a sample this wide for edge detection
detectionThreshold: number, // sample needs to have this many non-black pixels to be a valid edge
confirmationThreshold: number, //
singleSideConfirmationThreshold: number, // we need this much edges (out of all samples, not just edges) in order
// to confirm an edge in case there's no edges on top or bottom (other
// than logo, of course)
logoThreshold: number, // if edge candidate sits with count greater than this*all_samples, it can't be logo
// or watermark.
edgeTolerancePx?: number, // we check for black edge violation this far from detection point
edgeTolerancePercent?: number, // we check for black edge detection this % of height from detection point. unused
middleIgnoredArea: number, // we ignore this % of canvas height towards edges while detecting aspect ratios
minColsForSearch: number, // if we hit the edge of blackbars for all but this many columns (%-wise), we don't
// continue with search. It's pointless, because black edge is higher/lower than we
// are now. (NOTE: keep this less than 1 in case we implement logo detection)
},
pillarTest: {
ignoreThinPillarsPx: number, // ignore pillars that are less than this many pixels thick.
allowMisaligned: number // left and right edge can vary this much (%)
},
textLineTest: {
nonTextPulse: number, // if a single continuous pulse has this many non-black pixels, we aren't dealing
// with text. This value is relative to canvas width (%)
pulsesToConfirm: number, // this is a threshold to confirm we're seeing text.
pulsesToConfirmIfHalfBlack: number, // this is the threshold to confirm we're seeing text if longest black pulse
// is over 50% of the canvas width
testRowOffset: number // we test this % of height from detected edge
}
}
interface SettingsInterface {
2023-07-15 04:03:32 +02:00
_updateFlags?: {
requireReload?: SettingsReloadFlags,
forSite?: string
}
2024-10-19 16:04:20 +02:00
arDetect: AardSettings,
2021-10-25 23:11:34 +02:00
2024-06-12 20:29:00 +02:00
ui: {
inPlayer: {
enabled: boolean,
minEnabledWidth: number, // don't show UI if player is narrower than % of screen width
activation: 'trigger-zone' | 'player', // what needs to be hovered in order for UI to be visible
triggerZoneDimensions: { // how large the trigger zone is (relative to player size)
width: number
height: number,
offsetX: number, // fed to translateX(offsetX + '%'). Valid range [-100, 0]
offsetY: number // fed to translateY(offsetY + '%'). Valid range [-100, 100]
},
2024-06-12 20:29:00 +02:00
}
}
2021-10-25 23:11:34 +02:00
restrictions?: RestrictionsSettings;
crop: {
default: any;
},
stretch: {
default: any;
conditionalDifferencePercent: number // black bars less than this wide will trigger stretch
// if mode is set to '1'. 1.0=100%
},
2022-09-28 00:39:49 +02:00
kbm: {
enabled: boolean, // if keyboard/mouse handler service will run
keyboardEnabled: boolean, // if keyboard shortcuts are processed
mouseEnabled: boolean, // if mouse movement is processed
}
zoom: {
minLogZoom: number,
maxLogZoom: number,
announceDebounce: number // we wait this long before announcing new zoom
},
2021-10-25 23:11:34 +02:00
miscSettings: {
mousePan: {
enabled: boolean
},
mousePanReverseMouse: boolean,
defaultAr?: any
},
resizer: {
setStyleString: {
maxRetries: number,
retryTimeout: number
}
},
pageInfo: {
timeouts: {
urlCheck: number,
rescan: number
}
},
pan?: any,
version?: string,
preventReload?: boolean,
// -----------------------------------------
// ::: MITIGATIONS :::
// -----------------------------------------
// Settings for browser bug workarounds.
mitigations?: {
zoomLimit?: {
enabled?: boolean,
2021-04-05 03:29:56 +02:00
fullscreenOnly?: boolean,
limit?: number,
}
}
// -----------------------------------------
// ::: ACTIONS :::
// -----------------------------------------
// Nastavitve za ukaze. Zamenja stare nastavitve za bližnične tipke.
2021-10-25 23:11:34 +02:00
//
// Polje 'shortcut' je tabela, če se slučajno lotimo kdaj delati choordov.
actions: {
name?: string, // name displayed in settings
label?: string, // name displayed in ui (can be overridden in scope/playerUi)
cmd?: {
action: string,
arg: any,
customArg?: any,
persistent?: boolean, // optional, false by default. If true, change doesn't take effect immediately.
// Instead, this action saves stuff to settings
}[],
scopes?: {
global?: ActionScopeInterface,
site?: ActionScopeInterface,
page?: ActionScopeInterface
},
playerUi?: {
show: boolean,
path?: string,
},
userAdded?: boolean,
}[],
// This object fulfills the same purpose as 'actions', but is written in less retarded and overly
// complicated way. Hopefully it'll be easier to maintain it that way.
commands?: {
crop?: CommandInterface[],
stretch?: CommandInterface[],
zoom?: CommandInterface[],
pan?: CommandInterface[],
internal?: CommandInterface[],
},
whatsNewChecked: boolean,
newFeatureTracker: any,
// -----------------------------------------
// ::: SITE CONFIGURATION :::
// -----------------------------------------
// Config for a given page:
2021-10-25 23:11:34 +02:00
//
// <hostname> : {
// status: <option> // should extension work on this site?
// arStatus: <option> // should we do autodetection on this site?
2021-10-25 23:11:34 +02:00
//
// defaultAr?: <ratio> // automatically apply this aspect ratio on this side. Use extension defaults if undefined.
// stretch? <stretch mode> // automatically stretch video on this site in this manner
// videoAlignment? <left|center|right>
//
2021-10-25 23:11:34 +02:00
// type: <official|community|user> // 'official' — blessed by Tam.
// // 'community' — blessed by reddit.
// // 'user' — user-defined (not here)
// override: <true|false> // override user settings for this site on update
2021-10-25 23:11:34 +02:00
// }
//
// Valid values for options:
//
// status, arStatus, statusEmbedded:
2021-10-25 23:11:34 +02:00
//
// * enabled — always allow, full
// * basic — allow, but only the basic version without playerData
// * default — allow if default is to allow, block if default is to block
// * disabled — never allow
2021-10-25 23:11:34 +02:00
//
sites: {
[x: string]: SiteSettingsInterface,
}
}
export interface SiteSettingsInterface {
enable: ExtensionEnvironmentSettingsInterface;
enableAard: ExtensionEnvironmentSettingsInterface;
enableKeyboard: ExtensionEnvironmentSettingsInterface;
2023-07-15 01:28:20 +02:00
type?: 'official' | 'community' | 'user-defined' | 'testing' | 'officially-disabled' | 'unknown' | 'modified';
defaultType: 'official' | 'community' | 'user-defined' | 'testing' | 'officially-disabled' | 'unknown' | 'modified';
// must be defined in @global and @empty
persistCSA?: CropModePersistence, // CSA - crop, stretch, alignment
defaults?: { // must be defined in @global and @empty
crop?: {type: AspectRatioType, [x: string]: any},
stretch?: StretchType,
alignment?: {x: VideoAlignmentType, y: VideoAlignmentType},
}
cropModePersistence?: CropModePersistence;
stretchModePersistence?: CropModePersistence;
alignmentPersistence?: CropModePersistence;
activeDOMConfig?: string;
DOMConfig?: { [x: string]: SiteDOMSettingsInterface };
// the following script are for extension caching and shouldn't be saved.
// if they _are_ saved, they will be overwritten
currentDOMConfig?: SiteDOMSettingsInterface;
// the following fields are for use with extension update script
override?: boolean; // whether settings for this site will be overwritten by extension upgrade script
}
export interface SiteDOMSettingsInterface {
2023-07-15 04:03:32 +02:00
type: 'official' | 'community' | 'user-defined' | 'modified' | undefined;
elements?: {
player?: SiteDOMElementSettingsInterface,
video?: SiteDOMElementSettingsInterface,
other?: { [x: number]: SiteDOMElementSettingsInterface }
};
customCss?: string;
periodicallyRefreshPlayerElement?: boolean;
// the following script are for extension caching and shouldn't be saved.
// if they _are_ saved, they will be overwritten
anchorElementIndex?: number;
anchorElement?: HTMLElement;
}
export interface SiteDOMElementSettingsInterface {
manual?: boolean;
querySelectors?: string;
index?: number; // previously: useRelativeAncestor + videoAncestor
2023-07-15 04:03:32 +02:00
mode?: 'index' | 'qs';
nodeCss?: {[x: string]: string};
}
2021-10-25 23:11:34 +02:00
export default SettingsInterface;