您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Saves and restores scroll position on YouTube channel videos pages
当前为
- // ==UserScript==
- // @name YouTube Channel Scroll Saver
- // @name:de YouTube Channel Scroll Saver
- // @namespace https://www.youtube.com/
- // @version 1.6.1
- // @description Saves and restores scroll position on YouTube channel videos pages
- // @description:de Speichert und stellt die Scrollposition auf der Video-Seite des YouTube-Kanals wieder her
- // @author Kamikaze (https://github.com/Kamiikaze)
- // @supportURL https://github.com/Kamiikaze/Tampermonkey/issues
- // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
- // @match https://www.youtube.com/@*/videos
- // @match https://www.youtube.com/*
- // @require https://greasyfork.org/scripts/455253-kamikaze-script-utils/code/Kamikaze'%20Script%20Utils.js
- // @require https://cdnjs.cloudflare.com/ajax/libs/toastify-js/1.12.0/toastify.min.js
- // @resource toastifyCss https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css
- // @grant GM_xmlhttpRequest
- // @grant GM_getResourceText
- // @grant GM_addStyle
- // @license MIT
- // ==/UserScript==
- // Autoscroll to last postion on this channel.
- // Leave it false if you wan to click a button to manual scroll
- const doAutoscroll = false
- // Save scroll position at most every 3 second
- const saveDelay = 3000
- /* global Logger waitForElm notify */
- const SCRIPT_NAME = "YT Scroll Saver"
- const log = new Logger(SCRIPT_NAME, 4);
- // Load remote JS
- GM_xmlhttpRequest({
- method : "GET",
- // from other domain than the @match one (.org / .com):
- url : "https://cdnjs.cloudflare.com/ajax/libs/toastify-js/1.12.0/toastify.min.js",
- onload : (ev) =>
- {
- let e = document.createElement('script');
- e.innerText = ev.responseText;
- document.head.appendChild(e);
- }
- });
- // Load remote CSS
- const extCss = GM_getResourceText("toastifyCss");
- GM_addStyle(extCss);
- (function() {
- // https://stackoverflow.com/questions/61964265/getting-error-this-document-requires-trustedhtml-assignment-in-chrome
- if (window.trustedTypes && window.trustedTypes.createPolicy && !window.trustedTypes.defaultPolicy) {
- window.trustedTypes.createPolicy('default', {
- createHTML: string => string
- // Optional, only needed for script (url) tags
- //,createScriptURL: string => string
- //,createScript: string => string,
- });
- }
- let isScrolling = false;
- let btnAdded = false;
- let saveTimeout = null;
- let lastUrl = location.href;
- function getChannelUsername() {
- const match = window.location.pathname.match(/@([^/]+)/);
- return match ? match[1] : null;
- }
- function saveScrollPosition() {
- const username = getChannelUsername();
- if (!username) return;
- const scrollPosition = window.scrollY;
- if ( scrollPosition > 800 ) {
- localStorage.setItem(`yt_scroll_${username}`, scrollPosition);
- notify(`[YT Scroll Saver] Saved position: ${scrollPosition}px for @${username}`, 3000)
- log.debug(`Saved position: ${scrollPosition}px for @${username}`);
- } else {
- log.debug(`Scroll pos is below 800 (${scrollPosition}). Don't save pos.`);
- return
- }
- }
- function loadScrollPosition() {
- const username = getChannelUsername();
- if (!username) {
- isScrolling = false
- return
- };
- const savedPosition = parseInt(localStorage.getItem(`yt_scroll_${username}`) || "0", 10);
- if (savedPosition <= 0) {
- isScrolling = false
- return
- };
- notify(`[YT Scroll Saver] Trying to restore position: ${savedPosition}px for @${username}`)
- log.debug(`[YT Scroll Saver] Trying to restore position: ${savedPosition}px for @${username}`);
- if (!btnAdded) createManualScrollBtn(savedPosition)
- if (doAutoscroll) scrollTo(savedPosition)
- }
- function scrollTo(pos) {
- let attempts = 0;
- const maxAttempts = 20; // 500ms * 20 = 10 seconds max
- const scrollInterval = setInterval(() => {
- if (window.scrollY >= pos || attempts >= maxAttempts) {
- clearInterval(scrollInterval);
- isScrolling = false
- notify(`[[YT Scroll Saver] Scroll position reached or max attempts hit. {Pos: ${window.scrollY}, Saved: ${pos}}`)
- log.debug(`[YT Scroll Saver] Scroll position reached or max attempts hit. {Pos: ${window.scrollY}, Saved: ${pos}}`);
- return;
- }
- notify(`[YT Scroll Saver] Scrolling.. `, 1000)
- log.debug(`[YT Scroll Saver] Scrolling.. `);
- window.scrollTo(0, pos);
- attempts++;
- }, 500);
- }
- async function createManualScrollBtn(pos) {
- const chipList = await waitForElm("iron-selector")
- const btn = document.createElement("button")
- btn.addEventListener( 'click', () => scrollTo(pos) )
- btn.innerText = `Scroll to: ${pos}`
- btn.style = `
- background-color: transparent;
- padding: 8px;
- border-radius: 10px;
- margin: 0 30px;
- color: #f1f1f1;
- border-color: rgba(255, 255, 255, 0.2);
- outline: none !important;
- cursor: pointer;
- `
- chipList.append(btn)
- btnAdded = true
- }
- function checkUrlChange() {
- if (location.href !== lastUrl) {
- lastUrl = location.href;
- if (window.location.pathname.includes('/videos')) {
- log.debug("[YT Scroll Saver] Detected navigation to a channel's /videos page.");
- isScrolling = true
- setTimeout(loadScrollPosition, 1000);
- } else {
- btnAdded = false
- }
- log.debug("btnAdded",btnAdded)
- }
- }
- // Attach scroll event listener
- window.addEventListener('scroll', () => {
- if (!window.location.pathname.includes('/videos') || isScrolling) return;
- if (saveTimeout) clearTimeout(saveTimeout);
- saveTimeout = setTimeout(saveScrollPosition, saveDelay);
- });
- // Watch for SPA navigation changes
- const observer = new MutationObserver(checkUrlChange);
- observer.observe(document.body, { childList: true, subtree: true });
- // Restore scroll position after page loads
- if (window.location.pathname.includes('/videos')) {
- isScrolling = true
- notify(`[YT Scroll Saver] Loading scroll position.`)
- log.debug(`[YT Scroll Saver] Loading scroll position.`);
- setTimeout(loadScrollPosition, 1000); // Delay to allow initial content to load
- };
- })();