Removes video ads from online streaming services in ReYohoho (Rezka, Alloha, Collaps, VideoCDN etc.). Also applies minor enhancements, if possible (extra play speeds etc.).
// ==UserScript== // @name ReYohoho – No Ads + Enhancements [Ath] // @name:ru ReYohoho – Без Рекламы + Улучшения [Ath] // @name:uk ReYohoho – Без Реклами + Покращення [Ath] // @name:be ReYohoho – Без Рэкламы + Паляпшэнні [Ath] // @name:bg ReYohoho – Без Реклами + Подобрения [Ath] // @name:tt ReYohoho – Рекламасыз + Яхшыртулар [Ath] // @name:sl ReYohoho – Brez Oglasov + Izboljšave [Ath] // @name:sr ReYohoho – Bez Reklama + Poboljšanja [Ath] // @name:ka ReYohoho – რეკლამის გარეშე + გაუმჯობესებები [Ath] // @description Removes video ads from online streaming services in ReYohoho (Rezka, Alloha, Collaps, VideoCDN etc.). Also applies minor enhancements, if possible (extra play speeds etc.). // @description:ru Убирает рекламные ролики онлайн-кинотеатров в ReYohoho (Rezka, Alloha, Collaps, VideoCDN и т.д.). Также применяет небольшие улучшения, если возможно (дополнительные скорости проигрывания и т.п.). // @description:uk Видаляє рекламні ролики з онлайн-сервісів для перегляду відео у ReYohoho (Rezka, Alloha, Collaps, VideoCDN тощо). Також застосовує додаткові покращення, якщо це можливо (додаткові швидкості відтворення тощо). // @description:be Выдаляе рэкламныя ролікі з анлайн-стрымінгавых паслуг у ReYohoho (Rezka, Alloha, Collaps, VideoCDN і г.д.). Таксама ўжывае дробныя паляпшэнні, калі гэта магчыма (дадатковыя хуткасці прайгравання і г.д.). // @description:bg Премахва видео рекламите от онлайн стрийминг услугите в ReYohoho (Rezka, Alloha, Collaps, VideoCDN и т.н.). Така също прилага малки подобрения, ако е възможно (допълнителни скорости на възпроизвеждане и т.н.). // @description:tt Онлайн-трансляция хезмәтләрендәге ReYohoho (Rezka, Alloha, Collaps, VideoCDN һ.б.) видео рекламаларны бетерә. Шулай ук мөмкин булганда кечкенә яхшыртулар кертә (өстәмә уйнату тизлекләре һ.б.). // @description:sl Odstrani video oglase iz spletnih pretočnih storitev v ReYohoho (Rezka, Alloha, Collaps, VideoCDN itd.). Prav tako omogoča manjše izboljšave, če je to mogoče (dodatne hitrosti predvajanja itd.). // @description:sr Uklanja video reklame sa online striming servisa u ReYohoho (Rezka, Alloha, Collaps, VideoCDN itd.). Takođe primenjuje manja poboljšanja, ako je to moguće (dodatne brzine reprodukcije itd.). // @description:ka ამოიღებს ვიდეო რეკლამებს ონლაინ სტრიმინგის სერვისებიდან ReYohoho-ში (Rezka, Alloha, Collaps, VideoCDN და ა.შ.). ასევე იღებს პატარა გაუმჯობესებებს, თუ ეს შესაძლებელია (დამატებითი დაკვრის სიჩქარეები და ა.შ.). // @namespace athari // @author Athari (https://github.com/Athari) // @copyright © Prokhorov ‘Athari’ Alexander, 2024–2025 // @license MIT // @homepageURL https://github.com/Athari/AthariUserJS // @supportURL https://github.com/Athari/AthariUserJS/issues // @version 1.0.1 // @icon https://reyohoho.github.io/reyohoho/icons/favicon-32x32.png // @match https://*.allarknow.online/* // @match https://*.obrut.show/* // @match https://*.embess.ws/* // @match https://*.fotpro135alto.com/* // @match https://*.videoframe1.com/* // @match https://*.videoframe*.com/* // @match https://*.lumex.space/* // @match https://*.tv-2-kinoserial.net/* // @match https://*-kinoserial.net/* // @match https://*.rezka.*/* // @match https://*.hdrezka.*/* // @match https://*.kinopub.*/* // @match https://*.rezkify.*/* // @match https://*.rezkery.*/* // @grant unsafeWindow // @run-at document-start // @sandbox raw // @require https://cdn.jsdelivr.net/npm/@athari/[email protected]/monkeyutils.u.min.js // @resource script-urlpattern https://cdn.jsdelivr.net/npm/urlpattern-polyfill/dist/urlpattern.js // @tag athari // ==/UserScript== (async () => { 'use strict' const { isFunction, isObject, isString, assignDeep, waitForCallback, waitForEvent, waitFor, withTimeout, matchLocation, throwError, attempt, overrideProperty, overrideFunction, ress, scripts, els, opts, } = //require("../@athari-monkeyutils/monkeyutils.u"); // TODO athari.monkeyutils; const win = unsafeWindow; const res = ress(), script = scripts(res); const el = els(document); Object.assign(globalThis, globalThis.URLPattern ? null : await script.urlpattern); const anySubdomain = "(.*\\.)?"; const globMap = { ".": "\\.", "*": "[^\\.]+" }; const globDomain = (s) => s.replace(/\.|\*/g, ([m]) => globMap[m] ?? m); const oneOfDomains = (...ds) => `(${ds.map(globDomain).join("|")})`; const host = { alloha: `${anySubdomain}${oneOfDomains("allarknow.online")}`, collaps: `${anySubdomain}${oneOfDomains("embess.ws")}`, hdvb: `${anySubdomain}${oneOfDomains("fotpro135alto.com")}`, turbo: `${anySubdomain}${oneOfDomains("obrut.show")}`, vibix: `${anySubdomain}${oneOfDomains("videoframe*.com")}`, videocdn: `${anySubdomain}${oneOfDomains("lumex.space")}`, videoseed: `${anySubdomain}${oneOfDomains("*-kinoserial.net")}`, hdrezka: `${anySubdomain}${oneOfDomains("rezka.*", "hdrezka.*", "kinopub.*", "rezkify.*", "rezkery.*")}`, }; const loggingProxy = (o, level = 0, root = null, path = []) => new Proxy(o, new class { construct() { console.log("proxy", { o, level, root, path }); } #proxies = {} #logProp(act, t, prop, value, args = []) { console.log(act, "{", root, "}", `${path.join(".")}.{`, t, `}.${prop} = `, value, ` (${typeof value})`, args); } get(t, prop) { let proxy = this.#proxies[prop]; if (proxy != null) { this.#logProp("get", t, prop, proxy.value); return proxy.proxy; } const value = Reflect.get(t, prop); this.#logProp("get", t, prop, value); if (level > 1 && (isObject(value) || isFunction(value))) { proxy = { value, proxy: loggingProxy(value, level - 1, root ?? value, path.concat(prop)) }; this.#proxies[prop] = proxy; return proxy.proxy; } else { return value; } } set(t, prop, value) { this.#logProp("set", t, prop, value); return Reflect.set(t, prop, value); } apply(t, self, args) { const value = Reflect.apply(t, self, args); this.#logProp("fun", t, "()", value, args); return value; } construct(t, args) { const value = Reflect.construct(t, args); this.#logProp("new", t, "new()", value, args); return value; } }); const playSpeeds = [ 0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3, 3.5, 4 ]; const configPlayerJS = { settings: { customspeeds: 1, speeds: playSpeeds.join(","), }, volume: 1, postmessage: 1, log: 1, eventstracker: 1, // logging & messaging vast: 0, vast_timeout: 0, vast_volume: 0, // ad config preroll: "", prerolls: 0, midroll: [], midrolls: 0, // ad urls yamtr: 0, // counters }; const fixPlayerJS = (player) => { if (player == null) return console.error("playerjs not found"); player.api('update:vast', false); player.api('log', true); player.api('volume', 1); const options = overrideFunction(win.console, 'log', null, (_, v) => v, () => player.api('options')); if (options == null) return console.error("playerjs options not found"); const elPlayer = document.querySelector(`#${options.id}`); assignDeep(win, { player, options }); console.info({ player, options, el: elPlayer }); assignDeep(options, configPlayerJS, { events: options.events || /*'onPlayerJSEvent'*/'PlayerjsEvents', }); if (elPlayer) elPlayer.oncontextmenu = null; if (isString(options.events)) { const originalEvents = options.events; // gets reset for some reason overrideFunction(win, options.events, (onPlayerJSEvent, event, playerId, data) => { options.events = originalEvents; const ignore = event.startsWith('vast_'); if (![ 'time' ].includes(event)) console.info(ignore ? "event nay" : "event yay", { e: event, data, id: playerId }); if (ignore) return; (onPlayerJSEvent ?? win.PlayerjsEvents)?.(event, playerId, data); }) } }; const fixPlyrConfig = (player) => assignDeep(player, { controls: [ 'play-large', 'play', 'rewind', 'fast-forward', 'progress', 'current-time', 'duration', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen', ], ads: { enabled: false, midroll: [], preroll: "" }, speed: { options: playSpeeds }, settings: [ 'quality', 'audio', 'captions', 'speed', 'loop', 'scale', 'bugReport' ], volume: 1, disableContextMenu: false, debug: false, }); console.info("reyohoho no ads", location.href); // Alloha: Plyr // Configs parsed from JSON strings if (matchLocation(host.alloha)) { overrideFunction(win.JSON, 'parse', (parse, text) => { const json = parse(text); if (json.ads && json.controls) { console.info("plyr config original", structuredClone(json)); fixPlyrConfig(json); console.info("plyr config modified", structuredClone(json)); } else if (json.active && json.all) { console.info("fileList original", json); } else { console.debug("parsed json", json); } return json; }); } // Collaps: VenomPlayer // Configs passed to makePlayer wrapper function. Override function after var assignments. else if (matchLocation(host.collaps)) { const adTimeouts = { loading: 0, starting: 0, toNextImp: 0, global: 0 }; overrideProperty(win, 'adTimeouts', { log: true, set: v => assignDeep(v, adTimeouts) }); const adCfg = { maxImpressions: 0, urls: [], exitFullscreenVideo: false, vast: { timeouts: adTimeouts } }; overrideProperty(win, 'adsConfig', { log: true, set: v => assignDeep(v, { volume: 0, pre: adCfg, middle: adCfg, post: adCfg }) }); const modifiedOpts = { speed: playSpeeds, theme: 'modern', /* venom/classic/metro/modern */ }; overrideProperty(win, 'middleCount', _ => { overrideFunction(win, 'makePlayer', (makePlayer, opts) => makePlayer(new Proxy(assignDeep(opts, modifiedOpts), new class { set(t, prop, v) { return Object.hasOwn(modifiedOpts, prop) ? true : Reflect.set(t, prop, v); } }))); return 0; }); } // HDVB: PlayerJS // Configs provided as `let` variables. Private "rek" ads config. Break script, run manually. else if (matchLocation(host.hdvb)) { let fail = true; const tag = { conf: { banner_show: false }, key: "", script: "" }; const banner = { show: false, key: "", script: "" }; const roll = { time: "", url: "" }; overrideProperty(win, 'playerConfigs', { log: "player config", set: v => fail ? throwError("nope") : assignDeep(v, configPlayerJS, { events: v.events || 'onPlayerJSEvent', rek: { endtag: tag, starttag: tag, start2tag: tag, start3tag: tag, pausebanner: banner, push_roll: roll, midroll: [], preroll: [], pushbanner: [], }, }), }); const script = await waitFor(() => el.all.tag.script.filter(s => s.innerText.includes("let playerConfigs"))[0], 10000); if (script == null) return console.error("player script not found"); fail = false; win.eval(script.innerText.replace("let playerConfigs", "playerConfigs")); fixPlayerJS(win.player); } // Turbo: PlayerJS // Config provided as encrypted string passed to global Player function. Wait for pljssglobal[0] assignment. else if (matchLocation(host.turbo)) { //overrideProperty(win, 'pljssglobal', v => loggingProxy(v, 3)); let [ player0SetWait, player0Set ] = waitForCallback(); overrideProperty(win, 'pljssglobal', v => new Proxy(v, new class { set(t, prop, value) { if (prop == "0") player0Set(value); return Reflect.set(t, prop, value); } })); const player = win.player = win.pljssglobal?.[0] ?? await withTimeout(player0SetWait, 10000); fixPlayerJS(player); } // TODO Vibix: PlayerJS // Configs provided as constants and passed to Playerjs constructor. Break script, run manually. else if (matchLocation(host.vibix)) { // TODO Find a way to add download button overrideProperty(win, 'DownloadVideo', { get: () => (file) => win.DownloadVideo_(file) }); await waitForEvent(document, 'DOMContentLoaded'); const script = await waitFor(() => el.all.tag.script.filter(s => s.innerText.includes("DownloadVideo"))[0], 10000); if (script == null) return console.error("player script not found"); overrideFunction(win, 'Playerjs', (Playerjs, opts) => new Playerjs(assignDeep(opts, configPlayerJS))); win.eval(script.innerText.replace("DownloadVideo", "DownloadVideo_")); fixPlayerJS(win.player); } // VideoCDN/Lumex: VideoJS // Configs provided as HTML elements, player created in external script. Wait for variables. else if (matchLocation(host.videocdn)) { await waitForEvent(document, 'DOMContentLoaded'); const videojs = await waitFor(() => win.videojs, 10000); videojs.deregisterPlugin('vast'); const player = win.player = await waitFor(() => videojs.getAllPlayers()[0], 10000); player.activePlugins_.vast = false; player.playbackRates(playSpeeds); // TODO figure out why setting playbackRates doesn't work const options = win.options = player.options({ playbackRates: playSpeeds }); } // VideoSeed: PlayerJS // Configs passed to Playerjs constructor. else if (matchLocation(host.videoseed)) { overrideFunction(win, 'Playerjs', (Playerjs, opts) => new Playerjs(assignDeep(opts, configPlayerJS))); await waitFor(() => win.player, 10000); fixPlayerJS(win.player); } // Rezka: PlayerJS // Configs provided as `var` variables. Override assignments. else if (matchLocation(host.hdrezka)) { overrideProperty(win, 'CDNPlayerInfo', v => assignDeep(v, { preroll: "", midroll: "[]" })); } // Unknown domain // TODO: Try guessing provider and/or read ReYohoho's config. else { console.warn("Unexpected domain"); } })();