Kinopoisk Watch Button | Cododel [RU]
CODODELDEV
EN / RU
Back to Deck
[userscript]

Kinopoisk Watch Button

УСТАНОВИТЬ
ИСХОДНИК Kinopoisk
ВЕРСИЯ 1.0
АВТОР Cododel

Target Match:

  • https://www.kinopoisk.ru/*

UserScript для Кинопоиска, добавляющий удобную кнопку для просмотра фильмов через альтернативный плеер (SSpoisk).

Возможности

  • Интеграция в списки: Кнопка появляется рядом с кнопкой “Буду смотреть” в ленте и списках
  • Интеграция на странице фильма: Кнопка добавляется в основной блок кнопок на странице фильма/сериала
  • Умное обнаружение: Использует Network Idle для отслеживания подгрузки контента (SPA навигация)
  • Стилизация: Мимикрирует под интерфейс Кинопоиска

Как работает

Скрипт заменяет домен kinopoisk.ru на sspoisk.ru в ссылке, формируя прямой переход к просмотру.

Установка

  1. Установите расширение для UserScripts (Tampermonkey/Violentmonkey)
  2. Установить скрипт
  3. Перейдите на Кинопоиск и наслаждайтесь!

Исходный код

// ==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 ////
})()
[ ▲ 0 ]