NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Kinopoisk subtitle petition // @namespace http://tampermonkey.net/ // @version 0.5 // @description Helper for subtitle petition // @author Apkawa // @license MIT // @match https://forms.yandex.ru/surveys/10022784.8ae29888f3224e212d4a904160b6baf0a05acd37/* // @match https://hd.kinopoisk.ru/* // @match https://kinopoisk.ru/* // @match https://www.kinopoisk.ru/* // @icon https://www.google.com/s2/favicons?domain=kinopoisk.ru // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js // @grant none // @homepage https://github.com/Apkawa/userscripts // @homepageUrl https://github.com/Apkawa/userscripts // @supportUrl https://github.com/Apkawa/userscripts/issues // @downloadUrl https://github.com/Apkawa/userscripts/raw/master/dist/hd.kinopoisk.ru/kp_subtitle_petition.user.js // @updateUrl https://github.com/Apkawa/userscripts/raw/master/dist/hd.kinopoisk.ru/kp_subtitle_petition.user.js // ==/UserScript== (function() { "use strict"; function getElementByXpath(xpath, root = document) { const e = document.evaluate(xpath, root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; return e && e; } function getElementsByXpath(xpath, root = document) { const iterator = document.evaluate(xpath, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); const result = []; let el = iterator.iterateNext(); while (el) { result.push(el); el = iterator.iterateNext(); } return result; } function markElementHandled(wrapFn, attrName = "_handled") { return function(el) { if (el.getAttribute(attrName)) return; el.setAttribute(attrName, "1"); wrapFn(el); }; } function waitElement(match, callback, root = document.body) { const observer = new MutationObserver((mutations => { let matchFlag = false; mutations.forEach((mutation => { if (!mutation.addedNodes) return; for (let i = 0; i < mutation.addedNodes.length; i++) { const node = mutation.addedNodes[i]; matchFlag = match(node); } })); if (matchFlag) { _stop(); callback(); _start(); } })); let isStarted = false; function _start() { if (isStarted) return; observer.observe(root || document.body, { childList: true, subtree: true, attributes: false, characterData: false }); isStarted = true; } function _stop() { observer.disconnect(); isStarted = false; } _start(); return () => { _stop(); }; } function E(tag, attributes = {}, ...children) { const element = document.createElement(tag); for (const [k, v] of Object.entries(attributes)) element.setAttribute(k, v); const fragment = document.createDocumentFragment(); children.forEach((child => { if ("string" === typeof child) child = document.createTextNode(child); fragment.appendChild(child); })); element.appendChild(fragment); return element; } function mapLocation(map) { const s = document.location.hostname + document.location.pathname; for (const [k, v] of Object.entries(map)) if (RegExp(k).test(s)) v(); } function parseSearch() { return Object.fromEntries(new URLSearchParams(window.location.search).entries()); } Object.keys; Object.entries; Object.values; const FORM_URL = "https://forms.yandex.ru/surveys/10022784.8ae29888f3224e212d4a904160b6baf0a05acd37/"; function radioByText(text) { const xpath = `//p[text() = '${text}']/../..`; const matchingElement = getElementByXpath(xpath); null === matchingElement || void 0 === matchingElement ? void 0 : matchingElement.click(); } function selectByLabel(label, option) { const xpath = `//p[text() = '${label}']/ancestor::tbody//select`; const select = getElementByXpath(xpath); const selected = getElementByXpath(`//option[@selected]`, select); selected.removeAttribute("selected"); const opt = getElementByXpath(`//option[text() = '${option}']`, select); select.value = null === opt || void 0 === opt ? void 0 : opt.value; opt.setAttribute("selected", "1"); return select; } function fillInputByLabel(label, text) { const xpath = `//p[text() = '${label}']/ancestor::tbody//input`; const el = getElementByXpath(xpath); el.value = text; return el; } function getUserName() { const el = getElementByXpath(`//span[@class='user__name']`); return (null === el || void 0 === el ? void 0 : el.innerText) || ""; } function normalizeKPLink(s) { return s.replace(/www\./, ""); } function renderHdKinopoiskRequestLink(sub) { const filmContainer = getElementByXpath("ancestor::section", sub); if (!filmContainer) return; const subNames = [].slice.call(sub.getElementsByTagName("li")).map((el => el.innerText)); const audio = getElementByXpath(`//div[text() = 'Аудиодорожки']/../ul`); const audioNames = [].slice.call(null === audio || void 0 === audio ? void 0 : audio.getElementsByTagName("li")).map((el => el.innerText)); function getInfo() { const link = getElementByXpath(`//a[text() ='Подробнее на КиноПоиске']`, filmContainer).href.toString(); const type = getElementByXpath(`//button[text() = 'О сериале']`) ? "series" : "film"; return { link: normalizeKPLink(link), type: type }; } if (!audioNames.includes("Английский")) { const info = Object.assign(Object.assign({}, getInfo()), { mode: "audio" }); const formUrl = FORM_URL + "?" + new URLSearchParams(info).toString(); const li = E("li", {}, E("a", { href: formUrl, target: "_blank" }, "Запросить оригинальную озвучку")); null === audio || void 0 === audio ? void 0 : audio.appendChild(li); } if (!subNames.includes("Русские")) { const info = getInfo(); const formUrl = FORM_URL + "?" + new URLSearchParams(info).toString(); const li = E("li", {}, E("a", { href: formUrl, target: "_blank" }, "Запросить русские сабы")); sub.appendChild(li); } } function renderKinopoiskRequestLink(sub) { var _a, _b; const filmContainer = getElementByXpath("ancestor::body", sub); if (!filmContainer) return; const country = null === (_a = getElementByXpath(`//div[text() = 'Страна']/following-sibling::div`, filmContainer)) || void 0 === _a ? void 0 : _a.innerText; const audio = getElementByXpath(`//div[text() = 'Аудиодорожки']/following-sibling::div`, filmContainer); const audioNames = null === audio || void 0 === audio ? void 0 : audio.innerText.split(", "); const subNames = sub.innerText.split(", "); const info = { link: normalizeKPLink(window.location.href), type: (null === (_b = /https:\/\/(?:www.|)kinopoisk.ru\/(series|film)\//.exec(window.location.href)) || void 0 === _b ? void 0 : _b[1]) || "" }; if (!(null === audioNames || void 0 === audioNames ? void 0 : audioNames.includes("Английский")) && "Россия" !== country) { const formUrl = FORM_URL + "?" + new URLSearchParams(Object.assign(Object.assign({}, info), { mode: "audio" })).toString(); const li = E("a", { href: formUrl, target: "_blank" }, "Запросить оригинальную озвучку"); null === audio || void 0 === audio ? void 0 : audio.appendChild(li); } if (!subNames.includes("Русские")) { const formUrl = FORM_URL + "?" + new URLSearchParams(info).toString(); const li = E("a", { href: formUrl, target: "_blank" }, "Запросить русские сабы"); sub.appendChild(li); } } (function() { console.warn("DEPRECATED"); mapLocation({ "^forms.yandex.ru/": () => { const params = parseSearch(); if ("film" === params.type) radioByText("Фильм"); else radioByText("Сериал"); if ("audio" === params.mode) { radioByText("Аудиодорожку/озвучку"); selectByLabel("Какую аудиодорожку добавить?", "Оригинальную"); } else { radioByText("Субтитры"); const el = fillInputByLabel("Выберите субтитры:", params.lang || "Русский"); const hiddenEl = getElementByXpath(`ancestor::table//input[@type='hidden']`, el); null === hiddenEl || void 0 === hiddenEl ? void 0 : hiddenEl.setAttribute("value", "30010730"); } fillInputByLabel("Ссылка на фильм/сериал на КиноПоиске", params.link || ""); fillInputByLabel("Ваша почта", `${getUserName()}@yandex.ru`); }, "^hd.kinopoisk.ru/": () => { const xpath = `//div[text() = 'Субтитры']/../ul`; waitElement((el => Boolean(getElementByXpath(xpath, el))), (() => { const subtitles = getElementsByXpath(xpath); for (const el of subtitles) markElementHandled(renderHdKinopoiskRequestLink)(el); })); }, "(www.|)kinopoisk.ru/(series|film)": () => { const xpath = `//div[text() = 'Субтитры']/following-sibling::div`; waitElement((el => Boolean(getElementByXpath(xpath, el))), (() => { const subtitles = getElementsByXpath(xpath); for (const el of subtitles) markElementHandled(renderKinopoiskRequestLink)(el); })); } }); })(); })();