Schmarotzer

Untitled

Apr 16th, 2026
46
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.97 KB | None | 0 0
  1. // ==UserScript==
  2. // @name КиноКлик
  3. // @namespace https://greasyfork.org/ru/scripts/558634-киноклик
  4. // @author 4c5688
  5. // @license CC BY-SA
  6. // @version 1.1
  7. // @description Добавляет кнопки для просмотра и поиска на популярных трекерах фильмов и сериалов (отказоустойчиво)
  8. // @match *://*.kinopoisk.ru/*
  9. // @grant none
  10. // @icon https://www.kinopoisk.ru/favicon.ico
  11. // @downloadURL https://update.greasyfork.org/scripts/558634/%D0%9A%D0%B8%D0%BD%D0%BE%D0%9A%D0%BB%D0%B8%D0%BA.user.js
  12. // @updateURL https://update.greasyfork.org/scripts/558634/%D0%9A%D0%B8%D0%BD%D0%BE%D0%9A%D0%BB%D0%B8%D0%BA.meta.js
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. const SAFE_RESOURCES = [
  19. { title: "SSpoisk", url: "http://flcksbr.top/film/%id/", icon: "https://favicon.yandex.net/favicon/kinopoisk.ru" },
  20. { title: "Kinobox", url: "https://kinohost.web.app/movie/%id/", icon: "https://favicon.yandex.net/favicon/kinobox.tv" },
  21. { title: "YouTube", url: "https://www.youtube.com/results?search_query=%text %year", icon: "https://favicon.yandex.net/favicon/youtube.com" },
  22. { title: "VKVideo", url: "https://vkvideo.ru/?q=%text %year", icon: "https://favicon.yandex.net/favicon/vkvideo.ru" },
  23. { title: "Rutube", url: "https://rutube.ru/search/?query=%text %year", icon: "https://favicon.yandex.net/favicon/rutube.ru" },
  24. { title: "MyMail Video", url: "https://my.mail.ru/video/search?q=%text %year", icon: "https://favicon.yandex.net/favicon/my.mail.ru" },
  25. { title: "Flixomo", url: "https://flixmomo.org/search?q=%text", icon: "https://favicon.yandex.net/favicon/flixmomo.org" },
  26. { title: "Rutracker", url: "https://rutracker.net/forum/tracker.php?nm=%text %year", icon: "https://favicon.yandex.net/favicon/rutracker.net" },
  27. { title: "Rutor", url: "https://rutor.org/search/0/0/100/0/%cleantext %year", icon: "https://favicon.yandex.net/favicon/rutor.org" },
  28. { title: "Kinozal", url: "https://kinozal.tv/browse.php?s=%text&d=%year", icon: "https://favicon.yandex.net/favicon/kinozal.tv" },
  29. { title: "NNMClub", url: "https://nnmclub.to/forum/tracker.php?nm=%text %year", icon: "https://favicon.yandex.net/favicon/nnmclub.to" },
  30. { title: "The Pirate Bay", url: "https://thepiratebay.org/search.php?q=%engcleantext %year", icon: "https://favicon.yandex.net/favicon/thepiratebay.org" },
  31. { title: "Nyaa", url: "https://nyaa.land/?f=0&c=0_0&q=%text", icon: "https://favicon.yandex.net/favicon/nyaa.land" },
  32. ];
  33.  
  34. const QUERY_DATA = {};
  35.  
  36. function cleanQuery(str, lang='ru') {
  37. if(!str) return "";
  38. const stop = lang==='ru' ? ["в","во","на","по","и","с","со","к","ко","за","из","у","для","от","до","о","об","а"] : [];
  39. const regex = lang==='ru' ? /[^а-яё0-9\s]/gi : /[^a-z0-9\s]/gi;
  40. return str.toLowerCase()
  41. .replace(regex," ")
  42. .split(/\s+/)
  43. .filter(w => w.length>2 && !stop.includes(w))
  44. .join(" ");
  45. }
  46.  
  47. function querystring(str) {
  48. return str.replace(/%(\w+)/g, (_,w) => QUERY_DATA[w.toLowerCase()] ? encodeURIComponent(QUERY_DATA[w.toLowerCase()]) : _ );
  49. }
  50.  
  51. function extractQueryData() {
  52. const h1 = document.querySelector('h1[itemprop="name"]');
  53. if(!h1) return;
  54.  
  55. const titleSpan = h1.querySelector('span');
  56. const titleText = titleSpan ? titleSpan.textContent.trim() : '';
  57.  
  58. const origSpan = document.querySelector('span[class*="OriginalTitle"], span[class*="originalTitle"]');
  59. const engTitle = origSpan && origSpan.textContent.trim() ? origSpan.textContent.trim() : titleText;
  60.  
  61. const yearMatch = titleText.match(/\((\d{4})\)/);
  62. const year = yearMatch ? yearMatch[1] : '';
  63.  
  64. const idMatch = location.pathname.match(/\/(film|series)\/(\d+)/);
  65. const id = idMatch ? idMatch[2] : '';
  66.  
  67. QUERY_DATA.text = titleText.replace(/\(\d{4}\)/, '').trim();
  68. QUERY_DATA.engtext = engTitle;
  69. QUERY_DATA.year = year;
  70. QUERY_DATA.id = id;
  71. QUERY_DATA.cleantext = cleanQuery(QUERY_DATA.text, 'ru');
  72. QUERY_DATA.engcleantext = cleanQuery(QUERY_DATA.engtext, 'en');
  73. }
  74.  
  75. const style=document.createElement('style');
  76. style.textContent=`
  77. button.kinopoisk-watch-online-button,
  78. .styles_subscriptionOffer__eau7V,
  79. .styles_root__J2PUZ,
  80. .styles_hdMetaTableContainer__coI3m,
  81. .styles_watchingServices__EeMSa,
  82. .styles_plusBadgeCounter__ztZqV,
  83. .style_root__UUsUH.style_container__gsfKM { display:none!important; }
  84.  
  85. .kp-alt-wrapper { margin-top:6px; position:relative; display:inline-block; width:100%; }
  86. .kp-alt-body {
  87. position:absolute;
  88. top:calc(100% + 6px);
  89. left:0;
  90. transform-origin:top;
  91. transform:scaleY(0);
  92. border-radius:10px;
  93. padding:10px;
  94. display:flex;
  95. flex-direction:column;
  96. box-shadow:0 4px 12px rgba(0,0,0,.15);
  97. transition: transform .25s ease;
  98. z-index:100;
  99. background:rgba(255,255,255,0.98);
  100. width: max-content;
  101. min-width:200px;
  102. }
  103. .kp-alt-body.open { transform:scaleY(1); }
  104.  
  105. .kp-row {
  106. display:flex;
  107. align-items:center;
  108. gap:10px;
  109. padding:8px 12px;
  110. border-radius:8px;
  111. cursor:pointer;
  112. transition:background .2s, transform .15s;
  113. }
  114. .kp-row:hover { background:rgba(0,0,0,0.08); transform:scale(1.03); }
  115. .kp-row a { color:inherit!important; text-decoration:none; font-size:16px; font-weight:500; }
  116. `;
  117. document.head.appendChild(style);
  118.  
  119. let lastUrl = location.href;
  120.  
  121. const observer = new MutationObserver(()=>{
  122. const urlChanged = location.href !== lastUrl;
  123. if(urlChanged) { lastUrl = location.href; extractQueryData(); }
  124.  
  125. let watchBtn = document.getElementById("rk-watch-btn");
  126. if(!watchBtn){
  127. const originalBtn = document.querySelector("button[title='Буду смотреть']");
  128. const buttonRoot = originalBtn?.closest("div");
  129.  
  130. if(originalBtn && buttonRoot?.parentElement && QUERY_DATA.id){
  131. const wrapper=document.createElement("div");
  132. wrapper.className="kp-alt-wrapper";
  133.  
  134. const newBtn=document.createElement("button");
  135. newBtn.id="rk-watch-btn";
  136. newBtn.textContent="Смотреть онлайн";
  137. newBtn.className="style_button__Awsrq style_buttonSize52__MBeHC style_buttonPrimary__Qn_9l";
  138.  
  139. const body=document.createElement("div");
  140. body.className="kp-alt-body";
  141.  
  142. SAFE_RESOURCES.forEach(site=>{
  143. const row=document.createElement("div");
  144. row.className="kp-row";
  145. row.innerHTML=`<img src="${site.icon}" width="20" height="20"><a href="${querystring(site.url)}" target="_blank">${site.title}</a>`;
  146. body.appendChild(row);
  147. });
  148.  
  149. newBtn.onclick = (e)=>{
  150. e.stopPropagation();
  151. body.classList.toggle("open");
  152. };
  153.  
  154. document.addEventListener("click", e=>{
  155. if(!wrapper.contains(e.target)){
  156. body.classList.remove("open");
  157. }
  158. });
  159.  
  160. wrapper.appendChild(newBtn);
  161. wrapper.appendChild(body);
  162. buttonRoot.parentElement.insertBefore(wrapper, buttonRoot.nextSibling);
  163. }
  164. }
  165. });
  166.  
  167. observer.observe(document.body,{childList:true,subtree:true});
  168. extractQueryData();
  169. })();
Add Comment
Please, Sign In to add comment