// ==UserScript==
// @name xvideos-优化
// @version 1.0.2
// @namespace https://sleazyfork.org/zh-CN/users/1461640-%E6%98%9F%E5%AE%BF%E8%80%81%E9%AD%94
// @author 星宿老魔
// @description 近期标签记忆、已观看隐藏、稍后观看管理,自动播放-宽屏-高画质
// @match *://*.xvideos.com/*
// @icon https://www.xvideos.com/favicon-32x32.png
// @license MIT
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_notification
// @run-at document-start
// ==/UserScript==
(function(){"use strict";const CONFIG={storageKeys:{recentTags:"xv_recent_tags",hideWatched:"xv_hide_watched"},ui:{maxTags:25,debounceDelay:300}}
;class StyleManager{static init(){this.addBaseStyles()}static addBaseStyles(){
GM_addStyle('\n /* 隐藏广告元素 */\n #warning-survey, /* 蓝色警告横幅 */\n #video-right, /* 视频右侧广告容器 */\n #ad-footer /* 页面底部广告 */ { display: none !important; }\n \n /* 隐藏菜单栏广告链接 */\n .head__menu-line__main-menu__lvl1.red-ticket,\n .head__menu-line__main-menu__lvl1.live-cams,\n .head__menu-line__main-menu__lvl1.nutaku-games,\n .head__menu-line__main-menu__lvl1.ignore-popunder {\n display: none !important;\n }\n \n /* 隐藏包含特定链接的菜单项 */\n a[href*="xvideos.red/red/videos"],\n a[href*="zlinkt.com"],\n a[href*="nutaku.net"] {\n display: none !important;\n }\n \n /* 隐藏父级菜单项 */\n li:has(> a[href*="xvideos.red/red/videos"]),\n li:has(> a[href*="zlinkt.com"]),\n li:has(> a[href*="nutaku.net"]) {\n display: none !important;\n }\n \n /* 隐藏红色横幅广告 */\n div[style*="background: rgb(222, 38, 0)"],\n div[id*="9o6lsm8aj09wav6bf9"] {\n display: none !important;\n }\n \n /* 隐藏包含xvideos.red推广的元素 */\n p:has(> a[href*="xvideos.red?pmsc=header_adblock"]) {\n display: none !important;\n }\n \n /* 隐藏高级内容推广广告 */\n .premium-results-line,\n .premium-search-on-free,\n div[class*="premium-results"],\n .thumb-block.premium-search-on-free {\n display: none !important;\n }\n \n /* 隐藏包含高级标记的缩略图 */\n .thumb-block:has(.is-purchased-mark),\n .thumb-under:has(.is-purchased-mark) {\n display: none !important;\n }\n \n /* 隐藏高级内容链接 */\n a[href*="/c/p:1/"]:has(.icon-f.icf-ticket-red),\n a.see-more:has(.icon-f.icf-ticket-red) {\n display: none !important;\n }\n \n /* 隐藏包含XVIDEOS高级推广的父容器 */\n div:has(> .premium-results-line-title),\n div:has(> .premium-results-line-see-more) {\n display: none !important;\n }\n \n /* 基础动画 */\n .xv-fade-in {\n animation: xvFadeIn 300ms ease-in-out;\n }\n \n @keyframes xvFadeIn {\n from { opacity: 0; transform: translateY(-5px); }\n to { opacity: 1; transform: translateY(0); }\n }\n \n /* 近期标签容器样式 */\n #recent-tags-container { \n background: #f5f7fa !important; \n border-bottom: 1px solid #e5e7eb; \n padding: 6px 10px; \n line-height: 1; \n }\n #recent-tags-container .xv-title { \n font-weight: 600; \n color: #374151; \n margin-right: 8px; \n }\n #recent-tags-container .xv-tag { \n display: inline-block; \n font-size: 12px; \n color: #374151; \n text-decoration: none; \n background: #ffffff; \n border: 1px solid #d1d5db; \n border-radius: 999px; \n padding: 3px 10px; \n margin: 0 3px; \n }\n #recent-tags-container .xv-tag:hover { \n background: #f9fafb; \n border-color: #9ca3af; \n }\n\n /* 切换开关样式 */\n .xv-toggle {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: #495057;\n user-select: none;\n }\n \n .xv-switch {\n position: relative;\n width: 46px;\n height: 24px;\n }\n \n .xv-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n \n .xv-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: #ced4da;\n border-radius: 24px;\n transition: background 0.3s ease;\n box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);\n }\n \n .xv-slider:before {\n position: absolute;\n content: "";\n height: 20px;\n width: 20px;\n left: 2px;\n top: 2px;\n background: #ffffff;\n border-radius: 50%;\n transition: transform 0.3s ease;\n box-shadow: 0 2px 4px rgba(0,0,0,0.2);\n }\n \n .xv-switch input:checked + .xv-slider {\n background: #28a745;\n }\n \n .xv-switch input:checked + .xv-slider:before {\n transform: translateX(22px);\n }\n \n /* 稍后观看:缩略图右下角移除按钮 */\n .xv-watchlater-remove { \n position: absolute; \n right: 6px; \n bottom: 6px; \n width: 26px; \n height: 26px; \n border-radius: 50%;\n background: rgba(239,68,68,.92); \n color: #fff; \n display: flex; \n align-items: center; \n justify-content: center; \n cursor: pointer;\n font-weight: 700; \n line-height: 1; \n box-shadow: 0 2px 6px rgba(0,0,0,.25); \n z-index: 5; \n }\n .xv-watchlater-remove:hover { \n background: rgba(220,38,38,.98); \n }\n .xv-watchlater-remove .xv-remove-icon { \n pointer-events: none; \n font-size: 16px; \n }\n \n /* 隐藏已观看眼睛图标样式 */\n .xv-eye-icon {\n transition: all 0.3s ease;\n }\n \n /* 悬停效果 - 与原生按钮一致 */\n #xv-hide-watched-menu-item .head__menu-line__main-menu__lvl1:hover .xv-eye-icon {\n color: #d32f2f !important; /* 悬停时统一显示红色 */\n }\n \n #xv-hide-watched-menu-item .head__menu-line__main-menu__lvl1:active .xv-eye-icon {\n color: #b71c1c !important; /* 点击时更深的红色 */\n }\n ')
}}const e={select(e,t=document){return t.querySelector(e)},selectAll(e,t=document){return t.querySelectorAll(e)},create(e,t={},n=""){
const i=document.createElement(e);return Object.entries(t).forEach(([e,t])=>{
"className"===e?i.className=t:"style"===e&&"object"==typeof t?Object.assign(i.style,t):e.startsWith("on")&&"function"==typeof t?i.addEventListener(e.slice(2),t):i.setAttribute(e,t)
}),n&&(i.innerHTML=n),i}},t={get(e,t=null){try{const n="undefined"!=typeof GM_getValue?GM_getValue(e):localStorage.getItem(e)
;return null!=n?JSON.parse(n):t}catch(n){return void 0,t}},set(e,t){try{const n=JSON.stringify(t)
;return"undefined"!=typeof GM_setValue?GM_setValue(e,n):localStorage.setItem(e,n),!0}catch(n){return void 0,!1}}};function debounce(e,t){let n
;return function(...i){clearTimeout(n),n=setTimeout(()=>e(...i),t)}}class AdBlocker{static init(){this.setupStaticAdRemoval(),
this.setupDynamicAdRemoval()}static setupStaticAdRemoval(){}static setupDynamicAdRemoval(){document.addEventListener("DOMContentLoaded",()=>{
this.removeRedBanner(),this.removeMenuAds(),this.removePremiumAds(),this.observeAds()})}static removeRedBanner(){
const t=e.select('a[href*="xvideos.red?pmsc=header_adblock"]');if(t){const e=t.closest('div[style*="background: rgb(222, 38, 0)"]')
;e&&!e.dataset.xvRemoved&&(e.remove(),e.dataset.xvRemoved="true")}}static removeMenuAds(){let t=0
;[".head__menu-line__main-menu__lvl1.red-ticket",".head__menu-line__main-menu__lvl1.live-cams",".head__menu-line__main-menu__lvl1.nutaku-games",".head__menu-line__main-menu__lvl1.ignore-popunder",'a[href*="xvideos.red/red/videos"]','a[href*="zlinkt.com"]','a[href*="nutaku.net"]'].forEach(n=>{
e.selectAll(n).forEach(e=>{if(e&&!e.dataset.xvRemoved){const n=e.closest("li")||e;n.dataset.xvRemoved||(n.remove(),n.dataset.xvRemoved="true",t++)}})
});const n=e.select('p[id*="9o6lsm8aj09wav6bf9"]');if(n&&!n.dataset.xvRemoved){const e=n.closest('div[style*="background: rgb(222, 38, 0)"]')
;e&&(e.remove(),e.dataset.xvRemoved="true",t++)}t>0,0}static removePremiumAds(){
[".premium-results-line",".premium-search-on-free",'div[class*="premium-results"]',".thumb-block.premium-search-on-free",'a[href*="/c/p:1/"]'].forEach(t=>{
e.selectAll(t).forEach(e=>{
e&&!e.dataset.xvPremiumRemoved&&(e.classList.contains("premium-results-line")||e.classList.contains("premium-search-on-free")||e.querySelector(".premium-results-line-title")||e.querySelector(".is-purchased-mark"))&&(e.remove(),
e.dataset.xvPremiumRemoved="true")})}),e.selectAll("a.see-more").forEach(e=>{const t=e.textContent||""
;if(t.includes("XVIDEOS")&&(t.includes("高级")||t.includes("Premium")||e.querySelector(".icon-f.icf-ticket-red"))){
const t=e.closest(".premium-results-line")||e.closest('div[class*="premium"]');t&&!t.dataset.xvPremiumRemoved&&(t.remove(),
t.dataset.xvPremiumRemoved="true")}}),e.selectAll(".thumb-block").forEach(e=>{
(e.classList.contains("premium-search-on-free")||e.querySelector(".is-purchased-mark"))&&(e.dataset.xvPremiumRemoved||(e.remove(),
e.dataset.xvPremiumRemoved="true"))})}static observeAds(){new MutationObserver(debounce(()=>{this.removeRedBanner(),this.removeMenuAds(),
this.removePremiumAds()},CONFIG.ui.debounceDelay)).observe(document.body,{childList:!0,subtree:!0})}}function n(){setTimeout(()=>{try{
if("undefined"!=typeof html5player&&html5player&&"function"==typeof html5player.toggleExpand&&html5player.toggleExpand(),
"undefined"!=typeof html5player&&html5player&&html5player.hlsobj&&html5player.hlsobj.levels&&html5player.hlsobj.levels.length>0){
const e=Math.min(4,html5player.hlsobj.levels.length-1),t=html5player.hlsobj;void 0!==t.loadLevel&&(t.loadLevel=e)}
"undefined"!=typeof html5player&&html5player&&"function"==typeof html5player.play&&!html5player.playClicked&&setTimeout(()=>{
html5player&&!html5player.playClicked&&html5player.play&&(html5player.playClicked=!0,html5player.play())},200)}catch(e){}},800)}function i(){
const e=document.querySelector(".listing_filters");if(e)return{node:e,position:"beforebegin"};const t=document.querySelector(".head__menu-line")
;if(t)return{node:t,position:"afterend"};const n=document.querySelector("#header .header-bottom")||document.querySelector(".header-bottom");return n?{
node:n,position:"afterend"}:null}function a(){const e=i();if(e&&!document.getElementById("recent-tags-container")){
const t=document.createElement("div");t.id="recent-tags-container",t.style.backgroundColor="#f1f1f1",t.style.padding="5px 10px",
t.style.textAlign="left",t.style.borderBottom="1px solid #ddd",t.style.lineHeight="1.8",t.style.whiteSpace="nowrap",t.style.overflowX="auto",
t.style.overflowY="hidden",t.style.position="relative",t.style.width="100%",e.node.insertAdjacentElement(e.position,t)}c()}function s(){
return t.get(CONFIG.storageKeys.recentTags,[])}function r(e){t.set(CONFIG.storageKeys.recentTags,e)}function o(e){let t=s()
;t=t.filter(t=>t.href!==e.href),t.unshift(e),t.length>CONFIG.ui.maxTags&&(t=t.slice(0,CONFIG.ui.maxTags)),r(t)}function c(){
const e=document.getElementById("recent-tags-container");if(!e)return;e.innerHTML="";const t=s(),n=document.createElement("div")
;if(n.className="xv-tags",e.appendChild(n),t.length>0){const e=document.createElement("span");e.className="xv-title",e.textContent="近期标签:",
n.appendChild(e),t.forEach(e=>{const t=document.createElement("a");t.href=e.href,t.textContent=e.text,t.className="xv-tag",n.appendChild(t)})}}
document.addEventListener("click",function(e){const t=e.target?.closest("a");t&&t.classList.contains("is-keyword")&&t.getAttribute("href")&&(o({
text:t.textContent?.trim()||"",href:t.getAttribute("href")||""}),c())});const l=class{static init(){this.loadState(),
this.isListingPage()&&(this.setupObserver(),this.isEnabled&&(this.applyHiding(),setTimeout(()=>this.applyHiding(),2e3)))}static loadState(){
this.isEnabled=t.get(CONFIG.storageKeys.hideWatched,!1)}static toggle(e){this.isEnabled=e,t.set(CONFIG.storageKeys.hideWatched,e),
e?(this.applyHiding(),setTimeout(()=>this.applyHiding(),500)):this.showAllVideos()}static applyHiding(){
this.isEnabled&&e.selectAll(".thumb-block:not([data-xv-hidden]), .video-container:not([data-xv-hidden])").forEach(e=>{
this.isWatchedCard(e)?(e.style.display="none",e.dataset.xvHidden="1"):e.dataset.xvHidden="0"})}static isWatchedCard(e){
return!(!e||!e.classList.contains("viewed")&&!e.querySelector(".video-viewed")&&!e.querySelector(".viewedIcon"))}static showAllVideos(){
e.selectAll('[data-xv-hidden="1"]').forEach(e=>{e.style.display="",e.dataset.xvHidden="0"})}static setupObserver(){new MutationObserver(debounce(()=>{
this.isEnabled&&this.applyHiding()},CONFIG.ui.debounceDelay)).observe(document.body,{childList:!0,subtree:!0})}static isListingPage(){
return"other"!==this.getCurrentPageType()}static getCurrentPageType(){const e=location.pathname||"",params=new URLSearchParams(location.search)
;return e.startsWith("/video")?"video":e.startsWith("/c/")?"category":e.startsWith("/tags")?"tags":e.startsWith("/best")?"best":params.has("k")?"search":"/"===e||""===e?"home":"other"
}};l.isEnabled=!1;let d=l;class MenuIntegration{static init(){this.isListingPage()&&(this.addHideWatchedMenuItem(),setTimeout(()=>{
d&&this.updateMenuItemState(t.get(CONFIG.storageKeys.hideWatched,!1))},100))}static addHideWatchedMenuItem(){
if(e.select("#xv-hide-watched-menu-item"))return;let t=e.select(".head__menu-line__main-menu"),n="main-menu"
;if(!(t||(t=e.select(".header-bottom-inner"),n="header-bottom-inner",t||(t=e.select(".head__menu-line"),n="menu-line",t||(t=e.select("header .menu"),
n="header-menu",t||(t=e.select("header"),n="header",t))))))return;const i=this.createHideWatchedMenuItem(n);t.appendChild(i)}
static createHideWatchedMenuItem(n){const i=t.get(CONFIG.storageKeys.hideWatched,!1);if("header-bottom-inner"===n){const t=e.create("a",{
id:"xv-hide-watched-menu-item",className:"header-link",href:"#",onclick:e=>{e.preventDefault(),this.toggleHideWatched()}}),n=e.create("span",{
className:"icon-f "+(i?"icf-eye icf-red-crossed":"icf-eye")}),a=e.create("span",{className:"item-title"},i?"隐藏已观看":"显示已观看");return t.appendChild(n),
t.appendChild(a),t}{const t=e.create("li",{id:"xv-hide-watched-menu-item"}),n=e.create("a",{className:"head__menu-line__main-menu__lvl1",href:"#",
onclick:e=>{e.preventDefault(),this.toggleHideWatched()}}),a=e.create("span",{className:`icon-f ${i?"icf-eye icf-red-crossed":"icf-eye"} xv-eye-icon`
}),s=e.create("span",{className:"main-cats-title"},i?" 隐藏已观看 ":" 显示已观看 ");return n.appendChild(a),n.appendChild(s),t.appendChild(n),t}}
static toggleHideWatched(){const e=!t.get(CONFIG.storageKeys.hideWatched,!1);t.set(CONFIG.storageKeys.hideWatched,e),this.updateMenuItemState(e),
d&&d.toggle(e)}static updateMenuItemState(t){const n=e.select("#xv-hide-watched-menu-item");if(n){
const e=n.querySelector(".icon-f"),i=n.querySelector(".main-cats-title")||n.querySelector(".item-title")
;e&&i&&(t?(e.className=e.className.includes("xv-eye-icon")?"icon-f icf-eye icf-red-crossed xv-eye-icon":"icon-f icf-eye icf-red-crossed",
e.style.color="",
i.textContent="main-cats-title"===i.className?" 隐藏已观看 ":"隐藏已观看"):(e.className=e.className.includes("xv-eye-icon")?"icon-f icf-eye xv-eye-icon":"icon-f icf-eye",
e.style.color="",i.textContent="main-cats-title"===i.className?" 显示已观看 ":"显示已观看"))}}static isListingPage(){return"other"!==d.getCurrentPageType()}}
const m=class _WatchLaterManager{static init(){if(!this.isWatchLaterPage())return void 0,void 0;void 0,this.hookNetworkForTokens(),
this.probePlaylistTokens(),this.setupObserver(),this.addRemoveButtons()}static isWatchLaterPage(){return location.pathname.startsWith("/watch-later")}
static hookNetworkForTokens(){try{const e=window.fetch;window.fetch=async function(t,init){const n=await e.call(this,t,init)
;return _WatchLaterManager.tryCaptureTokensFromResponse(t,n),n}}catch{}try{let e=function(){const e=new t,n=e.open;return e.open=function(e,t){
return this.__xv_url=t,n.apply(this,arguments)},e.addEventListener("load",function(){_WatchLaterManager.tryCaptureTokensFromXHR(this.__xv_url,this)}),
e};const t=window.XMLHttpRequest;window.XMLHttpRequest=e}catch{}}static setupObserver(){new MutationObserver(debounce(()=>{this.addRemoveButtons()
},CONFIG.ui.debounceDelay)).observe(document.body,{childList:!0,subtree:!0})}static addRemoveButtons(){
e.selectAll(".video-container, .thumb-block").forEach(e=>{if("1"===e.dataset.xvRemBtn)return
;const t=e.querySelector(".video-thumb, .thumb, a:has(img), .thumb-inside"),n=e.querySelector('img[data-videoid], img[id^="pic_"]');if(!t||!n)return
;const i=this.extractVideoId(e);i&&this.addRemoveButton(e,i,t)})}static addRemoveButton(t,n,i){const a=e.create("div",{
className:"xv-watchlater-remove",title:"从稍后观看移除",onclick:e=>{e.preventDefault(),e.stopPropagation(),this.removeFromWatchLater(n,t)}
}),s=e.create("span",{className:"xv-remove-icon"},"×");a.appendChild(s),"static"===getComputedStyle(i).position&&(i.style.position="relative"),
i.appendChild(a),t.dataset.xvRemBtn="1"}static async removeFromWatchLater(e,t){try{
if(this.playlistId&&this.csrfRemove||await this.probePlaylistTokens(),!this.playlistId||!this.csrfRemove)return void 0,void 0
;const n=`/api/playlists/list/${this.playlistId}/remove/${e}`,i=`csrf=${encodeURIComponent(this.csrfRemove)}`,a=await fetch(n,{method:"POST",headers:{
"content-type":"application/x-www-form-urlencoded; charset=UTF-8",accept:"application/json, text/javascript, */*; q=0.01"},credentials:"same-origin",
body:i}),s=await a.json();s&&s.result?t&&t.parentElement&&t.parentElement.removeChild(t):void 0}catch(n){void 0}}static extractVideoId(e){
const t=e.querySelector('img[data-videoid], img[id^="pic_"]');if(t){
const e=t.getAttribute("data-videoid")||(t.id&&t.id.startsWith("pic_")?t.id.replace("pic_",""):null);if(e)return e}if(e.dataset.id)return e.dataset.id
;const n=e.querySelector('a[href*="/video"]');if(n){const e=n.getAttribute("href");if(e){const t=e.match(/\/video(\d+)/);if(t)return t[1]}}return null
}static tryCaptureTokensFromResponse(e,t){try{const n="string"==typeof e?e:e&&e.url?e.url:"";if(!/\/api\/playlists\/list\/(\d+)/.test(n))return
;const i=RegExp.$1;t.clone().json().then(e=>{e&&e.list&&e.list.csrf&&e.list.csrf.remove&&(this.playlistId=i,this.csrfRemove=e.list.csrf.remove)
}).catch(()=>{})}catch{}}static tryCaptureTokensFromXHR(e,t){try{if(!/\/api\/playlists\/list\/(\d+)/.test(e))return;const n=RegExp.$1,i=t.responseText
;if(!i)return;const a=JSON.parse(i);a&&a.list&&a.list.csrf&&a.list.csrf.remove&&(this.playlistId=n,this.csrfRemove=a.list.csrf.remove)}catch{}}
static async probePlaylistTokens(){if(!this.playlistId){const e=document.querySelector('a[href*="?pl="]');if(e){
const t=new URL(e.href,location.origin).searchParams.get("pl");t&&(this.playlistId=t)}}if(this.playlistId&&!this.csrfRemove)try{
const e=await fetch(`/api/playlists/list/${this.playlistId}`,{method:"POST",credentials:"same-origin"}),t=await e.json()
;t&&t.list&&t.list.csrf&&t.list.csrf.remove&&(this.csrfRemove=t.list.csrf.remove)}catch{}}};m.playlistId=null,m.csrfRemove=null;let h=m
;StyleManager.init(),AdBlocker.init(),document.addEventListener("DOMContentLoaded",()=>{"other"!==d.getCurrentPageType()&&MenuIntegration.init(),a(),
d.init(),h.init(),window.location.href.includes("/video")&&n()}),window.addEventListener("load",()=>{
"other"!==d.getCurrentPageType()&&setTimeout(()=>{t.get(CONFIG.storageKeys.hideWatched,!1)&&d.toggle(!0)},1e3),
window.location.href.includes("/video")&&setTimeout(()=>{"undefined"!=typeof html5player&&html5player&&n()},1e3)})})();