Sleazy Fork is available in English.

ebalka improved

Infinite scroll. Filter by duration, include/exclude phrases

  1. // ==UserScript==
  2. // @name ebalka improved
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0.9
  5. // @license MIT
  6. // @description Infinite scroll. Filter by duration, include/exclude phrases
  7. // @author smartacephale
  8. // @supportURL https://github.com/smartacephale/sleazy-fork
  9. // @match https://*ebalka.*.*/*
  10. // @match https://*.ebalk*.*/*
  11. // @grant GM_addStyle
  12. // @require https://cdn.jsdelivr.net/npm/billy-herrington-utils@1.1.8/dist/billy-herrington-utils.umd.js
  13. // @require https://cdn.jsdelivr.net/npm/jabroni-outfit@1.4.9/dist/jabroni-outfit.umd.js
  14. // @require https://update.greasyfork.org/scripts/494204/data-manager.user.js?version=1458190
  15. // @require https://update.greasyfork.org/scripts/494205/pagination-manager.user.js?version=1459738
  16. // @run-at document-idle
  17. // @icon https://www.google.com/s2/favicons?sz=64&domain=wwwa.ebalka.link
  18. // ==/UserScript==
  19. /* globals DataManager PaginationManager */
  20.  
  21. Object.assign(unsafeWindow, { bhutils: window.bhutils });
  22. const { JabroniOutfitStore, defaultStateWithDuration, JabroniOutfitUI, DefaultScheme } = window.jabronioutfit;
  23.  
  24. const LOGO = `
  25. ⣿⣿⣿⣿⣿⣿⢿⣻⣟⣿⣻⣽⣟⡿⣯⣟⣯⡿⣽⣻⣽⢽⢯⡿⡽⡯⣿⢽⣻⢽⡽⣽⣻⣿⣿⢿⣟⣿⣟⣿⣿⢿⣿⣿⣟⣿⣻⣿⣿⢿⣿⢿⡿⣿⢿⡿⣿⢿⣟⣿
  26. ⣿⣿⣿⣿⡿⣾⡿⣿⣽⣯⡿⣷⣟⣟⣷⣻⢾⣽⢽⣳⢿⢽⢯⢿⢽⢯⢿⡽⣞⣟⣞⣷⣿⣿⣾⢿⢿⢿⡽⣟⣿⣟⣿⡷⣿⣽⣟⣿⣽⣿⣻⣟⡿⣯⣿⣻⣿⣻⣯⣿
  27. ⣿⣿⣯⣿⣟⣿⣻⣯⣷⣿⣻⡷⣯⢷⣯⣯⢿⣺⢿⣽⣻⡽⣯⢿⢽⣻⣯⣿⢿⢾⡻⡫⡳⡱⡱⢱⠱⡱⡙⡜⡕⡯⡻⣻⣽⣾⣟⣷⡿⣯⣿⣽⣿⢿⣾⢿⣾⢿⣟⣯
  28. ⣿⣿⣻⣽⣿⡯⣿⢾⡷⣯⡷⣟⣯⣿⣳⣿⣻⡽⣟⡗⡟⡟⡽⡻⣛⢯⢻⢪⢳⢱⠱⡑⠕⡌⢌⠢⡑⢌⠢⡑⢌⢊⠎⡎⢮⢪⠯⣷⢿⣻⣽⣾⣻⣯⡿⣯⣯⢿⣽⣟
  29. ⣿⣿⢿⣿⣿⡽⣽⣯⢿⡽⣯⣿⢿⡽⡯⡳⣓⢝⠜⡜⢜⠸⡨⡊⢆⢣⠱⡱⡱⡡⡃⢎⠪⢐⠡⠊⠄⠅⠢⡈⡂⠅⡊⢌⢊⠎⡕⡕⡏⡿⣽⣾⢿⡷⣿⣻⢾⣟⣯⡿
  30. ⣿⣿⣿⡿⣾⢯⣗⣿⣽⢿⣻⢝⡗⡝⡎⡎⢆⠣⡑⢌⠢⢑⠐⢌⠢⠡⠣⡑⡱⡐⢌⠢⠡⡁⡊⠌⡨⢈⠂⢂⠂⠡⠨⢐⠐⠅⢕⢱⢱⢹⢸⡫⣿⣻⣗⣿⣟⣯⣯⢿
  31. ⣿⣿⣾⣿⡿⣽⣞⣿⣞⢟⡎⡗⡝⡜⢌⢊⠢⢑⠨⢐⠨⠠⠑⠄⠅⠕⡡⢑⢌⢌⠢⡑⠡⢂⠂⠅⡐⠠⠈⠄⡈⡈⢐⠐⠨⢈⠢⢑⠌⢎⢎⢎⢏⣷⣿⡷⣟⣷⣟⡿
  32. ⣿⣷⣿⣷⡿⣷⡿⡳⡕⣇⢏⢎⢎⢌⠢⠡⢊⢐⠨⠐⡈⠄⠅⠅⠅⠅⡂⢅⠢⡂⢕⠨⠨⢐⠨⠐⠠⢈⠐⠀⠄⠠⠀⠂⢁⠐⢐⠠⡁⡣⢊⠎⡎⡎⣿⣟⣿⡷⣟⣿
  33. ⣿⣿⣷⣿⣿⢿⡹⡱⡕⡕⡕⢕⠔⡂⢅⢑⠐⡀⡂⠡⠐⡈⠄⠡⡈⡂⠌⡐⡰⠨⢢⢡⢑⢐⠠⠁⠅⠠⠀⢁⠀⠂⠈⠀⠂⢈⠀⡂⠐⠄⢅⠣⡑⢕⢕⡻⣗⣿⣻⡷
  34. ⣿⣿⣯⢏⢾⢳⢱⢱⢱⢱⢑⠕⡨⢐⢐⠐⡈⠄⠄⡁⢂⠐⢈⠐⡀⢂⠅⡂⡪⡈⡪⡂⢎⡐⡨⠀⠅⠐⠈⠀⠀⠐⠈⠀⠁⠠⠀⠄⠡⠑⡐⡑⡘⠔⢅⢝⢽⣽⣯⢿
  35. ⣿⣷⡿⣵⢫⢪⢪⢪⠪⡢⡃⢅⠢⢁⠂⡂⠂⢂⠐⡀⠄⠂⡀⢂⠐⡐⢐⠌⡆⡕⡔⡕⡕⡌⡢⠡⠈⠄⠂⠀⠂⠀⠀⠀⠐⠀⠂⠈⠄⡁⡂⠢⠡⡑⢅⠪⡪⣿⢾⣟
  36. ⣿⣗⢭⢺⢸⢘⢔⢕⠱⡨⢌⠢⡁⡂⠅⠠⢁⠐⠀⠄⠐⠀⠄⠐⢀⠂⡡⢱⢱⢱⣏⣟⡎⡎⡢⢁⠅⠐⠀⠠⠀⠀⠀⠀⠂⠀⡈⢀⠂⡐⠠⠡⡑⢄⢑⠌⡪⣺⣟⢏
  37. ⣿⣯⢮⢣⠱⡑⡌⢆⠣⡊⡢⢁⠢⠠⠁⠅⠐⢈⠀⠂⠈⠀⠄⠁⠄⠂⠂⢕⠱⡹⡺⠾⡱⡑⢌⢐⠠⠁⡈⠀⠀⠀⠀⠀⢀⠀⠄⠠⠀⡂⠡⡁⡂⡂⡂⡑⢌⠮⢏⠢
  38. ⣿⡻⡪⡢⠣⡑⢜⠨⡊⡢⢊⢐⠌⡐⠡⢈⠐⠀⠄⠂⠈⠀⠀⠂⠐⢈⠨⠠⢑⠡⠣⠩⠨⢂⠅⡢⠨⠀⠄⠐⠀⠀⠂⠀⠀⡀⠄⠂⡐⠠⡁⡂⠔⢐⢐⠨⠢⡹⡐⢅
  39. ⣿⣞⢜⢌⠪⡘⢔⢑⠌⡂⡢⢂⠢⠨⢐⠠⠈⠄⠂⠀⠄⠈⠀⢈⠠⠀⢂⢁⠢⡑⡡⢑⢑⢔⠡⢂⠅⠅⠌⡀⠂⠠⠀⠐⠀⡀⠄⠂⠄⠅⡂⡐⠨⢐⢐⠨⡊⣎⠪⡢
  40. ⣿⡧⡣⡢⠣⡡⡑⢔⠡⢊⢐⠐⠌⠌⠄⢂⠡⠐⠀⡁⠀⠠⠈⠀⡀⠐⡀⢂⢂⢂⠢⢡⢑⠢⡁⡂⡊⠌⠔⡠⢈⠀⠂⡁⠄⡀⢂⠡⢁⢂⠂⠄⠅⡂⡂⠪⡰⡱⢑⢌
  41. ⣿⣯⢪⢸⠨⡢⢊⠢⡑⡐⡐⠡⠡⢁⠅⡂⠂⠌⠠⠀⠌⠀⠄⠂⡀⢂⢐⢐⢐⠄⢅⠅⡕⡑⢔⠐⢌⠌⡊⡐⡐⠨⠐⡀⢂⢐⠐⡈⡐⡀⡊⠨⢐⠐⢌⢊⢆⢇⢕⠢
  42. ⣿⣿⢕⢕⢱⢘⠔⡡⢂⠢⠊⠌⢌⢐⠐⠄⠅⠅⡡⢈⠐⠐⠐⡀⡂⠔⡐⡐⠅⢌⠢⡑⡸⣈⠢⡡⡑⡌⠔⡐⠨⠠⢁⠐⡀⢂⠂⡂⡐⡀⡂⠅⡂⡑⢔⢑⢜⢌⠢⣑
  43. ⣿⣿⣗⡕⡕⢜⢌⠢⡑⠌⢌⢊⢐⢐⠨⠨⡈⠢⢐⢐⠨⠨⠐⠄⡂⠅⢆⠪⡸⡐⡑⢌⢌⠆⢕⢰⢨⠢⢑⠨⢈⠐⡀⢂⠐⡀⢂⢐⢀⠂⠄⠅⡂⡪⡐⡅⡇⣕⣼⡺
  44. ⣿⣯⡿⣮⢪⢪⢂⢇⠪⡈⡂⡂⡂⠢⠨⢐⠈⢌⢐⢐⠨⠨⠨⠨⠠⡑⡐⢅⢂⠣⡪⡢⡱⡸⡨⡢⡣⢊⢐⠈⠄⢂⠐⡀⢂⠐⡀⡂⡐⠨⠨⠨⡂⡢⢪⠸⣜⢮⢖⣝
  45. ⣿⣷⢿⣟⣧⡣⡣⡢⡃⡪⢐⢐⠌⠌⡨⢐⠨⢐⠠⠂⠌⠄⠅⡡⠡⠐⡐⡐⡐⢅⠪⣎⢮⢮⣫⢿⡘⢔⠠⡁⢊⢀⠂⡐⠠⠨⢀⢂⠂⠅⢕⠡⢒⠌⡆⡯⢮⡳⡝⡮
  46. ⣿⡿⣯⣿⣽⣷⡕⡕⡌⡪⡐⠅⢌⢂⢂⠢⠨⢐⠠⠡⢁⢊⠐⡀⡂⠅⡐⡐⠄⡑⢜⡺⡝⣗⡏⠎⡎⡆⢅⠂⢅⠐⡐⠠⢁⠊⠄⡂⠅⠕⡁⣊⠢⡣⣱⢽⡳⡽⡺⣝
  47. ⣿⣿⣯⣷⡿⣾⢿⣮⢪⢢⠱⡑⢅⠢⡂⠅⢅⠢⠨⢈⢐⢀⢂⢂⢐⢐⠠⠂⠅⡊⡢⡫⣪⠯⡊⢌⠪⡪⡂⢕⠠⡁⡂⠅⡂⠅⠅⡢⠡⡑⢌⠢⡣⡱⣕⢗⣝⢮⡫⡮
  48. ⣿⣷⢿⣷⢿⣿⣻⣯⡷⣇⢇⡣⢅⠕⢌⢌⠢⠨⠨⡐⡐⡐⡐⠄⡂⠔⡈⠌⢌⠢⡣⣣⢏⠪⡐⠅⡅⡕⡕⡅⢆⠢⢂⠅⡢⠡⡑⡐⢅⠪⡘⡜⡜⡮⣎⢗⣕⢧⢳⡹
  49. ⣿⣟⣿⣟⣿⣽⢿⣾⣻⡿⡑⡕⣕⢱⠡⡢⢑⠅⢕⢐⢐⠔⡐⡁⡂⠅⡂⢅⢅⢕⢽⢱⠡⡃⡪⡨⢢⢱⡹⡸⡐⡅⠕⢌⠢⡑⡰⠨⡂⢕⢱⡸⡺⡜⡮⣳⡱⣝⢵⡹
  50. ⣿⣿⣽⣿⣽⢿⣻⣷⢿⡑⠔⢅⢕⢕⢕⢜⢐⠅⢕⢐⠔⡐⠔⡐⠌⢌⠢⢑⠔⡕⡕⢕⢑⢌⠢⡊⢆⠣⡣⡫⡪⡪⡊⢆⢕⠰⡨⠢⡑⡕⡵⣹⢪⡳⣝⢮⢺⢜⢮⡪
  51. ⣿⣟⣷⢿⣾⢿⣿⣽⡏⡎⠜⢌⢢⢱⢱⢱⡑⡕⢅⠢⡊⡐⠅⡌⢌⠢⡊⡢⢣⢣⠪⡊⢆⢅⠣⡊⢌⠪⡘⢜⢜⠜⡜⢔⢅⠕⡜⡸⡸⡸⡺⣜⣕⢯⢺⡪⣳⢹⡪⡺
  52. ⣿⣿⣽⣿⣽⡿⣿⢾⠣⡊⡪⡊⢎⢌⢢⢑⢕⢕⢕⢱⢨⠨⡊⢔⢡⠱⡨⡪⡪⡪⡪⡘⢔⢡⢑⢌⢢⢣⡪⡘⡜⡜⡸⡐⡅⡣⡱⡡⡳⣙⢞⢼⡸⣕⢗⢽⡸⡵⣹⢹
  53. ⣿⣾⣷⡿⣾⡿⣿⡏⡎⡪⡘⢌⠢⡢⡑⡕⠔⢕⢕⢕⢜⢌⢎⢢⠱⡑⡕⢜⢜⢜⡢⡣⡣⡪⡂⣎⢍⢗⢷⡐⡌⢎⢆⠕⢜⢌⢎⢎⢎⢎⢗⢧⡫⣎⢗⡵⣝⢞⢼⡱
  54. ⣿⣿⣷⣿⡿⣿⡿⣗⡑⡌⡊⡆⢕⠌⢆⠪⡊⡆⢕⢕⢕⢕⢕⢱⢱⢑⠕⡕⢕⢕⢬⡣⢎⠎⡎⡆⡇⣯⣻⣿⣮⡆⡧⡹⢰⠱⡸⡘⡌⢎⢪⢣⢯⢎⣗⣝⡎⣗⢧⡫
  55. ⣿⣿⣷⣿⢿⣿⣿⢣⠪⡪⢪⠸⡰⡑⡕⡱⡑⡌⢆⢕⢧⢣⢣⢣⢣⢣⠣⡣⡣⣳⣵⢱⢱⣱⣱⢜⣵⣿⣽⣿⣿⣿⡜⡜⡸⡘⠔⢅⢊⢢⢑⢅⠗⣗⢵⢺⡺⣪⢞⢮
  56. ⣿⡻⣷⣿⣿⣿⣽⡪⡊⡎⡪⡊⢆⢣⢊⠆⡕⢌⢆⢕⢧⢣⢣⠣⡣⠣⡣⡣⡣⣻⣿⣷⣿⣿⣿⣷⣿⣿⣯⣷⣿⣿⣿⣜⢔⢌⢕⠡⢊⠔⢌⢂⢇⠳⣝⣗⢯⡺⣕⣗
  57. ⣿⣝⢞⢿⣻⣾⡿⣇⢣⢣⢑⢎⢪⠢⡣⡱⡸⡨⡢⡃⡗⡕⡕⢕⡑⡅⡣⡱⡸⣸⣿⣿⣿⣿⣾⣿⣻⣾⣿⣻⣿⣽⣿⣿⣎⠢⡢⢑⠔⡨⢂⠕⡰⢩⢚⡮⣳⢝⣞⢮
  58. ⣿⣞⢽⢵⢝⡯⡽⣸⡪⡢⡣⡣⡱⡑⡜⢔⢱⠨⡢⡊⡞⡜⢜⠔⡜⣐⠱⡨⢪⠪⣿⣿⣿⣿⣿⣻⣿⣿⣿⣿⣿⣿⡿⣳⣳⢕⢌⠢⡊⡐⡐⡡⢊⠢⡃⢯⣳⣻⡺⡵
  59. ⣿⣞⢽⣕⢯⣺⢝⣿⡎⡎⡎⢆⢇⢎⢪⢊⢆⢣⢒⠜⣎⢎⢎⠪⡢⡂⡣⢊⠆⡝⢼⣫⡯⡯⣟⣿⢷⣿⣯⣿⢟⣗⢯⢞⡾⣇⢇⢕⢐⠅⡂⡊⠔⡑⠜⡜⣺⣺⡺⡽
  60. ⣿⣞⢗⣗⢯⣞⢽⡺⣝⡜⡜⡜⡜⡜⡜⡔⡕⢕⢱⣑⡧⡣⡊⢎⠔⢌⠢⡡⢱⠸⡸⣺⡽⣽⣳⢽⢽⣺⢝⣮⣳⢳⡫⣗⡿⡽⡪⡢⡑⢌⢂⠪⠨⡨⠪⡸⢸⢮⢯⢯`;
  61.  
  62. class EBALKA_RULES {
  63. constructor() {
  64. this.PAGINATION = Array.from(document.querySelectorAll('.pagination')).pop();
  65. const dataparams = bhutils.parseDataParams(document.querySelector('.pagination__item.last').getAttribute('data-parameters'));
  66. const lastfrom = dataparams[Object.keys(dataparams).filter(k => k.includes('from'))?.pop()];
  67. this.PAGINATION_LAST = parseInt(lastfrom) || 1;
  68. this.CONTAINER = document.querySelector('.content__video');
  69. this.HAS_VIDEOS = !!document.querySelector('.card_video');
  70. }
  71.  
  72. GET_THUMBS(html) { return html.querySelectorAll('.card_video'); }
  73.  
  74. THUMB_IMG_DATA() { return ({}); };
  75.  
  76. THUMB_URL(thumb) { return thumb.querySelector('.root__link').href; }
  77.  
  78. THUMB_DATA(thumb) {
  79. const title = bhutils.sanitizeStr(thumb.querySelector('.card__title').innerText);
  80. const duration = bhutils.timeToSeconds(Array.from(thumb.querySelector('.card__spot').children).pop().innerText);
  81. return { title, duration }
  82. }
  83.  
  84. URL_DATA() {
  85. const url = new URL(window.location.href);
  86. const offset = parseInt(document.querySelector('.pagination__item_active,input.pagination__item').innerText) || 1;
  87. const el = document.querySelector('.pagination__item.next');
  88.  
  89. const attrs = {
  90. mode: 'async',
  91. function: 'get_block',
  92. block_id: el?.getAttribute('data-block-id'),
  93. ...bhutils.parseDataParams(el?.getAttribute('data-parameters'))
  94. };
  95.  
  96. Object.keys(attrs).forEach(k => url.searchParams.set(k, attrs[k]));
  97.  
  98. const iteratable_url = n => {
  99. Object.keys(attrs).forEach(k => k.includes('from') && url.searchParams.set(k, n));
  100. url.searchParams.set('_', Date.now());
  101. return url.href;
  102. }
  103.  
  104. return { offset, iteratable_url }
  105. }
  106. }
  107.  
  108. const RULES = new EBALKA_RULES();
  109.  
  110. //====================================================================================================
  111.  
  112. function animateThumb(thumb) {
  113. const el = thumb.querySelector('.card__thumb_video');
  114. const src = el.querySelector('.card__image').getAttribute('data-preview');
  115.  
  116. el.classList.add('video-on');
  117.  
  118. const videoElem = bhutils.parseDom(`<video style="position: absolute; left: 0px; top: 0px; width: 330px; height: 187px; visibility: visible; margin-top: -1px;"
  119. autoplay="" loop="" playsinline="true" webkit-playsinline="true" src="${src}"></video>`);
  120. el.appendChild(videoElem);
  121.  
  122. return {
  123. removeElem: () => {
  124. el.classList.remove('video-on');
  125. videoElem.remove();
  126. }
  127. }
  128. }
  129.  
  130. function animate() {
  131. function handleThumbHover(e) {
  132. if (e.target.tagName !== 'IMG') return;
  133. const thumb = e.target.parentElement.parentElement.parentElement;
  134. const { removeElem } = animateThumb(thumb);
  135. thumb.addEventListener('mouseleave', removeElem, { once: true });
  136. }
  137.  
  138. RULES.CONTAINER.addEventListener('mouseover', handleThumbHover);
  139. }
  140.  
  141. //====================================================================================================
  142.  
  143. function route() {
  144. if (RULES.PAGINATION) {
  145. new PaginationManager(state, stateLocale, RULES, handleLoadedHTML, SCROLL_RESET_DELAY);
  146. }
  147.  
  148. if (RULES.HAS_VIDEOS) {
  149. animate();
  150. handleLoadedHTML(RULES.CONTAINER)
  151. new JabroniOutfitUI(store);
  152. }
  153. }
  154.  
  155. //====================================================================================================
  156.  
  157. console.log(LOGO);
  158.  
  159. const SCROLL_RESET_DELAY = 250;
  160. const store = new JabroniOutfitStore(defaultStateWithDuration);
  161. const { state, stateLocale } = store;
  162. const { applyFilters, handleLoadedHTML } = new DataManager(RULES, state);
  163. store.subscribe(applyFilters);
  164. route();