Skip to content

Instantly share code, notes, and snippets.

@Apkawa
Last active October 2, 2021 13:59
Show Gist options
  • Save Apkawa/34d7db5b9b33ebccf7ffb08dbd3d6e58 to your computer and use it in GitHub Desktop.
Save Apkawa/34d7db5b9b33ebccf7ffb08dbd3d6e58 to your computer and use it in GitHub Desktop.
Kinopoisk subtitle petition

Хелпер для петиции субтитров к фильмам

Письмо от поддержки

image

Добавляется ссылка в списке субтитров

  1. На kinopoisk.ru

image

  1. На hd.kinopoisk.ru

image

  1. В некоторых случаях нет оригинальной дорожки, можно создать петицию.

image

На кинопоиске для России не требуется запрос оригинальной дорожки, для hd нужно быть внимательным, там нет возможности выцепить страну в деталях

Установка

Установить аддон Tampermonkey для хрома или огнелиса

Кликнуть на Raw

Обновление

Еще раз кликнуть на Raw

Или включить в настройках аддона автообновление

// ==UserScript==
// @name Kinopoisk subtitle petition
// @namespace http://tampermonkey.net/
// @version 0.2
// @description Helper for add subtitle petition
// @author Apkawa
// @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
// @homepage https://gist.github.com/Apkawa/34d7db5b9b33ebccf7ffb08dbd3d6e58/
// @supportUrl https://gist.github.com/Apkawa/34d7db5b9b33ebccf7ffb08dbd3d6e58/
// @homepageUrl https://gist.github.com/Apkawa/34d7db5b9b33ebccf7ffb08dbd3d6e58/
// @downloadUrl https://gist.github.com/Apkawa/34d7db5b9b33ebccf7ffb08dbd3d6e58/raw/kinopoisk.user.js
// @updateUrl https://gist.github.com/Apkawa/34d7db5b9b33ebccf7ffb08dbd3d6e58/raw/kinopoisk.user.js
// @grant none
// ==/UserScript==
const FORM_URL = "https://forms.yandex.ru/surveys/10022784.8ae29888f3224e212d4a904160b6baf0a05acd37/"
function getElementByXpath(xpath, root = document) {
return document.evaluate(
xpath, root,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null).singleNodeValue;
}
function getElementsByXpath(xpath, root = document) {
const iterator = document.evaluate(
xpath, root,
null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
null);
let result = []
let el = iterator.iterateNext();
while (el) {
result.push(el)
el = iterator.iterateNext();
}
return result
}
function radioByText(text) {
let xpath = `//p[text() = '${text}']/../..`;
const matchingElement = getElementByXpath(xpath)
matchingElement.click()
}
function selectByLabel(label, option) {
let xpath = `//p[text() = '${label}']/ancestor::tbody//select`
const select = getElementByXpath(xpath)
let selected = getElementByXpath(`//option[@selected]`, select)
selected.removeAttribute('selected')
let opt = getElementByXpath(`//option[text() = '${option}']`, select)
select.value = opt.value
opt.setAttribute('selected', true)
return select
}
function fillInputByLabel(label, text) {
let 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 el.innerText
}
function parseSearch() {
return Object.fromEntries(new URLSearchParams(window.location.search).entries())
}
function waitElement(match, callback) {
let observer = new MutationObserver((mutations) => {
let matchFlag = false
mutations.forEach((mutation) => {
if (!mutation.addedNodes) return
for (let i = 0; i < mutation.addedNodes.length; i++) {
// do things to your newly added nodes here
let node = mutation.addedNodes[i]
matchFlag = match(node)
}
})
if (matchFlag) {
callback()
}
})
observer.observe(document.body, {
childList: true
, subtree: true
, attributes: false
, characterData: false
})
return () => {
// stop watching using:
observer.disconnect()
}
}
function E(tag, attributes = {}, ...children) {
const element = document.createElement(tag)
for (const attribute in attributes) {
if (attributes.hasOwnProperty(attribute)) {
element.setAttribute(attribute, attributes[attribute])
}
}
const fragment = document.createDocumentFragment()
children.forEach((child) => {
if (typeof child === 'string') {
child = document.createTextNode(child)
}
fragment.appendChild(child)
})
element.appendChild(fragment)
return element
}
function hdAddRequest(sub) {
let filmContainer = getElementByXpath('ancestor::section', sub)
let subNames = [].slice.call(sub.getElementsByTagName('li')).map(el => el.innerText)
if (sub.getAttribute('request-added')) {
return
}
let audio = getElementByXpath(`//div[text() = 'Аудиодорожки']/../ul`)
let audioNames = [].slice.call(audio.getElementsByTagName('li')).map(el => el.innerText)
function getInfo() {
let link = getElementByXpath(`//a[text() ='Подробнее на КиноПоиске']/@href`, filmContainer).value;
let type = getElementByXpath(`//button[text() = 'О сериале']`) ? 'series' : 'film'
return {
link,
type,
}
}
if (!audioNames.includes("Английский")) {
let info = { ...getInfo(), mode: 'audio' }
let formUrl = FORM_URL + '?' + new URLSearchParams(info).toString()
let li = E('li', {},
E('a', { 'href': formUrl, 'target': '_blank' },
'Запросить оригинальную озвучку')
)
audio.appendChild(li)
}
if (!subNames.includes("Русские")) {
let info = getInfo()
let formUrl = FORM_URL + '?' + new URLSearchParams(info).toString()
let li = E('li', {},
E('a', { 'href': formUrl, 'target': '_blank' },
'Запросить русские сабы')
)
sub.appendChild(li)
}
sub.setAttribute('request-added', 1)
}
function kinopoiskAddRequest(sub) {
let filmContainer = getElementByXpath('ancestor::body', sub)
if (sub.getAttribute('request-added')) {
return
}
const country = getElementByXpath(`//div[text() = 'Страна']/following-sibling::div`, filmContainer).innerText
let audio = getElementByXpath(`//div[text() = 'Аудиодорожки']/following-sibling::div`, filmContainer)
let audioNames = audio.innerText.split(', ')
let subNames = sub.innerText.split(', ')
let info = {
link: window.location.href,
type: window.location.href.match('https://(?:www.|)kinopoisk.ru/(series|film)/')[1]
}
if (!audioNames.includes("Английский") && country !== "Россия") {
let formUrl = FORM_URL + '?' + new URLSearchParams({ ...info, mode: 'audio' }).toString()
let li = E('a', { 'href': formUrl, 'target': '_blank' },
'Запросить оригинальную озвучку')
audio.appendChild(li)
}
if (!subNames.includes("Русские")) {
let formUrl = FORM_URL + '?' + new URLSearchParams(info).toString()
let li = E('a', { 'href': formUrl, 'target': '_blank' },
'Запросить русские сабы')
sub.appendChild(li)
}
sub.setAttribute('request-added', 1)
}
(function () {
'use strict';
if (window.location.hostname === 'forms.yandex.ru') {
// AUTO fill form
const params = parseSearch()
if (params.type === 'film') {
radioByText("Фильм")
} else {
radioByText("Сериал")
}
if (params.mode === 'audio') {
radioByText("Аудиодорожку/озвучку")
selectByLabel("Какую аудиодорожку добавить?", "Оригинальную")
} else {
radioByText("Субтитры")
fillInputByLabel('Выберите субтитры:', params.lang || 'Русский')
}
fillInputByLabel('Ссылка на фильм/сериал на КиноПоиске', params.link || '')
fillInputByLabel("Ваша почта", `${getUserName()}@yandex.ru`)
}
if (window.location.href.startsWith('https://hd.kinopoisk.ru/')) {
let xpath = `//div[text() = 'Субтитры']/../ul`
waitElement(el => getElementByXpath(xpath, el),
() => {
let subtitles = getElementsByXpath(xpath)
for (let el of subtitles) {
hdAddRequest(el)
}
})
}
if (window.location.href.match('https://(www.|)kinopoisk.ru/(series|film)/')) {
let xpath = `//div[text() = 'Субтитры']/following-sibling::div`
waitElement(el => getElementByXpath(xpath, el),
() => {
let subtitles = getElementsByXpath(xpath)
for (let el of subtitles) {
kinopoiskAddRequest(el)
}
})
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment