Greasy Fork is available in English.

ReYohoho – No Ads + Enhancements [Ath]

Removes video ads from online streaming services in ReYohoho (Rezka, Alloha, Collaps, VideoCDN etc.). Also applies minor enhancements, if possible (extra play speeds etc.).

  1. // ==UserScript==
  2. // @name ReYohoho – No Ads + Enhancements [Ath]
  3. // @name:ru ReYohoho – Без Рекламы + Улучшения [Ath]
  4. // @name:uk ReYohoho – Без Реклами + Покращення [Ath]
  5. // @name:be ReYohoho – Без Рэкламы + Паляпшэнні [Ath]
  6. // @name:bg ReYohoho – Без Реклами + Подобрения [Ath]
  7. // @name:tt ReYohoho – Рекламасыз + Яхшыртулар [Ath]
  8. // @name:sl ReYohoho – Brez Oglasov + Izboljšave [Ath]
  9. // @name:sr ReYohoho – Bez Reklama + Poboljšanja [Ath]
  10. // @name:ka ReYohoho – რეკლამის გარეშე + გაუმჯობესებები [Ath]
  11. // @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.).
  12. // @description:ru Убирает рекламные ролики онлайн-кинотеатров в ReYohoho (Rezka, Alloha, Collaps, VideoCDN и т.д.). Также применяет небольшие улучшения, если возможно (дополнительные скорости проигрывания и т.п.).
  13. // @description:uk Видаляє рекламні ролики з онлайн-сервісів для перегляду відео у ReYohoho (Rezka, Alloha, Collaps, VideoCDN тощо). Також застосовує додаткові покращення, якщо це можливо (додаткові швидкості відтворення тощо).
  14. // @description:be Выдаляе рэкламныя ролікі з анлайн-стрымінгавых паслуг у ReYohoho (Rezka, Alloha, Collaps, VideoCDN і г.д.). Таксама ўжывае дробныя паляпшэнні, калі гэта магчыма (дадатковыя хуткасці прайгравання і г.д.).
  15. // @description:bg Премахва видео рекламите от онлайн стрийминг услугите в ReYohoho (Rezka, Alloha, Collaps, VideoCDN и т.н.). Така също прилага малки подобрения, ако е възможно (допълнителни скорости на възпроизвеждане и т.н.).
  16. // @description:tt Онлайн-трансляция хезмәтләрендәге ReYohoho (Rezka, Alloha, Collaps, VideoCDN һ.б.) видео рекламаларны бетерә. Шулай ук мөмкин булганда кечкенә яхшыртулар кертә (өстәмә уйнату тизлекләре һ.б.).
  17. // @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.).
  18. // @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.).
  19. // @description:ka ამოიღებს ვიდეო რეკლამებს ონლაინ სტრიმინგის სერვისებიდან ReYohoho-ში (Rezka, Alloha, Collaps, VideoCDN და ა.შ.). ასევე იღებს პატარა გაუმჯობესებებს, თუ ეს შესაძლებელია (დამატებითი დაკვრის სიჩქარეები და ა.შ.).
  20. // @namespace athari
  21. // @author Athari (https://github.com/Athari)
  22. // @copyright © Prokhorov ‘Athari’ Alexander, 2024–2025
  23. // @license MIT
  24. // @homepageURL https://github.com/Athari/AthariUserJS
  25. // @supportURL https://github.com/Athari/AthariUserJS/issues
  26. // @version 1.0.1
  27. // @icon https://reyohoho.github.io/reyohoho/icons/favicon-32x32.png
  28. // @match https://*.allarknow.online/*
  29. // @match https://*.obrut.show/*
  30. // @match https://*.embess.ws/*
  31. // @match https://*.fotpro135alto.com/*
  32. // @match https://*.videoframe1.com/*
  33. // @match https://*.videoframe*.com/*
  34. // @match https://*.lumex.space/*
  35. // @match https://*.tv-2-kinoserial.net/*
  36. // @match https://*-kinoserial.net/*
  37. // @match https://*.rezka.*/*
  38. // @match https://*.hdrezka.*/*
  39. // @match https://*.kinopub.*/*
  40. // @match https://*.rezkify.*/*
  41. // @match https://*.rezkery.*/*
  42. // @grant unsafeWindow
  43. // @run-at document-start
  44. // @sandbox raw
  45. // @require https://cdn.jsdelivr.net/npm/@athari/monkeyutils@0.4.3/monkeyutils.u.min.js
  46. // @resource script-urlpattern https://cdn.jsdelivr.net/npm/urlpattern-polyfill/dist/urlpattern.js
  47. // @tag athari
  48. // ==/UserScript==
  49.  
  50. (async () => {
  51. 'use strict'
  52.  
  53. const { isFunction, isObject, isString, assignDeep,
  54. waitForCallback, waitForEvent, waitFor, withTimeout,
  55. matchLocation,
  56. throwError, attempt,
  57. overrideProperty, overrideFunction,
  58. ress, scripts, els, opts, } =
  59. //require("../@athari-monkeyutils/monkeyutils.u"); // TODO
  60. athari.monkeyutils;
  61.  
  62. const win = unsafeWindow;
  63. const res = ress(), script = scripts(res);
  64. const el = els(document);
  65.  
  66. Object.assign(globalThis, globalThis.URLPattern ? null : await script.urlpattern);
  67.  
  68. const anySubdomain = "(.*\\.)?";
  69. const globMap = { ".": "\\.", "*": "[^\\.]+" };
  70. const globDomain = (s) => s.replace(/\.|\*/g, ([m]) => globMap[m] ?? m);
  71. const oneOfDomains = (...ds) => `(${ds.map(globDomain).join("|")})`;
  72. const host = {
  73. alloha: `${anySubdomain}${oneOfDomains("allarknow.online")}`,
  74. collaps: `${anySubdomain}${oneOfDomains("embess.ws")}`,
  75. hdvb: `${anySubdomain}${oneOfDomains("fotpro135alto.com")}`,
  76. turbo: `${anySubdomain}${oneOfDomains("obrut.show")}`,
  77. vibix: `${anySubdomain}${oneOfDomains("videoframe*.com")}`,
  78. videocdn: `${anySubdomain}${oneOfDomains("lumex.space")}`,
  79. videoseed: `${anySubdomain}${oneOfDomains("*-kinoserial.net")}`,
  80. hdrezka: `${anySubdomain}${oneOfDomains("rezka.*", "hdrezka.*", "kinopub.*", "rezkify.*", "rezkery.*")}`,
  81. };
  82.  
  83. const loggingProxy = (o, level = 0, root = null, path = []) => new Proxy(o, new class {
  84. construct() {
  85. console.log("proxy", { o, level, root, path });
  86. }
  87. #proxies = {}
  88. #logProp(act, t, prop, value, args = []) {
  89. console.log(act, "{", root, "}", `${path.join(".")}.{`, t, `}.${prop} = `, value, ` (${typeof value})`, args);
  90. }
  91. get(t, prop) {
  92. let proxy = this.#proxies[prop];
  93. if (proxy != null) {
  94. this.#logProp("get", t, prop, proxy.value);
  95. return proxy.proxy;
  96. }
  97. const value = Reflect.get(t, prop);
  98. this.#logProp("get", t, prop, value);
  99. if (level > 1 && (isObject(value) || isFunction(value))) {
  100. proxy = { value, proxy: loggingProxy(value, level - 1, root ?? value, path.concat(prop)) };
  101. this.#proxies[prop] = proxy;
  102. return proxy.proxy;
  103. } else {
  104. return value;
  105. }
  106. }
  107. set(t, prop, value) {
  108. this.#logProp("set", t, prop, value);
  109. return Reflect.set(t, prop, value);
  110. }
  111. apply(t, self, args) {
  112. const value = Reflect.apply(t, self, args);
  113. this.#logProp("fun", t, "()", value, args);
  114. return value;
  115. }
  116. construct(t, args) {
  117. const value = Reflect.construct(t, args);
  118. this.#logProp("new", t, "new()", value, args);
  119. return value;
  120. }
  121. });
  122.  
  123. const playSpeeds = [ 0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3, 3.5, 4 ];
  124.  
  125. const configPlayerJS = {
  126. settings: {
  127. customspeeds: 1, speeds: playSpeeds.join(","),
  128. },
  129. volume: 1,
  130. postmessage: 1, log: 1, eventstracker: 1, // logging & messaging
  131. vast: 0, vast_timeout: 0, vast_volume: 0, // ad config
  132. preroll: "", prerolls: 0, midroll: [], midrolls: 0, // ad urls
  133. yamtr: 0, // counters
  134. };
  135.  
  136. const fixPlayerJS = (player) => {
  137. if (player == null)
  138. return console.error("playerjs not found");
  139. player.api('update:vast', false);
  140. player.api('log', true);
  141. player.api('volume', 1);
  142. const options = overrideFunction(win.console, 'log', null, (_, v) => v, () => player.api('options'));
  143. if (options == null)
  144. return console.error("playerjs options not found");
  145. const elPlayer = document.querySelector(`#${options.id}`);
  146. assignDeep(win, { player, options });
  147. console.info({ player, options, el: elPlayer });
  148. assignDeep(options, configPlayerJS, {
  149. events: options.events || /*'onPlayerJSEvent'*/'PlayerjsEvents',
  150. });
  151. if (elPlayer)
  152. elPlayer.oncontextmenu = null;
  153. if (isString(options.events)) {
  154. const originalEvents = options.events; // gets reset for some reason
  155. overrideFunction(win, options.events, (onPlayerJSEvent, event, playerId, data) => {
  156. options.events = originalEvents;
  157. const ignore = event.startsWith('vast_');
  158. if (![ 'time' ].includes(event))
  159. console.info(ignore ? "event nay" : "event yay", { e: event, data, id: playerId });
  160. if (ignore)
  161. return;
  162. (onPlayerJSEvent ?? win.PlayerjsEvents)?.(event, playerId, data);
  163. })
  164. }
  165. };
  166.  
  167. const fixPlyrConfig = (player) =>
  168. assignDeep(player, {
  169. controls: [
  170. 'play-large',
  171. 'play', 'rewind', 'fast-forward', 'progress', 'current-time', 'duration',
  172. 'mute', 'volume',
  173. 'captions', 'settings', 'pip', 'airplay', 'fullscreen',
  174. ],
  175. ads: { enabled: false, midroll: [], preroll: "" },
  176. speed: { options: playSpeeds },
  177. settings: [ 'quality', 'audio', 'captions', 'speed', 'loop', 'scale', 'bugReport' ],
  178. volume: 1,
  179. disableContextMenu: false,
  180. debug: false,
  181. });
  182.  
  183. console.info("reyohoho no ads", location.href);
  184.  
  185. // Alloha: Plyr
  186. // Configs parsed from JSON strings
  187. if (matchLocation(host.alloha)) {
  188. overrideFunction(win.JSON, 'parse', (parse, text) => {
  189. const json = parse(text);
  190. if (json.ads && json.controls) {
  191. console.info("plyr config original", structuredClone(json));
  192. fixPlyrConfig(json);
  193. console.info("plyr config modified", structuredClone(json));
  194. } else if (json.active && json.all) {
  195. console.info("fileList original", json);
  196. } else {
  197. console.debug("parsed json", json);
  198. }
  199. return json;
  200. });
  201. }
  202. // Collaps: VenomPlayer
  203. // Configs passed to makePlayer wrapper function. Override function after var assignments.
  204. else if (matchLocation(host.collaps)) {
  205. const adTimeouts = { loading: 0, starting: 0, toNextImp: 0, global: 0 };
  206. overrideProperty(win, 'adTimeouts', { log: true, set: v => assignDeep(v, adTimeouts) });
  207. const adCfg = { maxImpressions: 0, urls: [], exitFullscreenVideo: false, vast: { timeouts: adTimeouts } };
  208. overrideProperty(win, 'adsConfig', { log: true, set: v => assignDeep(v, { volume: 0, pre: adCfg, middle: adCfg, post: adCfg }) });
  209. const modifiedOpts = { speed: playSpeeds, theme: 'modern', /* venom/classic/metro/modern */ };
  210. overrideProperty(win, 'middleCount', _ => {
  211. overrideFunction(win, 'makePlayer', (makePlayer, opts) =>
  212. makePlayer(new Proxy(assignDeep(opts, modifiedOpts), new class {
  213. set(t, prop, v) {
  214. return Object.hasOwn(modifiedOpts, prop) ? true : Reflect.set(t, prop, v);
  215. }
  216. })));
  217. return 0;
  218. });
  219. }
  220. // HDVB: PlayerJS
  221. // Configs provided as `let` variables. Private "rek" ads config. Break script, run manually.
  222. else if (matchLocation(host.hdvb)) {
  223. let fail = true;
  224. const tag = { conf: { banner_show: false }, key: "", script: "" };
  225. const banner = { show: false, key: "", script: "" };
  226. const roll = { time: "", url: "" };
  227. overrideProperty(win, 'playerConfigs', {
  228. log: "player config",
  229. set: v => fail ? throwError("nope") : assignDeep(v, configPlayerJS, {
  230. events: v.events || 'onPlayerJSEvent',
  231. rek: {
  232. endtag: tag, starttag: tag, start2tag: tag, start3tag: tag,
  233. pausebanner: banner,
  234. push_roll: roll,
  235. midroll: [], preroll: [], pushbanner: [],
  236. },
  237. }),
  238. });
  239. const script = await waitFor(() => el.all.tag.script.filter(s => s.innerText.includes("let playerConfigs"))[0], 10000);
  240. if (script == null)
  241. return console.error("player script not found");
  242. fail = false;
  243. win.eval(script.innerText.replace("let playerConfigs", "playerConfigs"));
  244. fixPlayerJS(win.player);
  245. }
  246. // Turbo: PlayerJS
  247. // Config provided as encrypted string passed to global Player function. Wait for pljssglobal[0] assignment.
  248. else if (matchLocation(host.turbo)) {
  249. //overrideProperty(win, 'pljssglobal', v => loggingProxy(v, 3));
  250. let [ player0SetWait, player0Set ] = waitForCallback();
  251. overrideProperty(win, 'pljssglobal', v => new Proxy(v, new class {
  252. set(t, prop, value) {
  253. if (prop == "0")
  254. player0Set(value);
  255. return Reflect.set(t, prop, value);
  256. }
  257. }));
  258. const player = win.player = win.pljssglobal?.[0] ?? await withTimeout(player0SetWait, 10000);
  259. fixPlayerJS(player);
  260. }
  261. // TODO Vibix: PlayerJS
  262. // Configs provided as constants and passed to Playerjs constructor. Break script, run manually.
  263. else if (matchLocation(host.vibix)) {
  264. // TODO Find a way to add download button
  265. overrideProperty(win, 'DownloadVideo', { get: () => (file) => win.DownloadVideo_(file) });
  266. await waitForEvent(document, 'DOMContentLoaded');
  267. const script = await waitFor(() => el.all.tag.script.filter(s => s.innerText.includes("DownloadVideo"))[0], 10000);
  268. if (script == null)
  269. return console.error("player script not found");
  270. overrideFunction(win, 'Playerjs', (Playerjs, opts) => new Playerjs(assignDeep(opts, configPlayerJS)));
  271. win.eval(script.innerText.replace("DownloadVideo", "DownloadVideo_"));
  272. fixPlayerJS(win.player);
  273. }
  274. // VideoCDN/Lumex: VideoJS
  275. // Configs provided as HTML elements, player created in external script. Wait for variables.
  276. else if (matchLocation(host.videocdn)) {
  277. await waitForEvent(document, 'DOMContentLoaded');
  278. const videojs = await waitFor(() => win.videojs, 10000);
  279. videojs.deregisterPlugin('vast');
  280. const player = win.player = await waitFor(() => videojs.getAllPlayers()[0], 10000);
  281. player.activePlugins_.vast = false;
  282. player.playbackRates(playSpeeds); // TODO figure out why setting playbackRates doesn't work
  283. const options = win.options = player.options({ playbackRates: playSpeeds });
  284. }
  285. // VideoSeed: PlayerJS
  286. // Configs passed to Playerjs constructor.
  287. else if (matchLocation(host.videoseed)) {
  288. overrideFunction(win, 'Playerjs', (Playerjs, opts) => new Playerjs(assignDeep(opts, configPlayerJS)));
  289. await waitFor(() => win.player, 10000);
  290. fixPlayerJS(win.player);
  291. }
  292. // Rezka: PlayerJS
  293. // Configs provided as `var` variables. Override assignments.
  294. else if (matchLocation(host.hdrezka)) {
  295. overrideProperty(win, 'CDNPlayerInfo', v => assignDeep(v, { preroll: "", midroll: "[]" }));
  296. }
  297. // Unknown domain
  298. // TODO: Try guessing provider and/or read ReYohoho's config.
  299. else {
  300. console.warn("Unexpected domain");
  301. }
  302. })();