Skip to content

Commit

Permalink
Merge branch 'webosbrew:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
fire332 authored Mar 30, 2024
2 parents 8d003dd + 6c889ad commit 6a21895
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]

### Added

- [#142](https://github.com/webosbrew/youtube-webos/pull/141): Blocked some additional ads (@throwaway96)
- [#144](https://github.com/webosbrew/youtube-webos/pull/144): Added support for config change listeners (@throwaway96)

### Fixed

- [#103](https://github.com/webosbrew/youtube-webos/pull/103): Fixed SponsorBlock on videos with chapters (@alyyousuf7)
- [#131](https://github.com/webosbrew/youtube-webos/pull/131): Fixed minor README issue (@ANewDawn)
- [#141](https://github.com/webosbrew/youtube-webos/pull/141): Fixed black background behind video menu (@throwaway96; thanks to @reisxd)
- [#143](https://github.com/webosbrew/youtube-webos/pull/143): Fixed duplicate click bug (@throwaway96)

### Changed

- [#128](https://github.com/webosbrew/youtube-webos/pull/128): Updated workflows and dependencies (@throwaway96)
- [#133](https://github.com/webosbrew/youtube-webos/pull/133): Changed various dev stuff (@throwaway96)
- [#134](https://github.com/webosbrew/youtube-webos/pull/134): Refactored config/UI code (@throwaway96)
- [#138](https://github.com/webosbrew/youtube-webos/pull/138): Changed webpack to production mode by default (@throwaway96)
- [#145](https://github.com/webosbrew/youtube-webos/pull/145): Made observing attributes optional in waitForChildAdd() (@throwaway96)

## [0.3.2] - 2024/03/07

Expand Down
46 changes: 43 additions & 3 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const CONFIG_KEY = 'ytaf-configuration';

export const configOptions = new Map([
const configOptions = new Map([
['enableAdBlock', { default: true, desc: 'Enable ad blocking' }],
['enableSponsorBlock', { default: true, desc: 'Enable SponsorBlock' }],
[
Expand Down Expand Up @@ -40,6 +40,15 @@ const defaultConfig = (() => {
return ret;
})();

/** @type {Record<string, DocumentFragment>} as const */
const configFrags = (() => {
let ret = {};
for (const k of configOptions.keys()) {
ret[k] = new DocumentFragment();
}
return ret;
})();

function loadStoredConfig() {
const storage = window.localStorage.getItem(CONFIG_KEY);

Expand All @@ -63,7 +72,7 @@ function configExists(key) {
return configOptions.has(key);
}

export function getConfigDesc(key) {
export function configGetDesc(key) {
if (!configExists(key)) {
throw new Error('tried to get desc for unknown config key: ' + key);
}
Expand Down Expand Up @@ -95,7 +104,38 @@ export function configWrite(key, value) {
throw new Error('tried to write unknown config key: ' + key);
}

console.info('Setting key', key, 'to', value);
const oldValue =
localConfig[key] !== undefined ? localConfig[key] : defaultConfig[key];

console.info('Changing key', key, 'from', oldValue, 'to', value);
localConfig[key] = value;
window.localStorage[CONFIG_KEY] = JSON.stringify(localConfig);

configFrags[key].dispatchEvent(
new CustomEvent('ytafConfigChange', {
detail: { key, newValue: value, oldValue }
})
);
}

/**
* Add a listener for changes in the value of a specified config option
* @param {string} key Config option to monitor
* @param {(evt: Event) => void} callback Function to be called on change
*/
export function configAddChangeListener(key, callback) {
const frag = configFrags[key];

frag.addEventListener('ytafConfigChange', callback);
}

/**
* Remove a listener for changes in the value of a specified config option
* @param {string} key Config option to monitor
* @param {(evt: Event) => void} callback Function to be called on change
*/
export function configRemoveChangeListener(key, callback) {
const frag = configFrags[key];

frag.removeEventListener('ytafConfigChange', callback);
}
22 changes: 18 additions & 4 deletions src/ui.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*global navigate*/
import './spatial-navigation-polyfill.js';
import './ui.css';
import { configRead, configWrite, getConfigDesc } from './config.js';
import { configRead, configWrite, configGetDesc } from './config.js';

// We handle key events ourselves.
window.__spatialNavigation__.keyMode = 'NONE';
Expand Down Expand Up @@ -41,14 +41,22 @@ function createConfigCheckbox(key) {
const elmInput = document.createElement('input');
elmInput.type = 'checkbox';
elmInput.checked = configRead(key);
elmInput.addEventListener('change', (evt) => {

/** @type {(evt: Event) => void} */
const changeHandler = (evt) => {
configWrite(key, evt.target.checked);
};

elmInput.addEventListener('change', changeHandler);

configAddChangeListener(key, (evt) => {
elmInput.checked = evt.detail.newValue;
});

const elmLabel = document.createElement('label');
elmLabel.appendChild(elmInput);
// Use non-breaking space (U+00A0)
elmLabel.appendChild(document.createTextNode('\u00A0' + getConfigDesc(key)));
elmLabel.appendChild(document.createTextNode('\u00A0' + configGetDesc(key)));

return elmLabel;
}
Expand Down Expand Up @@ -84,7 +92,13 @@ function createOptionsPanel() {
navigate(ARROW_KEY_CODE[evt.keyCode]);
} else if (evt.keyCode === 13) {
// "OK" button
document.querySelector(':focus').click();

// The YouTube app generates these "OK" events from clicks (including
// with the Magic Remote), and we don't want to send a duplicate click
// event for those. It seems isTrusted is only true for "real" events.
if (evt.isTrusted === true) {
document.activeElement.click();
}
} else if (evt.keyCode === 27) {
// Back button
showOptionsPanel(false);
Expand Down
3 changes: 2 additions & 1 deletion src/userScript.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import './ui.js';
/** @type {HTMLVideoElement} */
const video = await waitForChildAdd(
document.body,
(node) => node instanceof HTMLVideoElement
(node) => node instanceof HTMLVideoElement,
false
);

const playerCtrlObs = new MutationObserver(() => {
Expand Down
30 changes: 22 additions & 8 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,24 @@ export function handleLaunch(params) {
}

/**
* Wait for a child element to be added that holds true for a predicate
* @template T
* @param {Element} parent
* @param {(node: Node) => node is T} predicate
* @param {AbortSignal=} abortSignal
* @return {Promise<T>}
* Wait for a child element to be added for which a predicate is true.
*
* When `observeAttributes` is false, the predicate is checked only when a node
* is first added. If you want the predicate to run every time an attribute is
* modified, set `observeAttributes` to true.
* @template {Node} T
* @param {Element} parent Root of tree to watch
* @param {(node: Node) => node is T} predicate Function that checks whether its argument is the desired element
* @param {boolean} observeAttributes Also run predicate on attribute changes
* @param {AbortSignal=} abortSignal Signal that can be used to stop waiting
* @return {Promise<T>} Matched element
*/
export async function waitForChildAdd(parent, predicate, abortSignal) {
export async function waitForChildAdd(
parent,
predicate,
observeAttributes,
abortSignal
) {
return new Promise((resolve, reject) => {
const obs = new MutationObserver((mutations) => {
for (const mut of mutations) {
Expand Down Expand Up @@ -128,6 +138,10 @@ export async function waitForChildAdd(parent, predicate, abortSignal) {
});
}

obs.observe(parent, { subtree: true, attributes: true, childList: true });
obs.observe(parent, {
subtree: true,
attributes: observeAttributes,
childList: true
});
});
}

0 comments on commit 6a21895

Please sign in to comment.