Back to Deck
INSTALL
[userscript]
Kinopoisk Watch Button
SOURCE Kinopoisk
VERSION 1.0
AUTHOR Cododel
Target Match:
- https://www.kinopoisk.ru/*
A UserScript for Kinopoisk that adds a convenient button to watch movies via an alternative player (SSpoisk).
Features
- List Integration: The button appears next to the “Will Watch” button in feeds and lists
- Movie Page Integration: The button is added to the main button block on the movie/series page
- Smart Detection: Uses
Network Idleto track content loading (SPA navigation) - Styling: Mimics the Kinopoisk interface
How it works
The script replaces the kinopoisk.ru domain with sspoisk.ru in the link, generating a direct link to watch.
Installation
- Install a UserScript extension (Tampermonkey/Violentmonkey)
- Install the script
- Navigate to Kinopoisk and enjoy!
Source Code
// ==UserScript==// @name Kinopoisk Watch Button// @namespace https://cododel.dev/// @version 1.0// @description Добавляет кнопку "Смотреть" (SSpoisk) на страницы фильмов и списки Кинопоиска// @author Cododel// @match https://www.kinopoisk.ru/*// @icon https://www.google.com/s2/favicons?sz=64&domain=kinopoisk.ru// @grant none// @updateURL https://cododel.dev/mods/kinopoisk-watch/install.user.js// @downloadURL https://cododel.dev/mods/kinopoisk-watch/install.user.js// ==/UserScript==
;(function () { 'use strict'
useNetworkidleEvents() let lastLocation = null
window.addEventListener('network-idle', () => { const currentLocation = window.location.href if (lastLocation === currentLocation) return lastLocation = currentLocation requestAnimationFrame(addWatchButtons) })
//// [START] Service Section ////
function isTargetPathname(pathname) { return pathname.startsWith('/film') || pathname.startsWith('/series') } function isTargetPage() { return document.body.hasAttribute('data-is-target-page') }
function checkForIsTargetPage() { if (isTargetPathname(window.location.pathname)) { document.body.setAttribute('data-is-target-page', '') } else { document.body.removeAttribute('data-is-target-page') } }
function useNetworkidleEvents() { let activeRequests = 0 let idleTimeout = null
// Функция для проверки состояния сети function checkNetworkIdle() { if (activeRequests === 0) { idleTimeout = setTimeout(() => { const event = new CustomEvent('network-idle') window.dispatchEvent(event) }, 500) // Задержка в 500 мс для устранения дребезга } }
// Настройка PerformanceObserver для отслеживания сетевых запросов const observer = new PerformanceObserver((list) => { list.getEntries().forEach((entry) => { if (entry.initiatorType === 'fetch' || entry.initiatorType === 'xmlhttprequest') { // Увеличиваем счетчик активных запросов activeRequests++ clearTimeout(idleTimeout)
// Уменьшаем счетчик активных запросов после завершения загрузки if (entry.duration > 0) { activeRequests-- checkNetworkIdle() } } }) })
// Начинаем наблюдение за сетевыми запросами observer.observe({ entryTypes: ['resource'] }) }
function selectTargetAnchorElement(container) { return ( container.querySelector('[href^="/film/"]') || container.querySelector('[href^="/series/"]') ) } function getPlannedToWatchButtons() { const plannedToWatchButtons = [...document.querySelectorAll('[title="Буду смотреть"]')] const snapshot = document.evaluate( "//button[contains(text(), 'Буду смотреть')]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null, ) for (let i = 0; i < snapshot.snapshotLength; i++) { plannedToWatchButtons.push(snapshot.snapshotItem(i)) }
return [...new Set(plannedToWatchButtons)] }
function getContainer(refButton) { if (isTargetPage()) { return refButton.closest('[class^="styles_buttonsContainer"]') } return refButton.parentElement.closest('[data-tid]') } function getWatchURL(buttonReferencePlannedToWatchButton) { let targetPageURL if (isTargetPage()) { targetPageURL = window.location.href } else { let tmpContainer = buttonReferencePlannedToWatchButton let tmpTarget do { tmpContainer = tmpContainer.parentElement tmpTarget = selectTargetAnchorElement(tmpContainer) } while (tmpTarget == undefined)
targetPageURL = tmpTarget.href if (!targetPageURL.startsWith('http')) { targetPageURL = window.location.origin + targetPageURL } } return targetPageURL.replace(/kinopoisk/, 'sspoisk') }
function makeButton(buttonReference) { const buttonClass = buttonReference.classList.toString() const watchButton = document.createElement('a') watchButton.classList.value = buttonClass + ' cdd-watch-button' watchButton.href = getWatchURL(buttonReference) watchButton.target = '_blank'
return watchButton }
function addStyles() { document.body.insertAdjacentHTML( 'beforeend', `<style> .cdd-watch-button { margin-right: 10px !important; display: flex; justify-content: center; transform: none !important; } .cdd-watch-button:before { display: inline-block; width: 12px; height: 12px; content: ""; background-image: url("data:image/svg+xml,%3csvg width='12' height='12' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='%23000000' fill-rule='evenodd' clip-rule='evenodd' d='M8 2.5H1v7h7V7.8L12 9V3L8 4.2V2.5Z'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: 50%; background-size: contain; filter: invert(1); mix-blend-mode: exclusion;
} [data-is-target-page] .cdd-watch-button:before{ width: 21px; height: 21px; } </style>`, ) }
function addWatchButtons() { checkForIsTargetPage()
addStyles() const refButtons = getPlannedToWatchButtons() refButtons.forEach((refButton) => { const button = makeButton(refButton) const container = getContainer(refButton)
container.prepend(button) }) }
//// [END] Service Section ////})()