Расширение для сайтов anime365 и smotret-anime
当前为
// ==UserScript==
// @name anime365-ext-beta
// @namespace http://tampermonkey.net/
// @version 0.33-beta
// @description Расширение для сайтов anime365 и smotret-anime
// @author https://greasyfork.org/ru/users/1065796-kazaev
// @match https://anime365.ru/catalog/*
// @match https://anime-365.ru/catalog/*
// @match https://smotret-anime.com/catalog/*
// @match https://hentai365.ru/catalog/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=anime365.ru
// @license MIT
// @grant GM_addStyle
// ==/UserScript==
(function () {
'use strict';
class Logger {
static settings = {
console: true,
ui: true,
theatreMode: true,
autoNextEpisode: {
enabled: true,
threshold: 99
},
autoplay: true,
};
static init() {
if (this.settings.ui && !document.getElementById("anime365-log")) {
const logDiv = document.createElement("div");
logDiv.id = "anime365-log";
logDiv.style.cssText = `
position: fixed;
bottom: 1rem;
right: 1rem;
background: rgba(0, 0, 0, 0.8);
color: #fff;
padding: 0.5rem 1rem;
font-size: 0.9rem;
border-radius: 0.5rem;
font-family: monospace;
z-index: 9999;
white-space: nowrap;
`;
document.body.appendChild(logDiv);
}
}
static log(message, type = 'info') {
const colors = {
info: "#00aaff",
success: "#00cc66",
warn: "#ffaa00",
error: "#ff4444"
};
if (this.settings.console) {
const color = colors[type] || "#00aaff";
console.log(`%c[anime365-ext] ${message}`, `color: ${color}`);
}
if (this.settings.ui) {
const logEl = document.getElementById("anime365-log");
if (!logEl) return;
logEl.style.borderLeft = `4px solid ${colors[type] || "#00aaff"}`;
logEl.textContent = `[anime365-ext] ${message}`;
}
}
}
class TheatreMode {
constructor() {
this.styleRef = null;
this.closeBtn = this.createCloseButton();
this.active = false;
this.init();
}
init() {
const bg = document.querySelector(".full-background");
if (bg) bg.after(this.closeBtn);
document.addEventListener("keydown", (e) => {
if (!Logger.settings.theatreMode) return;
if (e.altKey && e.code === "KeyT") {
e.preventDefault();
this.toggle();
} else if (e.key === "Escape") {
e.preventDefault();
this.close();
}
});
this.closeBtn.addEventListener("click", () => this.close());
// Автовоспроизведение при открытии, если включено в настройках
if (Logger.settings.autoplay) {
const autoplayInterval = setInterval(() => {
const doc = this.getIframeDocument();
const playBtn = doc?.querySelector(".vjs-big-play-button");
if (playBtn) {
playBtn.click();
Logger.log("Автовоспроизведение: нажата кнопка Play", 'success');
clearInterval(autoplayInterval);
}
}, 500);
}
}
getIframeDocument() {
return document.querySelector("iframe")?.contentDocument || null;
}
toggle() {
if (!Logger.settings.theatreMode) return;
const container = this.getVideoContainer();
if (!container) return;
container.classList.contains("cinema-mode") ? this.close() : this.open();
}
open() {
if (!Logger.settings.theatreMode) return;
const container = this.getVideoContainer();
if (!container) return;
container.classList.add("cinema-mode");
this.styleRef = GM_addStyle(`
.m-translation-player > .card-image > .video-container.cinema-mode {
position: fixed !important;
z-index: 10;
height: 100vh;
width: 100vw;
left: 0;
top: 0;
margin: 0;
padding: 0;
}
body {
overflow: hidden !important;
}
`);
this.closeBtn.style.display = "block";
this.active = true;
Logger.log("Включён кинотеатр", 'success');
}
close() {
if (!this.active) return;
const container = this.getVideoContainer();
if (!container) return;
container.classList.remove("cinema-mode");
this.closeBtn.style.display = "none";
if (this.styleRef) {
document.getElementById(this.styleRef.id)?.remove();
this.styleRef = null;
}
this.active = false;
Logger.log("Кинотеатр выключен", 'warn');
}
getVideoContainer() {
return document.querySelector(".m-translation-player > .card-image > .video-container");
}
createCloseButton() {
const btn = document.createElement("img");
btn.id = "cinemaCloseButton";
btn.style.cssText = "z-index: 20; position: fixed; top: 0; right: 0; margin: 1rem; width: 2rem; display: none; cursor: pointer;";
btn.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'%3E%3Cpath d='M16 8L8 16M12 12L16 16M8 8L10 10M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E";
return btn;
}
}
class AutoNextEpisode {
constructor() {
this.lastProgress = null;
this.lastLoggedProgress = null;
this.interval = setInterval(() => this.checkProgress(), 1000);
}
getIframeDocument() {
return document.querySelector("iframe")?.contentDocument || null;
}
checkProgress() {
if (!Logger.settings.autoNextEpisode.enabled) return;
const doc = this.getIframeDocument();
if (!doc) return;
const playProgressEl = doc.querySelector(".vjs-play-progress");
if (!playProgressEl) return;
// Получаем progress в процентах (с десятичной точностью)
const widthStyle = playProgressEl.style.width;
if (!widthStyle.endsWith("%")) return;
const progress = parseFloat(widthStyle);
if (isNaN(progress)) return;
const video = doc.querySelector("video");
if (!video) return;
// Логируем только если видео играет (не на паузе)
if (!video.paused) {
const progressFormatted = progress.toFixed(1);
if (progressFormatted !== this.lastLoggedProgress) {
Logger.log(`Прогресс видео: ${progressFormatted}%`, 'info');
this.lastLoggedProgress = progressFormatted;
}
}
// Если прогресс >= заданного порога - переходим к следующей серии
if (progress >= Logger.settings.autoNextEpisode.threshold) {
Logger.log(`Видео достигло ${progress.toFixed(1)}%. Переход к следующей серии...`, 'success');
this.goToNextEpisode();
// Не останавливаем интервал, чтобы скрипт продолжал работать после перехода
return;
}
// Если прогресс застывает на 0% и кнопка play доступна - повторно запускаем видео
if (this.lastProgress === 0 && progress === 0 && doc.querySelector(".vjs-big-play-button")) {
doc.querySelector(".vjs-big-play-button").click();
Logger.log("Прогресс 0%. Повторный запуск плеера", 'warn');
}
this.lastProgress = progress;
}
goToNextEpisode() {
// Кликаем по ссылке, а не по иконке
const nextLink = document.querySelector(".m-select-sibling-episode > a:nth-child(2)");
if (nextLink) {
Logger.log("Переходим к следующей серии", 'info');
nextLink.click();
// Если переход не срабатывает через клик, можно раскомментировать:
// window.location.href = nextLink.href;
} else {
Logger.log("Кнопка перехода к следующей серии не найдена", 'error');
}
}
}
function onDocumentReady(callback) {
document.addEventListener('page:load', callback);
document.addEventListener('turbolinks:load', callback);
if (document.readyState !== 'loading') {
callback();
} else {
document.addEventListener('DOMContentLoaded', callback);
}
}
onDocumentReady(() => {
// Настройки
Logger.settings.console = true; // Логирование в консоль
Logger.settings.ui = true; // Логирование в UI
Logger.settings.theatreMode = true; // Включение режима кинотеатра
Logger.settings.autoNextEpisode = {
enabled: true, // Включить авто-переход к следующей серии
threshold: 97 // Процент прогресса для перехода (по умолчанию 97)
};
Logger.settings.autoplay = true; // Включить автозапуск видео
Logger.init();
Logger.log("Инициализация расширения...", 'info');
if (Logger.settings.theatreMode) {
new TheatreMode();
}
new AutoNextEpisode();
});
})();