您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
infinite scroll, filter: public, private, duration, include/exclude tags
当前为
// ==UserScript== // @name thisvid infinite scroll and filter // @license MIT // @namespace http://tampermonkey.net/ // @version 3.3 // @description infinite scroll, filter: public, private, duration, include/exclude tags // @author smartacephale // @match https://*.thisvid.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=thisvid.com // @grant GM_addStyle // @run-at document-end // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/rxjs.umd.min.js // @require https://unpkg.com/[email protected]/dist/vue.global.prod.js // ==/UserScript== const SponsaaLogo = ` Kono bangumi ha(wa) goran no suponsaa no teikyou de okurishimasu⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡟⣟⢻⢛⢟⠿⢿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣿⣾⣾⣵⣧⣷⢽⢮⢧⢿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣯⣭⣧⣯⣮⣧⣯⣧⣯⡮⣵⣱⢕⣕⢕⣕⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⡫⡻⣝⢯⡻⣝⡟⣟⢽⡫⡟⣏⢏⡏⡝⡭⡹⡩⣻⣿⣿⣿⣿⠟⠟⢟⡟⠟⠻⠛⠟⠻⠻⣿⣿⣿⡟⠟⠻⠛⠟⠻⠻⣿⣿ ⣿⣿⣿⣿⡿⣻⣿⣿⣿⡿⣿⣿⡿⣿⣿⢿⢿⡻⢾⠽⡺⡞⣗⠷⣿⣿⣿⡏⠀⠀⠀⣣⣤⡄⠀⠠⣄⡆⠫⠋⠻⢕⣤⡄⠀ ⢀⣤⣔⣿⣿ ⣿⣿⣿⣿⣷⣷⣷⣾⣶⣯⣶⣶⣷⣷⣾⣷⣳⣵⣧⣳⡵⣕⣮⣞⣾⣿⡟⠄⢀⣦⠀⢘⣽⡇⠀⠨⣿⡌⠀⠣⣠⠹⠿⡭⠀ ⠐⣿⣿⣿⣿ ⣿⣿⣿⣿⣕⣵⣱⣫⣳⡯⣯⣫⣯⣞⣮⣎⣮⣪⣢⣣⣝⣜⡜⣜⣾⣿⠃⠀⠀⠑⠀⠀⢺⡇⠀ ⢘⣾⠀⢄⢄⠘⠀⢘⢎⠀⢈⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣙⣛⣛⢻⢛⢟⢟⣛⢻⢹⣙⢳⢹⢚⢕⣓⡓⡏⣗⣿⣓⣀⣀⣿⣿⣮⢀⣀⣇⣀⣐⣿⣔⣀⢁⢀⣀⣀⣅⣀⡠⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣾⡞⣞⢷⡻⡯⡷⣗⢯⢷⢞⢷⢻⢞⢷⡳⣻⣺⣿⣿⣿⣿⣿⣿⣿⣿⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣷⣵⡵⣼⢼⢼⡴⣵⢵⡵⣵⢵⡵⣵⣪⣾⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣮⣧⣫⣪⡪⡣⣫⣪⣣⣯⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿`.trim(); //==================================================================================================== unsafeWindow.Vue = Vue; const { ref, watch, reactive, createApp } = Vue; //==================================================================================================== function $(x, e = document.body) { return e.querySelector(x); } function $$(x, e = document.body) { return e.querySelectorAll(x); } function parseDOM(html) { const parsed = new DOMParser().parseFromString(html, 'text/html').body; return parsed.children.length > 1 ? parsed : parsed.firstElementChild; } function fetchHtml(url) { return fetch(url).then((r) => r.text()).then((h) => parseDOM(h)); } function parseCSSUrl(s) { return s.replace(/url\("|\"\).*/g, ''); } function timeToSeconds(t) { return (t.match(/\d+/gm) || ['0']) .reverse() .map((s, i) => parseInt(s) * 60 ** i) .reduce((a, b) => a + b) || 0; } function circularShift(n, c = 6, s = 1) { return (n + s) % c || c; } function parseIntegerOr(n, or) { return Number.isInteger(parseInt(n)) ? parseInt(n) : or; } function stringToTags(s) { return s.split(",").map(s => s.trim().toLowerCase()).filter(s => s); } function listenEvents(dom, events, callback) { for (const e of events) { dom.addEventListener(e, callback, true); } } //==================================================================================================== class Observer { constructor(callback) { this.observer = new IntersectionObserver((entries, observer) => { for (const entry of entries) { if (entry.isIntersecting) { callback(entry.target, observer); } } }); } observe(target) { this.observer.observe(target); } throttle(target, throttleTime) { this.observer.unobserve(target); setTimeout(() => this.observer.observe(target), throttleTime); } static observeWhile(target, callback, throttleTime) { const observer_ = new Observer(async (target) => { const condition = await callback(); if (condition) observer_.throttle(target, throttleTime); }); observer_.observe(target); return observer_; } } //==================================================================================================== class LazyImgLoader { attributeName = 'lazy-loading'; constructor(callback) { this.lazyImgObserver = new Observer((target) => { callback(target, this.delazify); }); } lazify(parent, img, src) { img.setAttribute(this.attributeName, src); img.src = ''; this.lazyImgObserver.observe(parent); } delazify = (target) => { this.lazyImgObserver.observer.unobserve(target); const img = $('img', target); img.src = img.getAttribute(this.attributeName); // img.removeAttribute(this.attributeName) } } //==================================================================================================== class PersistentState { key = "state__"; constructor(state) { this.state = reactive(state); this.trySetFromLocalStorage(); this.watchPersistence(); } watchPersistence() { watch(this.state, (value) => { this.saveToLocalStorage(this.key, value); }); } saveToLocalStorage(key, value) { localStorage.setItem(key, JSON.stringify(value)); } trySetFromLocalStorage() { const localStorageValue = localStorage.getItem(this.key); if (localStorageValue !== null) { const prevState = JSON.parse(localStorageValue); for (const prop of Object.keys(prevState)) { this.state[prop] = prevState[prop]; } } } } //==================================================================================================== const SCROLL_RESET_DELAY = 500; const ANIMATION_DELAY = 750; //==================================================================================================== class THISVID_RULES { constructor() { this.PAGINATION = $('.pagination'); this.PAGE_HAS_VIDEO = $('.tumbpu'); this.PAGINATION_LAST = this.PAGINATION ? parseInt($('.pagination-next').previousElementSibling.firstElementChild.textContent) : 1; } URL_DATA() { let { origin, pathname, search } = window.location; const offset = parseInt(pathname.split(/(\d+\/)$/)[1] || '1'); let pathname_ = pathname.split(/(\d+\/)$/)[0]; if (pathname === '/') pathname_ = '/latest-updates/'; const iteratable_url = (n) => `${origin}${pathname_}${n}/${search}`; return { offset, iteratable_url } } PREVIEW_IMG_DATA(thumbElement) { const img = $('img', thumbElement); const privateThumb = $('.private', thumbElement); let imgSrc; if (privateThumb) { imgSrc = parseCSSUrl(privateThumb.style.background); privateThumb.removeAttribute('style'); } else { imgSrc = img.getAttribute('data-original'); img.removeAttribute('data-original'); } img.removeAttribute('data-cnt'); return { img, imgSrc }; } ITERATE_PREVIEW_IMG(src) { return src.replace(/(\d)(?=\.jpg$)/, (_, n) => `${circularShift(parseInt(n))}`); } GET_VIDEO_DURATION(thumbElement) { return timeToSeconds($('.thumb > .duration', thumbElement).textContent); } IS_PRIVATE(thumbElement) { return thumbElement.firstElementChild.classList.contains('private'); } PRIVATE_PREVIEW_FIX() { unsafeWindow.$('img[alt!="Private"]').off('mouseover'); unsafeWindow.$('img[alt!="Private"]').off('mouseout'); } } //==================================================================================================== class VueApp { template = ` <div class="fixed bottom-0 right-0 z-9999 rounded px-2 py-0.5 bg-zinc-800 max-w-full" v-if="state.uiEnabled"> <div class="flex items-center cursor-pointer py-1 px-2 m-1" @click="state.hidden = !state.hidden"> <span v-text="state.hidden ? '☒' : '⛶'" class="text-right w-full text-xl text-zinc-300"></span> </div> <template v-if="!state.hidden"> <div class="flex items-center bg-zinc-900 py-1 px-2 m-1 font-mono"> <input type="checkbox" id="exclude" v-model="state.filterNegative" class="mr-2 size-auto"> <label for="exclude" class="text-zinc-300 font-mono">exclude</label> <input type="text" v-model="state.filterNegativeTags" placeholder="tag1, tag2,.." class="w-full h-8 text-white px-3 py-2 focus:outline-none bg-zinc-700 mx-2 rounded-sm"> </div> <div class="flex items-center bg-zinc-900 py-1 px-2 m-1 font-mono"> <input type="checkbox" id="include" v-model="state.filterPositive" class="mr-2 size-auto"> <label for="include" class="text-zinc-300 font-mono">include</label> <input type="text" v-model="state.filterPositiveTags" placeholder="tag1, tag2,.." class="w-full h-8 text-white px-3 py-2 focus:outline-none bg-zinc-700 mx-2 rounded-sm size-auto"> </div> <div class="flex items-center bg-zinc-900 py-1 px-2 m-1 font-mono"> <input type="checkbox" id="filterPrivate" v-model="state.filterPrivate" class="mr-2 size-auto"> <label for="filterPrivate" class="text-zinc-300 font-mono">private</label> <input type="checkbox" id="filterPublic" v-model="state.filterPublic" class="mx-2 size-auto"> <label for="filterPublic" class="text-zinc-300 font-mono">public</label> <span v-if="state.pagIndexLast > 0" class="text-zinc-300 ml-auto"> {{ state.pagIndexCur }}/{{ state.pagIndexLast }} </span> </div> <div class="flex items-center bg-zinc-900 py-1 px-2 m-1 font-mono"> <input type="checkbox" id="duration" v-model="state.filterDuration" class="mr-2"> <label for="duration" class="text-zinc-300 font-mono">duration</label> <label for="durationFrom" class="text-zinc-300 mx-2 font-mono">from</label> <input type="number" step="10" min="0" max="72000" id="durationFrom" v-model.number="state.filterDurationFrom" class="w-24 h-8 bg-gray-700 text-zinc-300 rounded px-3 py-2 focus:outline-none"> <label for="durationTo" class="text-zinc-300 mx-2 font-mono">to</label> <input type="number" step="10" min="0" max="72000" id="durationTo" v-model.number="state.filterDurationTo" class="w-24 h-8 bg-gray-700 text-zinc-300 rounded px-3 py-2 focus:outline-none"> </div> </template> </div> `; constructor(state) { const root = parseDOM('<div id="tapermonkey-app" style="position: relative; z-index: 999999;"></div>'); document.body.appendChild(root); this.vue = createApp({ setup: () => ({ state }), template: this.template }).mount("#tapermonkey-app"); GM_addStyle(`#tapermonkey-app *,#tapermonkey-app :before,#tapermonkey-app :after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}#tapermonkey-app :before,#tapermonkey-app :after{--tw-content: ""}#tapermonkey-app html,#tapermonkey-app :host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}#tapermonkey-app body{margin:0;line-height:inherit}#tapermonkey-app hr{height:0;color:inherit;border-top-width:1px}#tapermonkey-app abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}#tapermonkey-app h1,#tapermonkey-app h2,#tapermonkey-app h3,#tapermonkey-app h4,#tapermonkey-app h5,#tapermonkey-app h6{font-size:inherit;font-weight:inherit}#tapermonkey-app a{color:inherit;text-decoration:inherit}#tapermonkey-app b,#tapermonkey-app strong{font-weight:bolder}#tapermonkey-app code,#tapermonkey-app kbd,#tapermonkey-app samp,#tapermonkey-app pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}#tapermonkey-app small{font-size:80%}#tapermonkey-app sub,#tapermonkey-app sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}#tapermonkey-app sub{bottom:-.25em}#tapermonkey-app sup{top:-.5em}#tapermonkey-app table{text-indent:0;border-color:inherit;border-collapse:collapse}#tapermonkey-app button,#tapermonkey-app input,#tapermonkey-app optgroup,#tapermonkey-app select,#tapermonkey-app textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}#tapermonkey-app button,#tapermonkey-app select{text-transform:none}#tapermonkey-app button,#tapermonkey-app input:where([type=button]),#tapermonkey-app input:where([type=reset]),#tapermonkey-app input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}#tapermonkey-app :-moz-focusring{outline:auto}#tapermonkey-app :-moz-ui-invalid{box-shadow:none}#tapermonkey-app progress{vertical-align:baseline}#tapermonkey-app ::-webkit-inner-spin-button,#tapermonkey-app ::-webkit-outer-spin-button{height:auto}#tapermonkey-app [type=search]{-webkit-appearance:textfield;outline-offset:-2px}#tapermonkey-app ::-webkit-search-decoration{-webkit-appearance:none}#tapermonkey-app ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}#tapermonkey-app summary{display:list-item}#tapermonkey-app blockquote,#tapermonkey-app dl,#tapermonkey-app dd,#tapermonkey-app h1,#tapermonkey-app h2,#tapermonkey-app h3,#tapermonkey-app h4,#tapermonkey-app h5,#tapermonkey-app h6,#tapermonkey-app hr,#tapermonkey-app figure,#tapermonkey-app p,#tapermonkey-app pre{margin:0}#tapermonkey-app fieldset{margin:0;padding:0}#tapermonkey-app legend{padding:0}#tapermonkey-app ol,#tapermonkey-app ul,#tapermonkey-app menu{list-style:none;margin:0;padding:0}#tapermonkey-app dialog{padding:0}#tapermonkey-app textarea{resize:vertical}#tapermonkey-app input::-moz-placeholder,#tapermonkey-app textarea::-moz-placeholder{opacity:1;color:#9ca3af}#tapermonkey-app input::placeholder,#tapermonkey-app textarea::placeholder{opacity:1;color:#9ca3af}#tapermonkey-app button,#tapermonkey-app [role=button]{cursor:pointer}#tapermonkey-app :disabled{cursor:default}#tapermonkey-app img,#tapermonkey-app svg,#tapermonkey-app video,#tapermonkey-app canvas,#tapermonkey-app audio,#tapermonkey-app iframe,#tapermonkey-app embed,#tapermonkey-app object{display:block;vertical-align:middle}#tapermonkey-app img,#tapermonkey-app video{max-width:100%;height:auto}#tapermonkey-app [hidden]{display:none}#tapermonkey-app *,#tapermonkey-app :before,#tapermonkey-app :after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }#tapermonkey-app ::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }#tapermonkey-app .fixed{position:fixed}#tapermonkey-app .bottom-0{bottom:0}#tapermonkey-app .right-0{right:0}#tapermonkey-app .m-1{margin:.25rem}#tapermonkey-app .mx-2{margin-left:.5rem;margin-right:.5rem}#tapermonkey-app .ml-auto{margin-left:auto}#tapermonkey-app .mr-2{margin-right:.5rem}#tapermonkey-app .flex{display:flex}#tapermonkey-app .hidden{display:none}#tapermonkey-app .size-auto{width:auto;height:auto}#tapermonkey-app .h-8{height:2rem}#tapermonkey-app .w-24{width:6rem}#tapermonkey-app .w-full{width:100%}#tapermonkey-app .cursor-pointer{cursor:pointer}#tapermonkey-app .resize{resize:both}#tapermonkey-app .flex-wrap{flex-wrap:wrap}#tapermonkey-app .items-center{align-items:center}#tapermonkey-app .rounded{border-radius:.25rem}#tapermonkey-app .rounded-sm{border-radius:.125rem}#tapermonkey-app .border{border-width:1px}#tapermonkey-app .bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity))}#tapermonkey-app .bg-zinc-700{--tw-bg-opacity: 1;background-color:rgb(63 63 70 / var(--tw-bg-opacity))}#tapermonkey-app .bg-zinc-800{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / var(--tw-bg-opacity))}#tapermonkey-app .bg-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity))}#tapermonkey-app .px-2{padding-left:.5rem;padding-right:.5rem}#tapermonkey-app .px-3{padding-left:.75rem;padding-right:.75rem}#tapermonkey-app .py-0{padding-top:0;padding-bottom:0}#tapermonkey-app .py-0\.5{padding-top:.125rem;padding-bottom:.125rem}#tapermonkey-app .py-1{padding-top:.25rem;padding-bottom:.25rem}#tapermonkey-app .py-2{padding-top:.5rem;padding-bottom:.5rem}#tapermonkey-app .text-right{text-align:right}#tapermonkey-app .font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}#tapermonkey-app .text-xl{font-size:1.25rem;line-height:1.75rem}#tapermonkey-app .text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}#tapermonkey-app .text-zinc-300{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}`); } } //==================================================================================================== (function () { 'use strict'; console.log(SponsaaLogo); class DomManager { constructor() { this.data = new Map(); this.container = this.createThumbsContainer(); this.lazyImgLoader = new LazyImgLoader((target, delazify) => { if (!this.isFiltered(target)) { delazify(target); } }); } isFiltered(el) { return el.className.includes('filtered'); } filterPositiveTags_ = (k, v, tags) => { const containTagsNot = tags.some(tag => !k.includes(tag)); v.element.classList.toggle('filtered-tag-pos', state.filterPositive && containTagsNot); } filterNegativeTags_ = (k, v, tags) => { const containTags = tags.some(tag => k.includes(tag)); v.element.classList.toggle('filtered-tag-neg', state.filterNegative && containTags); } filterPrivate_(v) { v.element.classList.toggle('filtered-private', state.filterPrivate && RULES.IS_PRIVATE(v.element)); } filterPublic_(v) { v.element.classList.toggle('filtered-public', state.filterPublic && !RULES.IS_PRIVATE(v.element)); } filterByDuration_ = (v) => { const notInRange = v.duration < state.filterDurationFrom || v.duration > state.filterDurationTo; v.element.classList.toggle('filtered-duration', state.filterDuration && notInRange); } filter_ = (filters, offset = 0) => { const runFilters = []; if (filters.filterPrivate) { runFilters.push((_, v) => this.filterPrivate_(v)); } if (filters.filterPublic) { runFilters.push((_, v) => this.filterPublic_(v)); } if (filters.filterDuration) { runFilters.push((_, v) => this.filterByDuration_(v)); } if (filters.filterNegative) { const tags = stringToTags(state.filterNegativeTags); runFilters.push((k, v) => this.filterNegativeTags_(k, v, tags)); } if (filters.filterPositive) { const tags = stringToTags(state.filterPositiveTags); runFilters.push((k, v) => this.filterPositiveTags_(k, v, tags)); } let offset_counter = 1; for (const [k, v] of this.data.entries()) { offset_counter++; if (offset_counter > offset) { for (const rf of runFilters) { rf(k, v); } } } } filterAll = (offset) => { const applyFilters = {}; const stateFilters = ['filterPrivate', 'filterPublic', 'filterDuration', 'filterPositive', 'filterNegative']; stateFilters.forEach(f => { if (state[f]) applyFilters[f] = state[f] }); this.filter_(applyFilters, offset); } createThumbsContainer() { return parseDOM('<div class="thumbs-items"></div>'); } handleLoadedHTML = (html, mount, useGlobalContainer = true) => { const thumbs = $$('.tumbpu', html); const container = !useGlobalContainer ? this.createThumbsContainer() : this.container; const data_offset = this.data.size; for (const thumbElement of thumbs) { const url = thumbElement.getAttribute('href'); if (!url || this.data.has(url)) { thumbElement.remove(); continue; } this.data.set(url, { element: thumbElement, duration: RULES.GET_VIDEO_DURATION(thumbElement) }); const { img, imgSrc } = RULES.PREVIEW_IMG_DATA(thumbElement); img.classList.add('tracking'); this.lazyImgLoader.lazify(thumbElement, img, imgSrc); container.appendChild(thumbElement); } this.filterAll(data_offset); mount.before(container); }; } class PreviewManager { setTick(callback) { if (this.tick) this.tick.unsubscribe(); this.tick = rxjs.interval(ANIMATION_DELAY).pipe(rxjs.startWith(0)).subscribe(callback); } animatePreview = (e) => { const { target: el, type, src } = e; if (el.tagName !== 'IMG' || !el.classList.contains('tracking')) return; if (type === 'mouseout' || type === 'touchend') { this.tick.unsubscribe(); el.src = el.getAttribute('lazy-loading'); } if (type === 'mouseover' || type === 'touchstart') { if (!src) el.src = el.getAttribute('lazy-loading'); this.setTick(() => { el.src = RULES.ITERATE_PREVIEW_IMG(el.src); }); } }; listen(dom) { listenEvents(dom, ['mouseout', 'mouseover', 'touchstart', 'touchend'], this.animatePreview); } } const RULES = new THISVID_RULES(); class PaginationPageManager { constructor() { if (!RULES.PAGE_HAS_VIDEO) return; handleLoadedHTML(document.body, RULES.PAGINATION || RULES.PAGE_HAS_VIDEO.parentElement); previewManager.listen((RULES.PAGINATION || RULES.PAGE_HAS_VIDEO).parentElement); state.pagIndexCur = 1; state.pagIndexLast = RULES.PAGINATION_LAST; if (!RULES.PAGINATION) return; RULES.PAGINATION.style.opacity = 0; RULES.PRIVATE_PREVIEW_FIX(); this.paginationGenerator = this.createNextPageGenerator(); this.paginationObserver = Observer.observeWhile(RULES.PAGINATION, this.generatorConsume, SCROLL_RESET_DELAY); this.paginationObserver.observe(RULES.PAGINATION); } generatorConsume = async () => { const { value: { url, offset } = {}, done, } = this.paginationGenerator.next(); if (!done) { console.log(url); const nextPageHTML = await fetchHtml(url); const prevScrollPos = document.documentElement.scrollTop; handleLoadedHTML(nextPageHTML, RULES.PAGINATION); state.pagIndexCur = offset; window.scrollTo(0, prevScrollPos); } return !this.generatorDone; } createNextPageGenerator() { const { offset, iteratable_url } = RULES.URL_DATA(); state.pagIndexCur = offset; function* nextPageGenerator() { for (let c = offset + 1; c <= RULES.PAGINATION_LAST; c++) { const url = iteratable_url(c); yield { url, offset: c }; } } return nextPageGenerator(); } } class Router { constructor() { this.route(); } route() { const { href } = window.location; const allowed_pagination = [ /\.com\/$/, /\/(categories|tags?)\//, /\/?q=.*/, /\/((\w+-)?rated|(\w+-)?popular|(\w+-)?private|(\w+-)?newest|winners)\/$/, /\/members\/\d+\/\w+_videos\// ]; if (allowed_pagination.some(r => r.test(href))) { this.handlePaginationPage(); } else if (/\/members\/\d+\/$/.test(href)) { this.handleMemberPage(); } } handlePaginationPage() { this.paginationManager = new PaginationPageManager(); this.ui = new VueApp(state); } handleMemberPage() { const privates = $('#list_videos_private_videos_items'); if (privates) { const mistakes = $$('#list_videos_private_videos_items ~ div'); for (const m of mistakes) { if (m.id === 'list_videos_favourite_videos') break; privates.appendChild(m); } handleLoadedHTML(privates, privates, false); } const favorites = $('#list_videos_favourite_videos'); if (favorites) { const mountTo = favorites.firstElementChild.nextElementSibling; handleLoadedHTML(favorites, mountTo, false); } if (privates || favorites) { previewManager.listen((privates || favorites).parentElement); } } } const { state } = new PersistentState({ filterDurationFrom: 0, filterDurationTo: 600, filterDuration: false, filterPrivate: false, filterPublic: false, filterNegativeTags: "", filterNegative: false, filterPositiveTags: "", filterPositive: false, uiEnabled: true, pagIndexLast: 1, pagIndexCur: 1, }); const { filter_, handleLoadedHTML } = new DomManager(); watch([() => state.filterDurationFrom, () => state.filterDurationTo], (a, b) => { state.filterDurationFrom = parseIntegerOr(a[0], b[0]); state.filterDurationTo = parseIntegerOr(a[1], b[1]); if (state.filterDuration) filter_({ filterDuration: true }); }); watch(() => state.filterDuration, () => filter_({ filterDuration: true })); watch(() => state.filterPrivate, () => filter_({ filterPrivate: true })); watch(() => state.filterPublic, () => filter_({ filterPublic: true })); watch(() => state.filterNegative, () => filter_({ filterNegative: true })); watch(() => state.filterNegativeTags, () => { if (state.filterNegative) filter_({ filterNegative: true }); }, { deep: true }); watch(() => state.filterPositive, () => filter_({ filterPositive: true })); watch(() => state.filterPositiveTags, () => { if (state.filterPositive) filter_({ filterPositive: true }); }, { deep: true }); const previewManager = new PreviewManager(); const router = new Router(); GM_addStyle('.filtered-private, .filtered-duration, .filtered-public, .filtered-tag-pos, .filtered-tag-neg { display: none !important; }'); })();