Greasy Fork is available in English.
Добавляет возможность смотреть аниме в стороннем плеере в любой озвучке прямо на сайте shikimori
// ==UserScript== // @name Shiki OtherAnime // @name:ru Shiki OtherAnime // @namespace shikiOtherAnime // @version 0.2.2 // @description Adds other external anime online player to the shikimori // @description:ru Добавляет возможность смотреть аниме в стороннем плеере в любой озвучке прямо на сайте shikimori // @author Blank // @match *://shikimori.tld/* // @match *://shikimori.one/* // @match *://shikimori.me/* // @run-at document-end // @grant GM.xmlHttpRequest // @noframes // ==/UserScript== // Notes: // - fully compatible with shikiAnilibria script // - autopause does not require videoShortcuts script (player handles postMessage itself) // - videoShortcuts script is recommended for watching boring moments at 1.25 - 16x speed ^_^ (function main() { 'use strict'; const log = (...args) => console.log(`${GM.info.script.name}:`, ...args); log('start'); if (!document.querySelector('#watch-online-style')) { const style = document.createElement('style'); style.id = 'watch-online-style'; style.textContent = ` .watch-online-iframe { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80vw; height: 45vw; z-index: 9001; } .watch-online-line { padding-bottom: 5px; } #watch-online-overlay { display: none; position: fixed; width: 100%; height: 100%; z-index: 9000; background: rgba(0, 0, 0, 0.85); }`; document.querySelector('head').append(style); } // xmlHttpRequest const request = async (details) => new Promise((resolve, reject) => { GM.xmlHttpRequest({ method: 'GET', responseType: 'json', anonymous: true, ...details, onload(responseObject) { resolve(responseObject); }, onerror(responseObject) { reject(responseObject); }, }); }); const reduceR###lts = ({ r###lts }, title, sId) => { let filteredR###lts; let exactMatch = true; if (sId) { filteredR###lts = r###lts.filter((a) => a.shikimori_id === sId); } else { const prepare = (t) => t.toLowerCase().replace(/[ ,:'`]/g, ''); const origTitle = prepare(title); filteredR###lts = r###lts.filter((a) => prepare(a.title_orig) === origTitle); if (!filteredR###lts.length) { exactMatch = false; const firstTitle = prepare(r###lts[0].title_orig); filteredR###lts = r###lts.filter((a) => prepare(a.title_orig) === firstTitle); } } filteredR###lts.sort((a, b) => b.episodes_count - a.episodes_count); return { ...filteredR###lts[0], exactMatch }; }; const getOtherR###lts = async (ogTitle, sId) => { const apiUrl = 'https://metamedia.glitch.me/api/search'; const token = '3o52d19319b8dafoa194c9a77ofc9a3d8obfe1bd3eoe28b7cfd7aacebaaf4f6b'; const url = `${apiUrl}?${sId ? `shikimori_id=${sId}` : `title=${encodeURI(ogTitle)}`}&sign=${token.replace(/o/g, 0)}`; let res; try { res = await request({ url }); if (res.status !== 200) { log(`response error code ${res.status}: ${url}`); return null; } } catch (er) { log(`request error: ${url}`); return null; } const { response } = res; if (!response || !response.total || !response.r###lts || !response.r###lts.length) { if (sId) { log(`not found by shikimori_id (${sId})`); if (ogTitle) return getOtherR###lts(ogTitle); } else { log(`not found by ogTitle (${ogTitle})`); } return null; } return reduceR###lts(response, ogTitle, sId); }; const addEventListeners = ({ overlay, iframe, otherAnime }) => { overlay.addEventListener('click', (e) => { if (e.target !== e.currentTarget) return; overlay.style.display = 'none'; iframe.style.display = 'none'; iframe.contentWindow.postMessage({ key: 'kodik_player_api', value: { method: 'pause' } }, '*'); }, { passive: true }); otherAnime.addEventListener('click', () => { overlay.style.display = 'block'; iframe.style.display = 'block'; }, { passive: true }); }; const createElements = ({ src, count, exact, id, sId, }) => { let overlay = document.querySelector('#watch-online-overlay'); if (!overlay) { overlay = document.createElement('div'); overlay.id = 'watch-online-overlay'; document.body.prepend(overlay); } let iframe = document.querySelector('#watch-online-iframe-other'); if (!iframe) { iframe = document.createElement('iframe'); iframe.id = 'watch-online-iframe-other'; iframe.className = 'watch-online-iframe'; iframe.src = `${src}?translations=true`; iframe.allow = 'fullscreen'; overlay.append(iframe); } const contestBlock = document.querySelector('.block.contest_winners'); if (contestBlock) { let contestHeader = document.querySelector('#watch-online-contest-subheadline'); if (!contestHeader) { contestHeader = document.createElement('div'); contestHeader.id = 'watch-online-contest-subheadline'; contestHeader.className = 'subheadline'; contestHeader.textContent = 'Турниры'; contestBlock.prepend(contestHeader); } } let otherAnime = document.querySelector('#watch-online-a-other'); if (!otherAnime) { let block = document.querySelector('#watch-online-block'); if (!block) { block = document.createElement('div'); block.id = 'watch-online-block'; block.className = 'block'; const subheadline = document.createElement('div'); subheadline.className = 'subheadline'; subheadline.textContent = 'Онлайн просмотр'; block.append(subheadline); const targetBlock = document.querySelector('.block[itemprop="aggregateRating"]'); targetBlock.parentElement.insertBefore(block, targetBlock.nextElementSibling); } const line = document.createElement('div'); line.className = 'watch-online-line'; otherAnime = document.createElement('a'); otherAnime.id = 'watch-online-a-other'; otherAnime.className = 'b-link'; otherAnime.title = `Смотреть (точное совпадение ${exact ? '' : 'не '}найдено)`; otherAnime.textContent = `Другой плеер${exact ? '*' : ''} [${count ? `1-${count}` : '1'}]`; const aSite = document.createElement('a'); aSite.className = 'b-link'; aSite.target = '_blank'; aSite.title = 'Смотреть на сайте amove'; aSite.href = `https://amove.my.to/#${sId ? `shikimori-${sId}&${id}` : id}`; aSite.textContent = ' ↗ '; line.append(otherAnime, aSite); block.append(line); } addEventListeners({ overlay, iframe, otherAnime }); }; // new anime handler const newAnimeShow = async () => { const overlay = document.querySelector('#watch-online-overlay'); const iframe = document.querySelector('#watch-online-iframe-other'); const otherAnime = document.querySelector('#watch-online-a-other'); if (overlay && iframe && otherAnime) { addEventListeners({ overlay, iframe, otherAnime }); return; } const ogTitle = document.querySelector('head > meta[property = "og:title"]').content; const rawId = document.URL.substring(document.URL.lastIndexOf('/') + 1, document.URL.indexOf('-')); const sId = Number.isNaN(+rawId[0]) ? rawId.substring(1) : rawId; const r###lt = await getOtherR###lts(ogTitle, sId); if (!r###lt) { log('other anime not found'); return; } createElements({ src: r###lt.link, count: r###lt.episodes_count, exact: r###lt.exactMatch, id: r###lt.id, sId: r###lt.shikimori_id, }); }; // observer fire when html changes its body const observer = new MutationObserver((mutationsList) => { mutationsList.forEach((mutationRecord) => mutationRecord.addedNodes.forEach((node) => { if (node.classList.contains('p-animes-show')) newAnimeShow(); })); }); observer.observe(document.querySelector('html'), { childList: true }); if (document.body.classList.contains('p-animes-show')) newAnimeShow(); }());