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 в ссылке, формируя прямой переход к просмотру.
Установка
- Установите расширение для UserScripts (Tampermonkey/Violentmonkey)
- Установить скрипт
- Перейдите на Кинопоиск и наслаждайтесь!
Исходный код
// ==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 ////})()