// ==UserScript==
// @name Кнопка перехода на Flicksbar из Kinorium (без использования API Кинопоиска)
// @namespace http://tampermonkey.net/
// @version 1.0.5.5.0
// @description Ищет фильм в Google и автоматически переходит на Flicksbar без использования API Кинопоиска
// @author CgPT & Vladimir_0202
// @icon https://ru.kinorium.com/favicon.ico
// @match *://*.kinorium.com/*
// @match *://flcksbr.top/*
// @match *://www.google.com/search*
// @match *://duckduckgo.com/*
// @include /^https:\/\/.*flicksbar\..*$/
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_registerMenuCommand
// @license MIT
// ==/UserScript==
(function () {
'use strict';
//Добавляем кнопки в меню TamperMonkey
let menuCommands = {};
// Описываем функции в массиве
const features = [
{ key: 'function1Enabled', name: 'Очистка Хранилища', description: 'Удаляет сохранённые временные данные (например, постер и описание)' },
{ key: 'function2Enabled', name: 'Запрос при переходе на Flicksbar', description: 'Показывает подтверждение перед редиректом на Flicksbar' },
{ key: 'function3Enabled', name: 'Редирект через Google или DuckDuckGo ', description: 'Переключение редиректа с Google на DuckDuckGo'}
];
// Регистрируем все пункты меню
function registerAllMenus() {
// Удаляем старые команды меню
for (let id in menuCommands) {
GM_unregisterMenuCommand(menuCommands[id]);
}
menuCommands = {};
// Регистрируем новые команды
for (let feature of features) {
let enabled = GM_getValue(feature.key, true);
let title = (enabled ? "✅ Вкл: " : "❌ Выкл: ") + feature.name;
menuCommands[feature.key] = GM_registerMenuCommand(title, () => toggleFeature(feature));
}
}
// Функция переключения состояния
function toggleFeature(feature) {
let currentState = GM_getValue(feature.key, true);
let newState = !currentState;
GM_setValue(feature.key, newState);
alert(`${feature.name} ${newState ? '✅ Включен (а)' : '❌ Выключен (а)'}\n\nОписание: ${feature.description}`);
registerAllMenus(); // Перерегистрировать меню после переключения
}
// При старте скрипта
registerAllMenus();
const url = window.location.href;
// === 1. KINORIUM: Добавляем кнопку на страницу фильма ===
if (/kinorium\.com\/\d+\/?$/.test(url)) {
// Сохраняем постер и описание в хранилище Tampermonkey
const posterEl = document.querySelector('.jsCarouselImageContainer img') || document.querySelector('.carousel_image-handler img');
const descEl = document.querySelector('.film-page__text');
const titleEl = document.querySelector('.film-page__title-text.film-page__itemprop');
const originalTitleEl = document.querySelector('.film-page__orig_with_comment') || document.querySelector('.film-page__subtitle');
const yearEl = document.querySelector('.film-page__date a[href*="years_min="]');
const raitingEl = document.querySelector('.film-page__title-rating')
|| Array.from(document.querySelectorAll('.ratingsBlock li'))
.find(li => li.textContent.includes('Кинориум'))
?.querySelector('.value');
const poster = posterEl?.src;
const description = descEl?.innerText;
const titleTMP = titleEl?.innerText;
const originalTitleTMP = originalTitleEl?.innerText;
const yearTMP = yearEl?.innerText || '';
const raitingTMP = raitingEl?.innerText?.trim() || '';
const currentUrlTMP = window.location.href;
if (poster && description) {
GM_setValue('flicksbar_poster', poster); // Сохраняем в хранилище Tampermonkey
GM_setValue('flicksbar_description', description); // Сохраняем в хранилище Tampermonkey
GM_setValue('flicksbar_titleTMP', titleTMP); // Сохраняем в хранилище Tampermonkey
GM_setValue('flicksbar_originalTitleTMP', originalTitleTMP); // Сохраняем в хранилище Tampermonkey
GM_setValue('flicksbar_yearTMP', yearTMP); // Сохраняем в хранилище Tampermonkey
GM_setValue('flicksbar_raitingTMP', raitingTMP); // Сохраняем в хранилище Tampermonkey
GM_setValue('flicksbar_currentUrlTMP', currentUrlTMP);
console.log('✅ Постер и описание сохранены в Tampermonkey storage');
} else {
console.warn('⚠️ Не удалось найти постер или описание');
}
function getFilmDetails() {
const titleElement = document.querySelector('.film-page__title-text.film-page__itemprop');
const originalTitleElement = document.querySelector('.film-page__orig_with_comment');
const typeLink = document.querySelector('.b-post__info a[href*="/series/"]');
const yearElement = document.querySelector('.film-page__date a[href*="years_min="]');
const title = titleElement ? titleElement.textContent.trim() : '';
const originalTitle = originalTitleElement ? originalTitleElement.textContent.trim() : '';
const year = yearElement ? yearElement.textContent.trim() : '';
const isSeries = typeLink !== null;
return { title, originalTitle, year, isSeries };
}
function createButton() {
const button = document.createElement('button');
button.innerHTML = '<span style="display:inline-flex; align-items:center; justify-content:center; width:20px; height:22px; margin-right:8px; border:2px solid white; border-radius:50%; font-size:12px; padding-left:2px;">▶</span>Смотреть на Flicksbar';
button.style.cssText = `
padding: 9px;
margin-top: -5px;
margin-bottom: 2px;
background-color: #007bff;
color: white;
border: none;
border-radius: 3px;
width: 100%;
cursor: pointer;
transition: background-color 0.3s ease;
`;
button.addEventListener('mouseenter', () => button.style.backgroundColor = '#0056b3');
button.addEventListener('mouseleave', () => button.style.backgroundColor = '#007bff');
const { title, originalTitle, year } = getFilmDetails();
button.title = `Смотреть "${title} ${originalTitle} ${year}" бесплатно онлайн на Flicksbar`;
button.onclick = () => {
const { title, originalTitle, year, isSeries } = getFilmDetails();
if (!title) return alert('Не удалось извлечь информацию о фильме.');
const searchQuery = encodeURIComponent(`${title} ${originalTitle} ${year} кинопоиск`);
const flicksbarType = isSeries ? 'series' : 'film';
const googleUrl = GM_getValue('function3Enabled', true)
? `https://www.google.com/search?q=${searchQuery}&btnK&flcks_type=${flicksbarType}`
: `https://duckduckgo.com/?q=${searchQuery}&btnK&flcks_type=${flicksbarType}`;
window.open(googleUrl, '_blank');
};
const sideCover = document.querySelector('.collectionWidget.collectionWidgetData.withFavourites');
if (sideCover) sideCover.appendChild(button);
else console.warn('Элемент для вставки кнопки не найден.');
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createButton);
} else {
createButton();
}
}
// === 2. GOOGLE: Автопереход на Flicksbar по найденному ID Кинопоиска ===
if (/google\.com\/search/.test(url) || /duckduckgo\.com\//.test(url)) {
function showConfirmWithTimeout(flicksbarUrl, timeout = 5000) {
return new Promise((resolve) => {
const modal = document.createElement('div');
Object.assign(modal.style, {
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
padding: '20px',
backgroundColor: '#EEE8AA',
border: '1px solid #ccc',
zIndex: '9999',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
borderRadius: '8px',
});
const message = document.createElement('p');
message.style.color = 'black';
message.innerHTML = `<b>Переход на Flicksbar</b> <br><br>Перейти по ссылке: <b>${flicksbarUrl}</b> ?`;
modal.appendChild(message);
const okButton = document.createElement('button');
okButton.textContent = 'Да';
Object.assign(okButton.style, {
marginRight: '10px',
padding: '5px 10px',
backgroundColor: '#28a745',
color: 'white',
border: 'none',
cursor: 'pointer',
borderRadius: '5px',
});
okButton.onclick = () => {
resolve(true);
modal.remove();
};
modal.appendChild(okButton);
const cancelButton = document.createElement('button');
cancelButton.textContent = 'Нет';
Object.assign(cancelButton.style, {
padding: '5px 10px',
backgroundColor: '#dc3545',
color: 'white',
border: 'none',
cursor: 'pointer',
borderRadius: '5px',
});
cancelButton.onclick = () => {
resolve(false);
modal.remove();
};
modal.appendChild(cancelButton);
document.body.appendChild(modal);
setTimeout(() => {
resolve(false); //resolve(true); - если надо чтобы после таймера нажималась кнопка ОK
modal.remove();
}, timeout);
});
}
function observeResults() {
const observer = new MutationObserver(() => {
const kpLink = document.querySelector('a[href*="kinopoisk.ru/film/"]');
if (kpLink) {
observer.disconnect(); // Остановить наблюдение
tryRedirect(kpLink);
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
async function tryRedirect(kpLink) {
const urlParams = new URLSearchParams(window.location.search);
const type = urlParams.get('flcks_type'); // фильм / сериал (для Flicksbar)
const preferGoogle = GM_getValue('function3Enabled', true);
// ✅ Разрешаем редирект, если:
// - включён Google и есть параметр flcks_type
// - включён DuckDuckGo и есть параметр flcks_type
const allowRedirect =
(preferGoogle && type) || (!preferGoogle && type);
console.log ('urlParams: ', urlParams)
console.log ('type: ', type)
console.log ('preferGoogle: ', preferGoogle)
if (!allowRedirect) {
console.log('Редирект не выполняется: источник не соответствует настройке function3Enabled');
return;
}
const match = kpLink.href.match(/kinopoisk\.ru\/film\/(\d+)/);
if (!match) return;
const kpId = match[1];
//const urlParams = new URLSearchParams(window.location.search);
//const type = urlParams.get('flcks_type') || 'film';
const flicksbarUrl = `https://flcksbr.top/${type}/${kpId}/`;
if (GM_getValue('function2Enabled', true)) {
const answer = await showConfirmWithTimeout(flicksbarUrl, 5000);
if (answer) window.location.href = flicksbarUrl;
else console.log('Переход отменен пользователем или истек таймаут.');
} else {
window.location.href = flicksbarUrl;
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', tryRedirect);
} else {
observeResults();
}
}
// === 3. Flicksbar: изменение плеера и добавление постера и описания ===
if (/https?:\/\/(flicksbar\.mom|flcksbr\.top)\/(film|series)\/\d+\/?$/.test(location.href)) {
// Функция для отображения постера и описания в блоке справа
function displayMovieInfo(poster, description, titleTMP, originalTitleTMP, yearTMP, raitingTMP) {
const infoBox = document.getElementById("kinorium-info");
if (infoBox && poster && description && titleTMP && originalTitleTMP && yearTMP && raitingTMP) {
infoBox.innerHTML = `
<p style="font-size:25px;">${titleTMP} ${originalTitleTMP} ${yearTMP} ${raitingTMP}</p><br><img src="${poster}" alt="Постер" style="width:100%;border-radius:10px;margin-bottom:10px;display:flex;justify-content:center;align-items:center; ">
<p style="color:white;font-size:16px;line-height:1.4;">${description}</p>
`;
}
}
function clearFlicksbarInfo() {
console.log('✅ Очистка данных...');
GM_deleteValue('flicksbar_poster');
GM_deleteValue('flicksbar_description');
GM_deleteValue('flicksbar_titleTMP');
GM_deleteValue('flicksbar_originalTitleTMP');
GM_deleteValue('flicksbar_yearTMP');
GM_deleteValue('flicksbar_raitingTMP');
GM_deleteValue('flicksbar_currentUrlTMP');
}
// Убираем рекламу
function removeAds() {
const selectors = [
"#tgWrapper", "#TopAdMb", ".brand", ".topAdPad",
".adDown", "body > span", "body > script:first-child"
];
selectors.forEach(sel => {
const el = document.querySelector(sel);
if (el) el.remove();
});
}
// Структурируем страницу
function restructurePage() {
const main = document.querySelector(".mainContainer");
const player = document.querySelector(".kinobox");
if (!main || !player) return;
document.documentElement.style.overflowY = "auto"; // Включаем скроллинг на уровне всей страницы
document.body.style.overflowY = "auto"; // И на body тоже на всякий
document.body.style.background = "#1c1c1c";
player.style.minHeight = "0";
player.style.height = "100%";
player.style.width = "100%";
player.style.borderRadius = "8px";
const layout = document.createElement("div");
layout.style.display = "flex";
layout.style.flexWrap = "nowrap"; // не переносить элементы
layout.style.background = "#1c1c1c";
layout.style.gap = "10px";
layout.style.marginTop = "10px";
layout.style.alignItems = "stretch";
layout.style.width = "100%";
layout.style.boxSizing = "border-box";
layout.classList.add("responsive-layout");
const left = document.createElement("div");
left.style.flex = "7 1 0"; // flex-grow, flex-shrink, flex-basis
left.style.height = "100%";
left.style.minHeight = "500px";
left.style.marginLeft = "5px";
left.style.display = "flex";
left.style.flexDirection = "column";
left.style.minWidth = "0"; // чтобы flex мог ужимать
left.classList.add("responsive-left");
left.appendChild(player);
const right = document.createElement("div");
right.style.flex = "3 1 0";
right.style.fontFamily = "sans-serif";
right.style.color = "#eee";
right.style.borderRadius = "8px";
right.style.marginRight = "5px";
right.style.padding = "15px";
right.style.background = "#222";
right.style.overflow = "auto";
right.style.minWidth = "0";
right.classList.add("responsive-right");
const infoWrapper = document.createElement("div");
infoWrapper.id = "kinorium-info";
infoWrapper.style.flex = "flex";
infoWrapper.style.flexDirection = "column";
infoWrapper.style.gap = "20px";
infoWrapper.style.width = "100%";
right.appendChild(infoWrapper);
layout.appendChild(left);
layout.appendChild(right);
main.innerHTML = "";
main.style.display = "inline-block";
main.style.background = "#1c1c1c";
main.style.boxSizing = "border-box"; // на всякий
main.style.width = "100%";
main.appendChild(layout);
const style = document.createElement("style");
style.textContent = `
@media (max-width: 768px) and (orientation: portrait) {
.responsive-layout {
flex-direction: column !important;
margin-top: 2px !important;
}
.responsive-left {
width: auto !important;
height: auto !important;
margin: 2px !important;
min-height: auto !important;
}
.responsive-right {
flex: none !important;
}
}
`;
document.head.appendChild(style);
}
// Добавялем инфу про фильм
function loadAndDisplayInfo() {
const poster = GM_getValue('flicksbar_poster', null);
const description = GM_getValue('flicksbar_description', null);
const titleTMP = GM_getValue('flicksbar_titleTMP', null);
const originalTitleTMP = GM_getValue('flicksbar_originalTitleTMP', null);
const yearTMP = GM_getValue('flicksbar_yearTMP', null);
const raitingTMP = GM_getValue('flicksbar_raitingTMP', null);
const currentUrlTMP = GM_getValue('flicksbar_currentUrlTMP', null);
const infoContainer = document.querySelector("#kinorium-info");
if (!infoContainer) {
console.log('#kinorium-info element not found on the page.');
return;
}
infoContainer.innerHTML = "";
const dynamicStyle = document.createElement('style');
dynamicStyle.textContent = `
@keyframes fadeInScale {
0% {
opacity: 0;
transform: scale(0.95);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.fade-in-scale {
animation: fadeInScale 0.8s ease-out;
}
`;
const invisibleScrollbarStyle = document.createElement('style');
invisibleScrollbarStyle.textContent = `
/* Скроллбар невидим в обычном состоянии */
::-webkit-scrollbar {
width: 8px;
height: 8px;
background: transparent;
}
/* Трек (фон полосы) тоже прозрачный */
::-webkit-scrollbar-track {
background: transparent;
}
/* Ползунок скрыт, но появляется при наведении */
::-webkit-scrollbar-thumb {
background: transparent;
border-radius: 10px;
transition: background 0.3s ease;
}
/* При наведении — появляется аккуратный ползунок */
:hover::-webkit-scrollbar-thumb {
background: rgba(150, 150, 150, 0.7);
}
/* Для Firefox */
* {
scrollbar-width: thin;
scrollbar-color: transparent transparent;
}
*:hover {
scrollbar-color: rgba(150,150,150,0.7) transparent;
}
`;
document.head.appendChild(invisibleScrollbarStyle);
document.head.appendChild(dynamicStyle);
// Заголовок
if (titleTMP || originalTitleTMP || yearTMP) {
const titleElement = document.createElement("div");
titleElement.innerHTML = `
<div style="text-align: center; margin-bottom: 5px;">
<div style="font-size: 22px; font-weight: bold;">${titleTMP || ""}</div>
${(originalTitleTMP || yearTMP) ? `
<div style="font-size: 18px; margin-top: 2px;">
<span style="color: #aaa;">${originalTitleTMP || ""}</span>
${yearTMP ? `<span style="color: #fff;"> (${yearTMP})</span>` : ""}
</div>
` : ""}
</div>
`;
infoContainer.appendChild(titleElement);
}
// Рейтинг
if (raitingTMP) {
let ratingColor = "#aaa";
const ratingNum = parseFloat(raitingTMP);
if (ratingNum < 5) ratingColor = "red";
else if (ratingNum < 7) ratingColor = "orange";
else ratingColor = "lightgreen";
const ratingElement = document.createElement("p");
ratingElement.textContent = `Рейтинг: ${raitingTMP}`;
ratingElement.style.cssText = `
font-size: 18px;
color: ${ratingColor};
text-align: center;
margin: 0 0 5px;
`;
infoContainer.appendChild(ratingElement);
}
// Постер
if (poster) {
const posterElement = document.createElement("img");
posterElement.src = poster;
posterElement.alt = "Постер фильма";
posterElement.style.maxWidth = "100%";
posterElement.style.borderRadius = "12px";
posterElement.style.display = "block";
posterElement.style.margin = "0 auto 10px";
posterElement.className = "fade-in-scale"; // применяем нашу анимацию через класс
infoContainer.appendChild(posterElement);
posterElement.addEventListener('mouseover', () => {
posterElement.style.transform = 'scale(1.005)';
posterElement.style.boxShadow = '0 2px 6px rgba(0, 0, 0, 0.3)';
posterElement.style.boxShadow = 'none';
posterElement.style.cursor = 'pointer';
});
posterElement.addEventListener('mouseout', () => {
posterElement.style.transform = 'scale(1)';
posterElement.style.boxShadow = '0 2px 6px rgba(0, 0, 0, 0.3)';
posterElement.style.cursor = 'pointer';
});
posterElement.addEventListener('click', () => {
window.open(currentUrlTMP, '_blank');
});
}
// Описание
if (description) {
const descriptionWrapper = document.createElement("div");
descriptionWrapper.style.overflowY = "auto";
descriptionWrapper.style.maxHeight = "300px";
descriptionWrapper.style.paddingRight = "8px";
const descPara = document.createElement("p");
descPara.innerText = description;
descPara.style.fontSize = "16px";
descPara.style.lineHeight = "1.5";
descPara.style.textAlign = "justify";
descPara.style.boxShadow = "0 0 15px rgba(0,0,0,0.4)";
descPara.style.borderRadius = "10px";
descPara.style.padding = "5px";
descPara.style.background = "#1c1c1c";
descPara.className = "fade-in-scale"; // применяем нашу анимацию через класс
descriptionWrapper.appendChild(descPara);
infoContainer.appendChild(descriptionWrapper);
// Плавное уменьшение шрифта при переполнении
setTimeout(() => {
let fontSize = 16;
while (descriptionWrapper.scrollHeight > descriptionWrapper.clientHeight && fontSize > 12) {
fontSize--;
descPara.style.fontSize = fontSize + "px";
}
}, 100);
}
console.log('✅ Информация успешно отображена (гибкий режим)');
}
// Вызываем всё нужное
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
removeAds();
restructurePage();
loadAndDisplayInfo();
});
} else {
removeAds();
restructurePage();
loadAndDisplayInfo();
}
// Очистка данных при закрытии страницы или переходе на другой сайт
window.addEventListener("unload", () => {
if (GM_getValue('function1Enabled', true)) {
console.log("✅ Очистка хранилища...");
clearFlicksbarInfo(); // Очистка данных
} else {
console.log("✅ Очистка хранилища отключена...");
}
});
}
})();