WIP on player detection config import

This commit is contained in:
Tamius Han 2025-12-27 03:20:28 +01:00
parent 077c7cae15
commit 3f5ab5ecc6
10 changed files with 384 additions and 112 deletions

View File

@ -501,11 +501,10 @@ export interface SiteDOMSettingsInterface {
} }
export interface PlayerDOMSettingsInterface { export interface PlayerDOMSettingsInterface {
playerDetectionMode: PlayerDetectionMode, detectionMode: PlayerDetectionMode,
allowAutoFallback: boolean, allowAutoFallback: boolean,
ancestorIndex?: number, ancestorIndex?: number,
querySelectors?: string, querySelectors?: string,
customCSS?: string,
} }
export interface SiteDOMElementSettingsInterface { export interface SiteDOMElementSettingsInterface {

View File

@ -341,6 +341,20 @@ const ExtensionConfPatch = Object.freeze([
userOptions.sites[site].persistCSA = userOptions.sites[site].persistCSA ?? CropModePersistence.Default; userOptions.sites[site].persistCSA = userOptions.sites[site].persistCSA ?? CropModePersistence.Default;
} }
if (userOptions.sites[site].type as any === 'user-added' || userOptions.sites[site].type === 'user-defined') {
userOptions.sites[site].type = SiteSupportLevel.UserDefined;
}
if (userOptions.sites[site].type as any === 'no-support') {
userOptions.sites[site].type = SiteSupportLevel.Unknown;
}
if (userOptions.sites[site].defaultType as any === 'user-added' || userOptions.sites[site].defaultType === 'user-defined') {
userOptions.sites[site].type = SiteSupportLevel.UserDefined;
}
if (userOptions.sites[site].defaultType as any === 'no-support') {
userOptions.sites[site].defaultType = SiteSupportLevel.Unknown;
}
for (const domConf in siteData.DOMConfig) { for (const domConf in siteData.DOMConfig) {
logger.log('updateFn', "Updating domconf", domConf); logger.log('updateFn', "Updating domconf", domConf);
const oldConf = userOptions.sites[site].DOMConfig[domConf] as any; const oldConf = userOptions.sites[site].DOMConfig[domConf] as any;
@ -351,22 +365,31 @@ const ExtensionConfPatch = Object.freeze([
elements: {} elements: {}
}; };
if (newConf.type === 'user-added' || newConf.type === 'user-defined') {
newConf.type = SiteSupportLevel.UserDefined;
}
if (newConf.type === 'no-support') {
newConf.type = SiteSupportLevel.Unknown;
}
if (oldConf.elements?.player) { if (oldConf.elements?.player) {
newConf.elements['player'] = { newConf.elements['player'] = {
playerDetectionMode: oldConf?.elements?.player?.manual ? ( detectionMode: oldConf?.elements?.player?.manual ? (
oldConf?.elements?.player?.querySelectors.trim() ? PlayerDetectionMode.QuerySelectors : PlayerDetectionMode.AncestorIndex oldConf?.elements?.player?.querySelectors.trim() ? PlayerDetectionMode.QuerySelectors : PlayerDetectionMode.AncestorIndex
) : PlayerDetectionMode.Auto, ) : PlayerDetectionMode.Auto,
allowAutoFallback: true, allowAutoFallback: true,
ancestorIndex: oldConf?.elements?.player?.parentIndex, ancestorIndex: oldConf?.elements?.player?.parentIndex,
querySelectors: oldConf?.elements?.player?.querySelectors, querySelectors: oldConf?.elements?.player?.querySelectors,
customCSS: oldConf?.elements?.player?.customCss,
} }
} else { } else {
newConf.elements['player'] = { newConf.elements['player'] = {
playerDetectionMode: PlayerDetectionMode.Auto, detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true, allowAutoFallback: true,
} }
} }
if (oldConf.customCss) {
newConf.customCss = oldConf.customCss;
}
if (oldConf.elements?.video) { if (oldConf.elements?.video) {
newConf.elements['video'] = { newConf.elements['video'] = {
@ -394,7 +417,7 @@ const ExtensionConfPatch = Object.freeze([
type: SiteSupportLevel.Unknown, type: SiteSupportLevel.Unknown,
elements: { elements: {
player: { player: {
playerDetectionMode: PlayerDetectionMode.Auto, detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true, allowAutoFallback: true,
} }
} }
@ -406,18 +429,12 @@ const ExtensionConfPatch = Object.freeze([
type: SiteSupportLevel.UserDefined, type: SiteSupportLevel.UserDefined,
elements: { elements: {
player: { player: {
playerDetectionMode: PlayerDetectionMode.Auto, detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true, allowAutoFallback: true,
// ancestorIndex: 1,
// querySelectors: '',
// customCSS: ''
}, },
video: { video: {
playerDetectionMode: PlayerDetectionMode.Auto, detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true, allowAutoFallback: true,
// ancestorIndex: 1,
// querySelectors: '',
// customCSS: ''
} }
}, },
} }

View File

@ -829,11 +829,10 @@ const ExtensionConf: SettingsInterface = {
type: SiteSupportLevel.Unknown, type: SiteSupportLevel.Unknown,
elements: { elements: {
player: { player: {
playerDetectionMode: PlayerDetectionMode.Auto, detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true, allowAutoFallback: true,
// ancestorIndex: 1, // we leave those on undefined, // ancestorIndex: 1, // we leave those on undefined,
// querySelectors: '', // querySelectors: '',
// customCSS: ''
} }
} }
} }
@ -863,18 +862,16 @@ const ExtensionConf: SettingsInterface = {
type: SiteSupportLevel.UserDefined, type: SiteSupportLevel.UserDefined,
elements: { elements: {
player: { player: {
playerDetectionMode: PlayerDetectionMode.Auto, detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true, allowAutoFallback: true,
// ancestorIndex: 1, // ancestorIndex: 1,
// querySelectors: '', // querySelectors: '',
// customCSS: ''
}, },
video: { video: {
playerDetectionMode: PlayerDetectionMode.Auto, detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true, allowAutoFallback: true,
// ancestorIndex: 1, // ancestorIndex: 1,
// querySelectors: '', // querySelectors: '',
// customCSS: ''
} }
}, },
} }
@ -899,7 +896,7 @@ const ExtensionConf: SettingsInterface = {
type: SiteSupportLevel.OfficialSupport, type: SiteSupportLevel.OfficialSupport,
elements: { elements: {
player: { player: {
playerDetectionMode: PlayerDetectionMode.QuerySelectors, detectionMode: PlayerDetectionMode.QuerySelectors,
allowAutoFallback: true, allowAutoFallback: true,
querySelectors: "#movie_player, #player, #c4-player", querySelectors: "#movie_player, #player, #c4-player",
} }
@ -927,7 +924,7 @@ const ExtensionConf: SettingsInterface = {
type: SiteSupportLevel.OfficialSupport, type: SiteSupportLevel.OfficialSupport,
elements: { elements: {
player: { player: {
playerDetectionMode: PlayerDetectionMode.QuerySelectors, detectionMode: PlayerDetectionMode.QuerySelectors,
allowAutoFallback: true, allowAutoFallback: true,
querySelectors: "#movie_player, #player, #c4-player", querySelectors: "#movie_player, #player, #c4-player",
} }
@ -950,7 +947,7 @@ const ExtensionConf: SettingsInterface = {
type: SiteSupportLevel.CommunitySupport, type: SiteSupportLevel.CommunitySupport,
elements: { elements: {
player: { player: {
playerDetectionMode: PlayerDetectionMode.Auto, detectionMode: PlayerDetectionMode.Auto,
allowAutoFallback: true, allowAutoFallback: true,
// ancestorIndex: 1, // ancestorIndex: 1,
// querySelectors: '', // querySelectors: '',
@ -975,12 +972,12 @@ const ExtensionConf: SettingsInterface = {
type: SiteSupportLevel.OfficialSupport, type: SiteSupportLevel.OfficialSupport,
elements: { elements: {
player: { player: {
playerDetectionMode: PlayerDetectionMode.QuerySelectors, detectionMode: PlayerDetectionMode.QuerySelectors,
allowAutoFallback: true, allowAutoFallback: true,
querySelectors: ".btm-media-player", querySelectors: ".btm-media-player",
}, },
video: { video: {
playerDetectionMode: PlayerDetectionMode.QuerySelectors, detectionMode: PlayerDetectionMode.QuerySelectors,
allowAutoFallback: true, allowAutoFallback: true,
querySelectors: ".btm-media-client-element" querySelectors: ".btm-media-client-element"
} }
@ -1005,7 +1002,7 @@ const ExtensionConf: SettingsInterface = {
type: SiteSupportLevel.OfficialSupport, type: SiteSupportLevel.OfficialSupport,
elements: { elements: {
player: { player: {
playerDetectionMode: PlayerDetectionMode.QuerySelectors, detectionMode: PlayerDetectionMode.QuerySelectors,
allowAutoFallback: true, allowAutoFallback: true,
querySelectors: "#movie_player, #player, #c4-player", querySelectors: "#movie_player, #player, #c4-player",
} }
@ -1058,7 +1055,7 @@ const ExtensionConf: SettingsInterface = {
type: SiteSupportLevel.CommunitySupport, type: SiteSupportLevel.CommunitySupport,
elements: { elements: {
player: { player: {
playerDetectionMode: PlayerDetectionMode.QuerySelectors, detectionMode: PlayerDetectionMode.QuerySelectors,
allowAutoFallback: true, allowAutoFallback: true,
querySelectors: "#jwplayer-container" querySelectors: "#jwplayer-container"
} }

View File

@ -4,7 +4,7 @@
class="popup-overlay" class="popup-overlay"
:class="{'dim': dimOverlay}" :class="{'dim': dimOverlay}"
> >
<div class="popup-content p-4 border border-stone-700 max-h-[90dvh] max-w-[90dvw]" > <div class="popup-content p-4 border border-stone-700 bg-stone-950 max-h-[90dvh] max-w-[90dvw]" >
<div class="h-full flex flex-col w-full"> <div class="h-full flex flex-col w-full">
<div v-if="title" class="header grow-0 shrink-0" :class="type"> <div v-if="title" class="header grow-0 shrink-0" :class="type">
<h3 class="mb-4 mt-0">{{title}}</h3> <h3 class="mb-4 mt-0">{{title}}</h3>
@ -15,7 +15,7 @@
<div v-else class="h-full w-full -mr-4 pr-4 grow shrink overflow-y-auto overflow-x-hidden"> <div v-else class="h-full w-full -mr-4 pr-4 grow shrink overflow-y-auto overflow-x-hidden">
<slot></slot> <slot></slot>
</div> </div>
<div class="grow-0 shrink-0 flex row gap-2 justify-end w-full"> <div v-if="!clientSideButtons" class="grow-0 shrink-0 flex row gap-2 justify-end w-full">
<button <button
class="primary" class="primary"
v-if="confirmButtonText" v-if="confirmButtonText"
@ -63,6 +63,10 @@ export default {
type: String, type: String,
default: 'info' default: 'info'
}, },
clientSideButtons: {
type: Boolean,
default: false,
},
confirmButtonText: { confirmButtonText: {
type: String, type: String,
required: false, required: false,
@ -94,6 +98,7 @@ export default {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
.header { .header {
font-size: 1.33rem; font-size: 1.33rem;

View File

@ -1,7 +1,7 @@
<template> <template>
<div <div v-if="computedSiteSupportLevel === SiteSupportLevel.OfficialSupport"
v-if="computedSiteSupportLevel === 'official'"
class="site-support site-support-level official" class="site-support site-support-level official"
:class="{'no-tooltip': disableTooltip}"
:style="supportLevelStyle" :style="supportLevelStyle"
> >
<mdicon name="check-decagram" /> <mdicon name="check-decagram" />
@ -11,7 +11,11 @@
The extension is being tested and should work on this site. The extension is being tested and should work on this site.
</div> </div>
</div> </div>
<div v-if="computedSiteSupportLevel === 'community'" class="site-support site-support-level community" :style="supportLevelStyle"> <div v-if="computedSiteSupportLevel === SiteSupportLevel.CommunitySupport"
class="site-support site-support-level community"
:class="{'no-tooltip': disableTooltip}"
:style="supportLevelStyle"
>
<mdicon name="account-group" /> <mdicon name="account-group" />
<div v-if="!small">Community</div> <div v-if="!small">Community</div>
<div class="tooltip" :style="tooltipStyle"> <div class="tooltip" :style="tooltipStyle">
@ -21,7 +25,11 @@
is geoblocked. is geoblocked.
</div> </div>
</div> </div>
<div v-if="computedSiteSupportLevel === 'no-support' || computedSiteSupportLevel === 'unknown'" class="site-support site-support-level no-support" :style="supportLevelStyle"> <div v-if="computedSiteSupportLevel === SiteSupportLevel.Unknown"
class="site-support site-support-level no-support"
:class="{'no-tooltip': disableTooltip}"
:style="supportLevelStyle"
>
<mdicon name="help-circle-outline" /> <mdicon name="help-circle-outline" />
<div v-if="!small">Untested</div> <div v-if="!small">Untested</div>
<div class="tooltip" :style="tooltipStyle"> <div class="tooltip" :style="tooltipStyle">
@ -31,7 +39,11 @@
(unaware, not using the site, language barrier, geoblocking, paid services Tam doesn't use). (unaware, not using the site, language barrier, geoblocking, paid services Tam doesn't use).
</div> </div>
</div> </div>
<div v-if="computedSiteSupportLevel === 'user-added' || computedSiteSupportLevel === 'user-defined'" class="site-support site-support-level user-added" :style="supportLevelStyle"> <div v-if="computedSiteSupportLevel === SiteSupportLevel.UserDefined || computedSiteSupportLevel === SiteSupportLevel.UserModified"
class="site-support site-support-level user-added"
:class="{'no-tooltip': disableTooltip}"
:style="supportLevelStyle"
>
<mdicon name="account" /> <mdicon name="account" />
<div v-if="!small">Modified by you</div> <div v-if="!small">Modified by you</div>
<div class="tooltip" :style="tooltipStyle"> <div class="tooltip" :style="tooltipStyle">
@ -39,7 +51,14 @@
You are on your own. You have manually changed settings for this site. The extension is doing what you told it to do. You are on your own. You have manually changed settings for this site. The extension is doing what you told it to do.
</div> </div>
</div> </div>
<div v-if="computedSiteSupportLevel === 'officially-disabled'" class="site-support site-support-level officially-disabled" :style="supportLevelStyle"> <div v-if="computedSiteSupportLevel === SiteSupportLevel.BetaSupport">
</div>
<div v-if="computedSiteSupportLevel === 'officially-disabled'"
class="site-support site-support-level officially-disabled"
:class="{'no-tooltip': disableTooltip}"
:style="supportLevelStyle"
>
<mdicon name="close-circle" /> <mdicon name="close-circle" />
<div v-if="!small">Blacklisted</div> <div v-if="!small">Blacklisted</div>
<div class="tooltip" :style="tooltipStyle"> <div class="tooltip" :style="tooltipStyle">
@ -47,22 +66,30 @@
Extension is known to not work with this site. Extension is known to not work with this site.
</div> </div>
</div> </div>
<!-- </div> -->
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue' import { defineComponent } from 'vue'
import { SiteSupportLevel } from '@src/common/enums/SiteSupportLevel.enum';
export default defineComponent({ export default defineComponent({
data() {
return {
SiteSupportLevel
};
},
props: { props: {
siteSupportLevel: String, siteSupportLevel: String,
siteSettings: Object, siteSettings: Object,
small: Boolean, small: Boolean,
disableTooltip: Boolean,
supportLevelStyle: String, supportLevelStyle: String,
tooltipStyle: String, tooltipStyle: String,
}, },
computed: { computed: {
computedSiteSupportLevel() { computedSiteSupportLevel() {
return this.siteSupportLevel ?? (this.siteSettings ? this.siteSettings.data.type ?? 'no-support' : 'waiting'); return this.siteSupportLevel ?? (this.siteSettings ? this.siteSettings.data.type ?? SiteSupportLevel.Unknown : 'waiting');
} }
} }
@ -75,6 +102,7 @@ export default defineComponent({
.site-support { .site-support {
@apply inline-flex flex-row items-center gap-1 rounded-[0.5rem] relative; @apply inline-flex flex-row items-center gap-1 rounded-[0.5rem] relative;
.tooltip { .tooltip {
padding: 1rem; padding: 1rem;
display: none; display: none;
@ -93,6 +121,13 @@ export default defineComponent({
word-break: break-word; word-break: break-word;
} }
&:hover { &:hover {
&.no-tooltip {
.tooltip {
display: none !important;
}
}
.tooltip { .tooltip {
display: block; display: block;
} }

View File

@ -437,7 +437,7 @@ export default defineComponent({
this.editSnapshot(snapshot); this.editSnapshot(snapshot);
}, },
handleImportedSnapshotError(error: any) { handleImportedSnapshotError(error: any) {
console.error(`[ultrawidify] Failed to upload snapshot. Error:`, e); console.error(`[ultrawidify] Failed to upload snapshot. Error:`, error);
} }
} }
//#endregion //#endregion

View File

@ -330,6 +330,7 @@
title="Player detection options" title="Player detection options"
confirmButtonText="Save" confirmButtonText="Save"
cancelButtonText="Cancel" cancelButtonText="Cancel"
@onCancel="playerDetectionOptionsDialog.visible = false"
> >
<PlayerSelectorAdvancedForm <PlayerSelectorAdvancedForm
:settings="settings" :settings="settings"

View File

@ -2,15 +2,35 @@
<!-- ADVANCED OPTIONS --> <!-- ADVANCED OPTIONS -->
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex flex-row gap-2 justify-end"> <div class="flex flex-row gap-2 justify-end">
<button>Load existing config</button> <button @click="openSelectSnapshotDialog()">Load existing config</button>
<button>Import from file</button> <UploadJsonFileButton
@importedJson="handleImportedSettings"
@error="handleSettingsImportError"
>
Import settings
</UploadJsonFileButton>
<button>Export config</button>
</div> </div>
<template v-if="DOMConfigData" > <div v-if="loaded" class="w-[690px] max-w-full">
<div v-if="DOMConfigName" class="field">
<div class="label">Current preset name:</div>
<div class="flex flex-row gap-2 items-center">
{{DOMConfigName}}
<SupportLevelIndicator
:siteSupportLevel="DOMConfigData.type"
:disableTooltip="true"
></SupportLevelIndicator>
</div>
</div>
<!-- #region player element detection -->
<div class="field"> <div class="field">
<div class="label">Player detection:</div> <div class="label">Player detection:</div>
<div class="select"> <div class="select">
<select v-model="DOMConfigData.elements.player.playerDetectionMode"> <select v-model="DOMConfigData.elements.player.detectionMode">
<option :value="PlayerDetectionMode.Auto">Automatic</option> <option :value="PlayerDetectionMode.Auto">Automatic</option>
<option :value="PlayerDetectionMode.AncestorIndex">Fixed player container offset</option> <option :value="PlayerDetectionMode.AncestorIndex">Fixed player container offset</option>
<option :value="PlayerDetectionMode.QuerySelectors">Query selectors</option> <option :value="PlayerDetectionMode.QuerySelectors">Query selectors</option>
@ -18,48 +38,107 @@
</div> </div>
</div> </div>
<!-- Player container offset ( ancestor index field -->
<template v-if="DOMConfigData.elements.player.detectionMode === PlayerDetectionMode.AncestorIndex">
<div class="field"> <div class="field">
<div class="label">Use automatic detection for fallback</div> <div class="label">Player container offset:</div>
<div class="checkbox">
<input type="checkbox" v-model="DOMConfigData.elements.player.allowAutoFallback" />
</div>
</div>
<div class="field">
<div class="label">Player container offset</div>
<div class="input"> <div class="input">
<input v-model="DOMConfigData.elements.player.ancestorIndex" /> <input v-model="DOMConfigData.elements.player.ancestorIndex" />
</div> </div>
</div>
<div class="hint"> <div class="hint">
Defines how far away from video the player container element is. If you don't know what you're doing, start Defines how far away from video the player container element is. If you don't know what you're doing, start
with 1, save settings, and reload the page. If this doesn't fix your problem, increase the number by 1 and with 1, save settings, and reload the page. If this doesn't fix your problem, increase the number by 1 and
repeat the procedure. If no value between 1-16 works, the site may require additional CSS in order to work. repeat the procedure. If no value between 1-16 works, the site may require additional CSS in order to work.
</div> </div>
</div> </template>
<!-- Query selectors field -->
<template v-if="DOMConfigData.elements.player.detectionMode === PlayerDetectionMode.QuerySelectors">
<div class="field"> <div class="field">
<div class="label">Query selector</div> <div class="label">Query selectors:</div>
<div class="input"> <div class="input">
<input v-model="DOMConfigData.elements.player.querySelectors" /> <input v-model="DOMConfigData.elements.player.querySelectors" />
</div> </div>
</div>
<div class="hint"> <div class="hint">
You should be at least somewhat proficient with CSS in order to use this option. If you need to provide more You should be at least somewhat proficient with CSS in order to use this option. If you need to provide more
than one query selector, use commas to separate them. than one query selector, use commas to separate them.
</div> </div>
</div> </template>
<!-- Use auto-selection for fallback -->
<template v-if="DOMConfigData.elements.player.detectionMode !== PlayerDetectionMode.Auto">
<div class="field"> <div class="field">
<div class="label">Additional CSS for the page</div> <div class="label">Use automatic detection for fallback</div>
<div class="input"> <div class="checkbox">
<textarea v-model="DOMConfigData.customCSS" /> <input type="checkbox" v-model="DOMConfigData.elements.player.allowAutoFallback" />
</div>
</div> </div>
<div class="hint"> <div class="hint">
You should be at least somewhat proficient with CSS in order to use this option. If no valid player is found by following these settings, use automatic player detection as a fallback.
You should probably keep this option on.
</div> </div>
</div>
</template> </template>
<!-- #endregion -->
<!-- #region video element detection -->
<div class="field">
<div class="label">Video detection:</div>
<div class="select">
<select v-model="DOMConfigData.elements.video.detectionMode">
<option :value="PlayerDetectionMode.Auto">Automatic</option>
<option :value="PlayerDetectionMode.QuerySelectors">Query selectors</option>
</select>
</div>
</div>
<!-- Player container offset / ancestor index field, not valid in video element selection -->
<template v-if="DOMConfigData.elements.video.detectionMode === PlayerDetectionMode.AncestorIndex">
<div class="field">
<div class="label">Yo, that 'video detection' mode is straight up illegal. Change it to something else.</div>
</div>
</template>
<!-- Query selectors field -->
<template v-if="DOMConfigData.elements.video.detectionMode === PlayerDetectionMode.QuerySelectors">
<div class="field">
<div class="label">Query selectors:</div>
<div class="input">
<input v-model="DOMConfigData.elements.video.querySelectors" />
</div>
</div>
<div class="hint">
You should be at least somewhat proficient with CSS in order to use this option. If you need to provide more
than one query selector, use commas to separate them.
</div>
</template>
<!-- Use auto-selection for fallback -->
<template v-if="DOMConfigData.elements.video.detectionMode !== PlayerDetectionMode.Auto">
<div class="field">
<div class="label">Use automatic detection for fallback</div>
<div class="checkbox">
<input type="checkbox" v-model="DOMConfigData.elements.video.allowAutoFallback" />
</div>
</div>
<div class="hint">
If no valid video is found by following these settings, use automatic player detection as a fallback.
You should probably keep this option on.
</div>
</template>
<div class="field">
<div class="label">Additional CSS for the page:</div>
<div class="input">
<textarea v-model="DOMConfigData.customCss"/>
</div>
</div>
<div class="hint">
CSS that will be injected into the page. You should be at least somewhat proficient with CSS in order to use this option.
</div>
</div>
<template v-else> <template v-else>
No DOM config for this site. No DOM config for this site.
</template> </template>
@ -67,28 +146,91 @@
<div class="w-full flex flex-row gap-2 justify-end"> <div class="w-full flex flex-row gap-2 justify-end">
<button>Save as</button> <button>Save as</button>
<button>Save</button> <button :disabled="DOMConfigName?.startsWith('@')">Save</button>
<button>Close</button> <button>Close</button>
</div> </div>
<!-- PRESET SELECT DIALOG -->
<Popup <Popup
v-if="selectSnapshotDialog.visible" v-if="selectSnapshotDialog.visible"
title="Select configuration"
@onClose="selectSnapshotDialog.visible = false"
> >
<div>Select configuration:</div>
<div class="flex flex-col gap-2 py-4">
<div
v-for="(value, key) of siteSettings.data.DOMConfig" :key="key"
class="
px-4 py-2 flex flex-row gap-2 w-full items-center
border border-stone-800 hover:bg-stone-800
"
:class="{'!border-primary-300 text-primary-300': key === siteSettings.data.activeDOMConfig}"
@click="selectConfig(key)"
>
<div>{{key}}</div>
<SupportLevelIndicator :siteSupportLevel="value.type" :disableTooltip="true"></SupportLevelIndicator>
<div class="grow"></div>
<div v-if="key === siteSettings.data.activeDOMConfig" class="">
<mdicon name="check-bold" :size="20"></mdicon>
</div>
</div>
</div>
</Popup> </Popup>
<!-- FINISH IMPORT DIALOG -->
<Popup
v-if="finishImportDialog.visible"
:title="finishImportDialog.data.invalidJson ? 'Import failed' : 'Select configuration'"
@onClose="finishImportDialog.visible = false"
:clientSideButtons="true"
>
<template v-if="finishImportDialog.data.invalidJson">
<div>The selected file appears to be corrupted, as it contains no valid configurations.</div>
<div class="flex flex-row justify-end mt-4"><button @click="finishImportDialog.visible = false">OK</button></div>
</template>
<template v-else>
<div>The following configurations were imported. Resolve any conflicts and select the config you wish to use.</div>
<div class="flex flex-col gap-2 py-4">
<div
v-for="(value, key) of finishImportDialog.data.DOMConfig" :key="key"
class="
px-4 py-2 flex flex-row gap-2 w-full items-center
border border-stone-800 hover:bg-stone-800
"
:class="{'!border-primary-300 text-primary-300': key === finishImportDialog.data.activeDOMConfig}"
>
<div>{{key}}</div>
<SupportLevelIndicator :siteSupportLevel="value.type" :disableTooltip="true"></SupportLevelIndicator>
<div class="grow"></div>
<div v-if="key === finishImportDialog.data.activeDOMConfig" class="">
<mdicon name="check-bold" :size="20"></mdicon>
</div>
</div>
</div>
</template>
</Popup>
<!-- EXPORT CONFIG DIALOG -->
<!-- SAVE AS DIALOG -->
</template> </template>
<script lang="ts"> <script lang="ts">
import { SiteSettings } from '@src/ext/lib/settings/SiteSettings'; import { SiteSettings } from '@src/ext/lib/settings/SiteSettings';
import { PlayerDetectionMode } from '@src/common/enums/PlayerDetectionMode.enum'; import { PlayerDetectionMode } from '@src/common/enums/PlayerDetectionMode.enum';
import UploadJsonFileButton from '@components/common/UploadJsonFileButton.vue';
import SupportLevelIndicator from '@components/common/SupportLevelIndicator.vue';
import Popup from '@components/common/Popup.vue'; import Popup from '@components/common/Popup.vue';
import { _cp } from '../../../../../common/js/utils'; import { _cp } from '@src/common/js/utils';
import { SiteDOMSettingsInterface } from '@src/common/interfaces/SettingsInterface';
export default({ export default({
components: { components: {
PlayerDetectionMode, PlayerDetectionMode,
UploadJsonFileButton,
SupportLevelIndicator,
Popup, Popup,
}, },
props: [ props: [
@ -98,8 +240,11 @@ export default({
data() { data() {
return { return {
PlayerDetectionMode, PlayerDetectionMode,
loaded: false,
DOMConfigData: undefined as any, DOMConfigData: undefined as any,
DOMConfigName: undefined as any,
selectSnapshotDialog: {visible: false}, selectSnapshotDialog: {visible: false},
finishImportDialog: {visible: false}
} }
}, },
watch: { watch: {
@ -113,14 +258,69 @@ export default({
} }
}, },
methods: { methods: {
loadSiteSettings(siteSettings: SiteSettings) { loadSiteSettings(siteSettings: SiteSettings, configKey = siteSettings.data.activeDOMConfig) {
let DOMConfigData;
if (siteSettings.data.DOMConfig) { if (siteSettings.data.DOMConfig) {
this.DOMConfigData = siteSettings.data.DOMConfig[siteSettings.data.activeDOMConfig]; DOMConfigData = siteSettings.data.DOMConfig[configKey];
this.DOMConfigName = configKey;
if (!DOMConfigData.elements) {
DOMConfigData.elements = _cp(siteSettings.blankSettings.DOMConfig.empty);
}
if (!DOMConfigData.elements.player) {
DOMConfigData.elements.player = _cp(siteSettings.blankSettings.DOMConfig.empty.elements.player);
}
if (!DOMConfigData.elements.video) {
DOMConfigData.elements.video = _cp(siteSettings.blankSettings.DOMConfig.empty.elements.video);
}
} else { } else {
this.DOMConfigData = _cp(siteSettings.blankSettings.DOMConfig.blank) DOMConfigData = _cp(siteSettings.blankSettings.DOMConfig.empty)
}
this.DOMConfigData = DOMConfigData;
this.loaded = true;
},
openSelectSnapshotDialog() {
this.selectSnapshotDialog = {
visible: true
}
},
selectConfig(key: string) {
this.loadSiteSettings(this.siteSettings, key);
this.siteSettings.set('activeDOMConfig', key);
this.selectSnapshotDialog = false;
},
handleImportedSettings(json: {activeDOMConfig?: string, DOMConfig: { [x: string]: SiteDOMSettingsInterface & {overwrite?: boolean}}} | SiteDOMSettingsInterface) {
let afterImportDialogData;
if ((json as any).DOMConfig) {
afterImportDialogData = json;
} else if ((json as SiteDOMSettingsInterface).type) {
const key = `imported-${new Date().toISOString()}`;
afterImportDialogData = {
activeDOMConfig: key,
DOMConfig: {
[key]: json as SiteDOMSettingsInterface
}
}
} else {
afterImportDialogData = {
invalidJson: true
} }
} }
this.finishImportDialog = {
data: afterImportDialogData,
visible: true
};
},
handleSettingsImportError(error) {
console.error(`[ultrawidify] Failed to upload snapshot. Error:`, error);
},
} }
}); });
</script> </script>

View File

@ -14,5 +14,9 @@
&:hover { &:hover {
@apply border-primary-500 text-primary-300; @apply border-primary-500 text-primary-300;
} }
&:disabled {
@apply pointer-events-none text-stone-500 border-stone-700;
}
} }
} }

View File

@ -4,6 +4,10 @@
.input, .range-input { .input, .range-input {
@apply py-1 px-2 bg-black/25 border-b border-white/50 w-full relative; @apply py-1 px-2 bg-black/25 border-b border-white/50 w-full relative;
&.multiline {
@apply border border-stone-500;
}
&:active, &:focus, &:focus-within { &:active, &:focus, &:focus-within {
@apply border-b-1 border-b-primary-400; @apply border-b-1 border-b-primary-400;
} }
@ -21,6 +25,14 @@
color: #fff; color: #fff;
} }
textarea {
@apply text-sm font-mono border border-stone-800 -my-1 -mb-2 -mx-2 px-2 py-1 pb-2;
width: 100%;
outline: none;
background-color: transparent;
color: #fff;
}
.unit { .unit {
position: absolute; position: absolute;
right: 0px; right: 0px;
@ -115,16 +127,18 @@
font-size: 5rem;; font-size: 5rem;;
} }
&:hover { &:hover {
@apply text-black bg-primary-400; @apply text-black bg-primary-300;
} }
} }
} }
} }
} }
.hint { .hint {
@apply text-primary-200/70;
font-size: 0.8rem; font-size: 0.8rem;
opacity: 0.7; opacity: 0.7;
margin-top: 0.25rem; margin-top: -0.33rem;
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
margin-left: 5rem; margin-left: 5rem;
box-sizing:border-box; box-sizing:border-box;