commit a5d8d34e24965c631d77d2355a50d687f3292d66 Author: sup39 Date: Tue Jan 30 08:28:27 2024 +0900 feat: add Youtube Share Link Sanitizer diff --git a/README.md b/README.md new file mode 100644 index 0000000..1688923 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# sup-browser-utils +Browser utility scripts by sup39 + +## Scripts +- [Youtube Share Link Sanitizer](./src/youtube-share-link-sanitizer.js): remove tracking parameters from youtube share links diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..4b338c8 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "checkJs": true, + "strict": true, + "target": "es6" + } +} diff --git a/src/youtube-share-link-sanitizer.js b/src/youtube-share-link-sanitizer.js new file mode 100644 index 0000000..2edf80a --- /dev/null +++ b/src/youtube-share-link-sanitizer.js @@ -0,0 +1,85 @@ +// ==UserScript== +// @name Youtube Share Link Sanitizer +// @namespace https://forgejo.sup39.dev/sup39/sup-browser-utils/ +// @version 2024-01-30 +// @description Sanitize Youtube share links +// @author sup39 +// @match https://www.youtube.com/* +// @grant none +// ==/UserScript== + +(() => { + 'use strict'; + /** + * @param {string} selector + * @param {Element} root + */ + // https://stackoverflow.com/a/61511955 + const untilElementLoaded = (selector, root=document.body) => new Promise(resolve => { + // resolve immediately if element already exists + const elm = root.querySelector(selector); + if (elm != null) return resolve(elm); + // observe root until element presents + const observer = new MutationObserver(() => { + const elm = root.querySelector(selector); + if (elm != null) { + observer.disconnect(); + resolve(elm); + } + }); + observer.observe(root, { + childList: true, + subtree: true + }); + }); + /** @param {string} rawUrl */ + function sanitizeUrl(rawUrl) { + const url = new URL(rawUrl); + url.search = ''; + return url.toString(); + } + untilElementLoaded('ytd-popup-container') + .then(root => untilElementLoaded('#share-url', root)) + .then(elm => { + elm.value = sanitizeUrl(elm.value); + // https://stackoverflow.com/a/72223895 + const {get, set} = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value') ?? {}; + if (get && set) { + Object.defineProperty(elm, 'value', { + get, + set: function(rawUrl) { + set.call(this, sanitizeUrl(rawUrl)); + }, + }); + } else { + throw new Error('Unexpected error: Cannot get getter and setter of #share-url.value'); + } + }); +})(); + +/** + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to + */