// ==UserScript== // @name New Userscript NCD3-1 // @namespace https://docs.scriptcat.org/ // @version 0.1.0 // @description try to take over the world! // @author LiuQi // @match https://*/* // @grant none // @noframes // ==/UserScript== (function() { 'use strict'; // 资源缓存 const loadedSet = new Set(); const videoThumbCache = new Map(); const mediaDomMap = new Map(); // DOM缓存,增量渲染优化 const mediaConfig = { globalMute: true, autoPlay: true, filterMinDuration: 10 }; let phoneGalleryEl = null; let fullPreviewWrap = null; let controlBar = null; let tabBar = null; let phoneHomeBtn = null; let allMediaList = []; let currentTab = 'all'; let currentMediaIndex = 0; let touchStartX = 0; let touchMoveX = 0; const slideThreshold = 40; let slideThrottle = false; // 滑动节流标记 // ===================== 1. iOS磨砂整机DOM结构 ===================== const phoneBox = document.createElement('div'); phoneBox.className = 'ios-blur-card'; const phoneNotch = document.createElement('div'); phoneNotch.className = 'ios-notch'; // iOS原生分段控件Tab(替代旧胶囊按钮,解决别扭问题) tabBar = document.createElement('div'); tabBar.className = 'ios-segment-control'; const tabAll = document.createElement('button'); tabAll.className = 'seg-btn active'; tabAll.textContent = '全部'; const tabImage = document.createElement('button'); tabImage.className = 'seg-btn'; tabImage.textContent = '图片'; const tabVideo = document.createElement('button'); tabVideo.className = 'seg-btn'; tabVideo.textContent = '视频'; tabBar.append(tabAll, tabImage, tabVideo); // 轻量化控制按钮栏 controlBar = document.createElement('div'); controlBar.className = 'ios-control-row'; const muteBtn = document.createElement('button'); muteBtn.className = 'ios-small-btn mute-btn'; muteBtn.textContent = '静音开'; const autoPlayBtn = document.createElement('button'); autoPlayBtn.className = 'ios-small-btn autoplay-btn'; autoPlayBtn.textContent = '自动播放开'; controlBar.append(muteBtn, autoPlayBtn); const screenContainer = document.createElement('div'); screenContainer.className = 'ios-screen-wrap'; phoneGalleryEl = document.createElement('ul'); phoneGalleryEl.className = 'media-gallery'; fullPreviewWrap = document.createElement('div'); fullPreviewWrap.className = 'full-preview-layer'; fullPreviewWrap.style.display = 'none'; phoneHomeBtn = document.createElement('div'); phoneHomeBtn.className = 'ios-home-button'; phoneHomeBtn.addEventListener('click', closeFullPreview); screenContainer.append(phoneGalleryEl, fullPreviewWrap); phoneBox.append(phoneNotch, tabBar, controlBar, screenContainer, phoneHomeBtn); document.body.prepend(phoneBox); // ===================== 2. iOS分段Tab切换逻辑 ===================== function switchTab(type) { currentTab = type; document.querySelectorAll('.seg-btn').forEach(btn => btn.classList.remove('active')); if (type === 'all') tabAll.classList.add('active'); if (type === 'image') tabImage.classList.add('active'); if (type === 'video') tabVideo.classList.add('active'); renderFilterMedia(); } tabAll.addEventListener('click', () => switchTab('all')); tabImage.addEventListener('click', () => switchTab('image')); tabVideo.addEventListener('click', () => switchTab('video')); // ===================== 3. 滑动切换 + 节流优化 ===================== function handleSlideSwitch() { if (slideThrottle) return; slideThrottle = true; const diff = touchMoveX - touchStartX; if (diff < -slideThreshold) nextMedia(); if (diff > slideThreshold) prevMedia(); touchStartX = 0; touchMoveX = 0; setTimeout(() => slideThrottle = false, 250); } // 触屏事件 fullPreviewWrap.addEventListener('touchstart', e => touchStartX = e.touches[0].clientX); fullPreviewWrap.addEventListener('touchmove', e => touchMoveX = e.touches[0].clientX); fullPreviewWrap.addEventListener('touchend', handleSlideSwitch); // PC鼠标拖拽 fullPreviewWrap.addEventListener('mousedown', e => touchStartX = e.clientX); fullPreviewWrap.addEventListener('mousemove', e => { if (e.buttons === 1) touchMoveX = e.clientX; }); fullPreviewWrap.addEventListener('mouseup', handleSlideSwitch); // 键盘切换 document.addEventListener('keydown', e => { if (fullPreviewWrap.style.display === 'none') return; if (e.key === 'ArrowLeft') prevMedia(); if (e.key === 'ArrowRight') nextMedia(); }); function prevMedia() { if (currentMediaIndex <= 0) return; currentMediaIndex--; openMediaByIndex(currentMediaIndex); } function nextMedia() { if (currentMediaIndex >= allMediaList.length - 1) return; currentMediaIndex++; openMediaByIndex(currentMediaIndex); } function openMediaByIndex(index) { const media = allMediaList[index]; if (media.type === 'image') openFullImage(media.src); if (media.type === 'video') openFullVideo(media.src); if (media.type === 'iframe-video') openFullIframe(media.src); } muteBtn.addEventListener('click', () => { mediaConfig.globalMute = !mediaConfig.globalMute; muteBtn.textContent = mediaConfig.globalMute ? '静音开' : '静音关'; }); autoPlayBtn.addEventListener('click', () => { mediaConfig.autoPlay = !mediaConfig.autoPlay; autoPlayBtn.textContent = mediaConfig.autoPlay ? '自动播放开' : '自动播放关'; }); // ===================== 4. 工具函数 ===================== function parseIframeVideoSrc(iframeSrc) { let realVideo = ''; if (iframeSrc.includes('bilibili.com/player')) { const bvidMatch = iframeSrc.match(/bvid=([A-Za-z0-9]+)/); const aidMatch = iframeSrc.match(/aid=(\d+)/); if (bvidMatch) realVideo = `https://player.bilibili.com/player.html?bvid=${bvidMatch[1]}`; else if (aidMatch) realVideo = `https://player.bilibili.com/player.html?aid=${aidMatch[1]}`; } if (iframeSrc.includes('youku.com')) { const vidMatch = iframeSrc.match(/vid_([A-Za-z0-9]+)/); if (vidMatch) realVideo = `https://player.youku.com/embed/${vidMatch[1]}`; } return realVideo; } function createVideoThumb(src, callback) { if (videoThumbCache.has(src)) { callback(videoThumbCache.get(src)); return; } const canvas = document.createElement('canvas'); canvas.width = 120; canvas.height = 60; const ctx = canvas.getContext('2d'); const tempVid = document.createElement('video'); tempVid.src = src; tempVid.muted = true; tempVid.currentTime = 0.1; tempVid.addEventListener('loadeddata', () => { if (tempVid.duration < mediaConfig.filterMinDuration) { callback(null); return; } ctx.drawImage(tempVid, 0, 0, canvas.width, canvas.height); const dataUrl = canvas.toDataURL(); videoThumbCache.set(src, dataUrl); callback(dataUrl); }); tempVid.load(); } function bindLongPressDownload(el, mediaSrc, mediaType) { let pressTimer = null; const longPressTime = 600; el.addEventListener('mousedown', () => pressTimer = setTimeout(() => { const a = document.createElement('a'); a.href = mediaSrc; a.download = mediaType === 'image' ? '预览图片' : '预览视频'; document.body.appendChild(a); a.click(); a.remove(); }, longPressTime)); el.addEventListener('mouseup', () => clearTimeout(pressTimer)); el.addEventListener('mouseleave', () => clearTimeout(pressTimer)); } // ===================== 5. 增量渲染媒体DOM(性能核心优化) ===================== function createMediaItem(mediaObj) { const { type, src } = mediaObj; if (mediaDomMap.has(src)) return mediaDomMap.get(src); const li = document.createElement('li'); li.className = 'gallery-item'; li.dataset.mediaType = type; li.dataset.src = src; if (type === 'image') { const thumb = document.createElement('img'); thumb.className = 'media-thumb'; thumb.src = src; thumb.onerror = () => thumb.style.background = '#eee'; thumb.addEventListener('click', e => { e.stopPropagation(); currentMediaIndex = allMediaList.findIndex(m => m.src === src); openFullImage(src); }); bindLongPressDownload(thumb, src, 'image'); li.appendChild(thumb); } else if (type === 'video') { const thumbImg = document.createElement('img'); thumbImg.className = 'media-thumb'; const videoTag = document.createElement('span'); videoTag.className = 'ios-video-tag'; videoTag.textContent = '视频'; createVideoThumb(src, thumbData => { if (!thumbData) li.remove(); else thumbImg.src = thumbData; }); thumbImg.addEventListener('click', e => { e.stopPropagation(); currentMediaIndex = allMediaList.findIndex(m => m.src === src); openFullVideo(src); }); bindLongPressDownload(thumbImg, src, 'video'); li.append(thumbImg, videoTag); } else if (type === 'iframe-video') { const thumbImg = document.createElement('img'); thumbImg.className = 'media-thumb'; thumbImg.src = 'data:image/svg+xml;utf8,内嵌视频'; const iframeTag = document.createElement('span'); iframeTag.className = 'ios-iframe-tag'; iframeTag.textContent = 'B站/优酷'; thumbImg.addEventListener('click', e => { e.stopPropagation(); currentMediaIndex = allMediaList.findIndex(m => m.src === src); openFullIframe(src); }); li.append(thumbImg, iframeTag); } mediaDomMap.set(src, li); return li; } function renderFilterMedia() { phoneGalleryEl.innerHTML = ''; allMediaList.forEach(media => { if (currentTab === 'all' || (currentTab === 'image' && media.type === 'image') || (currentTab === 'video' && (media.type === 'video' || media.type === 'iframe-video'))) { const dom = createMediaItem(media); phoneGalleryEl.appendChild(dom); } }); } // ===================== 6. 全屏预览 ===================== function openFullImage(src) { fullPreviewWrap.innerHTML = ''; fullPreviewWrap.style.background = '#fff'; const fullImg = document.createElement('img'); fullImg.src = src; fullImg.className = 'full-preview-img'; fullImg.addEventListener('click', closeFullPreview); fullPreviewWrap.appendChild(fullImg); fullPreviewWrap.style.display = 'flex'; } function openFullVideo(src) { fullPreviewWrap.innerHTML = ''; fullPreviewWrap.style.background = '#000'; const fullVideo = document.createElement('video'); fullVideo.src = src; fullVideo.className = 'full-preview-video'; fullVideo.controls = true; fullVideo.muted = mediaConfig.globalMute; fullVideo.autoplay = mediaConfig.autoPlay; fullPreviewWrap.appendChild(fullVideo); fullPreviewWrap.style.display = 'flex'; } function openFullIframe(src) { fullPreviewWrap.innerHTML = ''; fullPreviewWrap.style.background = '#000'; const iframe = document.createElement('iframe'); iframe.src = src; iframe.className = 'full-preview-iframe'; iframe.allow = 'autoplay;fullscreen;encrypted-media'; iframe.frameBorder = '0'; fullPreviewWrap.appendChild(iframe); fullPreviewWrap.style.display = 'flex'; } function closeFullPreview() { const videoEl = fullPreviewWrap.querySelector('video'); if (videoEl) { videoEl.pause(); videoEl.src = ''; } fullPreviewWrap.style.display = 'none'; fullPreviewWrap.innerHTML = ''; } // ===================== 7. 页面媒体采集(增量追加,防抖) ===================== function collectAllMedia() { let hasNew = false; // 图片采集 Array.from(document.querySelectorAll('img')).forEach(imgDom => { let src = imgDom.src || imgDom.dataset.src || imgDom.dataset.original; if (!src) return; const blankGif = src.startsWith('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); if (blankGif || loadedSet.has(src)) return; loadedSet.add(src); allMediaList.push({ type: 'image', src }); hasNew = true; }); // 原生视频 Array.from(document.querySelectorAll('video')).forEach(videoDom => { let src = videoDom.src; if (!src && videoDom.querySelector('source')) src = videoDom.querySelector('source').src; if (!src || loadedSet.has(src)) return; loadedSet.add(src); allMediaList.push({ type: 'video', src }); hasNew = true; }); // iframe视频 Array.from(document.querySelectorAll('iframe')).forEach(iframe => { const realSrc = parseIframeVideoSrc(iframe.src); if (!realSrc || loadedSet.has(realSrc)) return; loadedSet.add(realSrc); allMediaList.push({ type: 'iframe-video', src: realSrc }); hasNew = true; }); if (hasNew) renderFilterMedia(); } let observerTimer = null; const observer = new MutationObserver(() => { clearTimeout(observerTimer); observerTimer = setTimeout(() => collectAllMedia(), 300); }); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['src', 'data-src', 'data-original'] }); collectAllMedia(); // ===================== 8. iOS磨砂半透明完整样式 ===================== const style = document.createElement('style'); style.textContent = ` /* iOS 磨砂玻璃悬浮整机卡片 核心 */ .ios-blur-card { position: fixed; top: 20px; right: 20px; z-index: 99999; width: 280px; height: 560px; /* 毛玻璃半透核心属性 */ background: rgba(255, 255, 255, 0.72); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); border: 1px solid rgba(255, 255, 255, 0.85); border-radius: 36px; padding: 16px 12px; /* iOS柔和多层阴影 */ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.09), 0 8px 24px rgba(0, 0, 0, 0.06), inset 0 0 0 0.5px rgba(255,255,255,0.9); box-sizing: border-box; display: flex; flex-direction: column; align-items: center; gap: 10px; } /* 顶部灵动岛刘海 极简 */ .ios-notch { width: 62px; height: 13px; background: #111; border-radius: 0 0 11px 11px; } /* ========== iOS原生分段控件(解决之前按钮别扭问题) ========== */ .ios-segment-control { width: 96%; display: flex; background: rgba(0,0,0,0.06); border-radius: 999px; padding: 3px; gap: 2px; } .seg-btn { flex: 1; border: none; padding: 5px 0; font-size: 10px; font-weight: 500; border-radius: 999px; background: transparent; color: #666; cursor: pointer; transition: all 0.2s cubic-bezier(0.2,0,0.2,1); } .seg-btn.active { background: #ffffff; color: #007aff; box-shadow: 0 1px 4px rgba(0,0,0,0.08); } /* 控制按钮行 iOS小型标签 */ .ios-control-row { width: 100%; display: flex; gap: 8px; justify-content: center; } .ios-small-btn { font-size: 9px; padding: 3px 8px; border-radius: 999px; border: none; background: rgba(0,122,255,0.09); color: #007aff; cursor: pointer; transition: 0.2s; } .ios-small-btn:hover { background: rgba(0,122,255,0.16); } /* 屏幕容器 */ .ios-screen-wrap { flex: 1; width: 100%; position: relative; border-radius: 20px; overflow: hidden; background: rgba(255,255,255,0.4); border: 1px solid rgba(0,0,0,0.05); } /* 媒体缩略图库 */ .media-gallery { width: 100%; height: 100%; margin: 0; padding: 9px; list-style: none; overflow-y: auto; overflow-x: hidden; display: flex; flex-wrap: wrap; gap: 5px; box-sizing: border-box; } .media-gallery::-webkit-scrollbar { width: 4px; } .media-gallery::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.12); border-radius: 2px; } /* iOS圆形Home键 半透磨砂 */ .ios-home-button { width: 32px; height: 32px; border: 1.5px solid rgba(0,0,0,0.22); border-radius: 50%; margin-top: 2px; cursor: pointer; transition: 0.2s; background: rgba(255,255,255,0.35); backdrop-filter: blur(4px); } .ios-home-button:hover { border-color: #007aff; background: rgba(0,122,255,0.08); } /* 缩略格子多列 */ .gallery-item { flex: 1 0 42px; margin: 0; position: relative; } .media-thumb { width: 100%; height: 42px; object-fit: cover; background: #fff; border-radius: 7px; border: 1px solid rgba(0,0,0,0.06); cursor: pointer; transition: transform 0.2s ease; display: block; box-shadow: 0 1px 2px rgba(0,0,0,0.04); } .media-thumb:hover { transform: scale(1.04); box-shadow: 0 3px 9px rgba(0,0,0,0.09); } /* iOS系统风格媒体角标 半透磨砂 */ .ios-video-tag, .ios-iframe-tag { position: absolute; bottom: 2px; right: 2px; color: #fff; font-size: 6px; padding: 1px 3px; border-radius: 3px; backdrop-filter: blur(6px); font-weight: 500; } .ios-video-tag { background: rgba(0,122,255,0.75); } .ios-iframe-tag { background: rgba(52,199,89,0.75); } /* 全屏预览层 */ .full-preview-layer { position: absolute; inset: 0; background: rgba(255,255,255,0.9); display: flex; align-items: center; justify-content: center; padding: 6px; box-sizing: border-box; touch-action: pan-y; } .full-preview-img { width: 100%; height: 100%; object-fit: contain; cursor: grab; border-radius: 10px; } .full-preview-img:active { cursor: grabbing; } .full-preview-video, .full-preview-iframe { width: 100%; height: 100%; object-fit: contain; border: none; border-radius: 10px; } /* 移动端适配 */ @media (max-width: 768px) { .ios-blur-card { width: 144px; height: 324px; right: 10px; bottom: 10px; top: auto; padding: 12px 8px; } .gallery-item { flex: 1 0 30px; } .media-thumb { height: 30px; border-radius: 5px; } .ios-home-button { width: 24px; height: 24px; } .ios-notch { width: 40px; height: 9px; } .seg-btn, .ios-small-btn { font-size: 7px; } .ios-video-tag, .ios-iframe-tag { font-size: 5px; } } `; document.head.appendChild(style); })();