// ==UserScript==
// @name 加强糯米!
// @namespace https://www.nuomill.com
// @version 1.10
// @description 糯米洛洛网站增强
// @author Sunse666
// @match https://www.nuomill.com/*
// @grant none
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
(function () {
var match = location.pathname.match(/^\/user\/(\d+)/);
if (match) {
var now = Date.now();
var lastRedirect = 0;
try { lastRedirect = parseInt(sessionStorage.getItem('nml_ur_ts')) || 0; } catch (e) {}
if (now - lastRedirect < 10000) {
var retryDelay = 10000 - (now - lastRedirect) + 500;
var retryUserId = match[1];
var retryAttempts = 0;
try { retryAttempts = parseInt(sessionStorage.getItem('nml_ur_retries')) || 0; } catch (e) {}
if (retryAttempts >= 3) {
try { sessionStorage.removeItem('nml_ur_ts'); } catch (e) {}
try { sessionStorage.removeItem('nml_ur_retries'); } catch (e) {}
return;
}
setTimeout(function() {
if (document.querySelector('.content-tabs') || document.querySelector('.user-header')) return;
try { sessionStorage.removeItem('nml_ur_ts'); } catch (e) {}
try { sessionStorage.setItem('nml_user_redirect', retryUserId); } catch (e) {}
try { sessionStorage.setItem('nml_ur_retries', String(retryAttempts + 1)); } catch (e) {}
location.replace('/');
}, retryDelay);
return;
}
try { sessionStorage.removeItem('nml_ur_retries'); } catch (e) {}
try { sessionStorage.setItem('nml_user_redirect', match[1]); } catch (e) {}
try { sessionStorage.setItem('nml_ur_ts', String(now)); } catch (e) {}
location.replace('/');
return;
}
if (location.pathname === '/' || location.pathname === '') {
var userId;
try { userId = sessionStorage.getItem('nml_user_redirect'); } catch (e) {}
if (userId) {
try { sessionStorage.removeItem('nml_user_redirect'); } catch (e) {}
var tries = 0;
(function tryNav() {
var router;
try {
var app = document.getElementById('__nuxt').__vue_app__;
router = app.config.globalProperties.$router;
} catch (e) {}
if (router) {
router.push('/user/' + userId);
} else if (tries++ < 80) {
setTimeout(tryNav, 200);
} else {
try { sessionStorage.removeItem('nml_ur_ts'); } catch (e) {}
try { sessionStorage.setItem('nml_user_redirect', userId); } catch (e) {}
location.replace('/user/' + userId);
}
})();
}
}
})();
const SPEEDS = [0.25, 0.5, 1.0, 1.25, 1.5, 2.0];
const DBLCLICK_THRESHOLD = 320;
const VOLUME_KEY = 'nml_saved_volume';
const SPEED_KEY = 'nml_saved_speed';
let savedSpeed = parseFloat(localStorage.getItem(SPEED_KEY));
if (isNaN(savedSpeed) || !SPEEDS.includes(savedSpeed)) savedSpeed = 1.0;
let currentSpeed = savedSpeed;
let savedVolume = parseFloat(localStorage.getItem(VOLUME_KEY));
if (isNaN(savedVolume) || savedVolume < 0 || savedVolume > 1) savedVolume = null;
let videoEl = null;
let speedBtn = null;
let speedMenu = null;
let toastEl = null;
let mounted = false;
let clickTimer = null;
let lastClickTime = 0;
function setupDoubleClick(video) {
if (!video || video.dataset.nmlDblclick) return;
video.dataset.nmlDblclick = '1';
video.addEventListener('click', e => {
const now = Date.now();
const elapsed = now - lastClickTime;
lastClickTime = now;
e.stopPropagation();
e.stopImmediatePropagation();
if (elapsed < DBLCLICK_THRESHOLD && elapsed > 0) {
clearTimeout(clickTimer);
lastClickTime = 0;
toggleFullscreen();
return;
}
clearTimeout(clickTimer);
clickTimer = setTimeout(() => {
lastClickTime = 0;
const v = findVideo();
if (v) {
v.paused ? v.play() : v.pause();
}
}, DBLCLICK_THRESHOLD);
}, true);
}
function toggleFullscreen() {
const wrapper = document.querySelector('.player-wrapper');
if (!wrapper) return;
if (document.fullscreenElement || document.webkitFullscreenElement) {
(document.exitFullscreen || document.webkitExitFullscreen).call(document);
} else {
(wrapper.requestFullscreen || wrapper.webkitRequestFullscreen).call(wrapper);
}
showToast(currentSpeed);
}
// Volume memory
function saveVolume(vol) {
savedVolume = vol;
try { localStorage.setItem(VOLUME_KEY, String(vol)); } catch (e) {}
}
function applySavedVolume(video) {
if (savedVolume == null) return;
video.volume = savedVolume;
video.muted = savedVolume === 0;
// Also sync the slider
const slider = document.querySelector('.custom-video-player .volume-slider');
if (slider) {
slider.value = savedVolume;
slider.dispatchEvent(new Event('input', { bubbles: true }));
}
}
// Video tracking
function setupMobileLongPress(video) {
if (!video || video.dataset.nmlLongPress) return;
video.dataset.nmlLongPress = '1';
video.addEventListener('click', function(e) {
if (video._nmlSuppressClick && Date.now() - video._nmlSuppressClick < 450) {
e.stopImmediatePropagation();
e.preventDefault();
}
}, true);
var longPressTimer = null;
var savedRate = null;
var LONG_PRESS_MS = 500;
var MOVE_THRESHOLD = 30;
var startX = 0, startY = 0;
function clear() {
if (longPressTimer) { clearTimeout(longPressTimer); longPressTimer = null; }
if (savedRate !== null) {
video.playbackRate = savedRate;
currentSpeed = savedRate;
savedRate = null;
showToast(currentSpeed);
updateUI();
}
video._nmlHolding = false;
}
function onStart(x, y) {
clear();
video._nmlHolding = true;
startX = x;
startY = y;
longPressTimer = setTimeout(function() {
savedRate = video.playbackRate;
video.playbackRate = 2.0;
currentSpeed = 2.0;
showToast(2.0, true);
updateUI();
longPressTimer = null;
}, LONG_PRESS_MS);
}
function onMove(x, y) {
if (!longPressTimer || savedRate !== null) return;
var dx = x - startX;
var dy = y - startY;
if (Math.abs(dx) > MOVE_THRESHOLD || Math.abs(dy) > MOVE_THRESHOLD) {
clear();
}
}
function onEnd() {
var wasLongPress = savedRate !== null;
if (wasLongPress) {
video._nmlSuppressClick = Date.now();
var rate = savedRate;
savedRate = null;
video.playbackRate = rate;
currentSpeed = rate;
showToast(rate);
updateUI();
}
if (longPressTimer) { clearTimeout(longPressTimer); longPressTimer = null; }
video._nmlHolding = false;
}
video.addEventListener('contextmenu', function(e) {
if (video._nmlHolding) {
e.preventDefault();
e.stopPropagation();
}
});
video.addEventListener('touchstart', function(e) {
if (e.touches.length !== 1) { clear(); return; }
onStart(e.touches[0].clientX, e.touches[0].clientY);
}, { passive: true });
video.addEventListener('touchmove', function(e) {
onMove(e.touches[0].clientX, e.touches[0].clientY);
}, { passive: true });
video.addEventListener('touchend', onEnd);
video.addEventListener('touchcancel', clear);
video.addEventListener('mousedown', function(e) {
if (e.button !== 0) return;
onStart(e.clientX, e.clientY);
});
document.addEventListener('mousemove', function(e) {
onMove(e.clientX, e.clientY);
});
document.addEventListener('mouseup', function(e) {
if (savedRate !== null || longPressTimer) {
onEnd();
}
});
}
function findVideo() {
const player = document.querySelector('.custom-video-player');
if (!player) return null;
const v = player.querySelector('video');
if (v && v !== videoEl) {
videoEl = v;
v.playbackRate = currentSpeed;
applySavedVolume(v);
setupMobileLongPress(v);
setupDoubleClick(v);
watchVideo(v);
}
return v;
}
function watchVideo(video) {
video.addEventListener('loadstart', () => {
applySavedVolume(video);
setTimeout(() => {
applySavedVolume(video);
if (video.playbackRate !== currentSpeed) {
video.playbackRate = currentSpeed;
}
}, 60);
});
video.addEventListener('loadedmetadata', () => {
applySavedVolume(video);
if (video.playbackRate !== currentSpeed) {
video.playbackRate = currentSpeed;
}
});
video.addEventListener('volumechange', () => {
if (!video.muted && video.volume > 0) {
saveVolume(video.volume);
}
});
}
const videoObserver = new MutationObserver(() => {
findVideo();
});
function startVideoObserver() {
const player = document.querySelector('.custom-video-player');
if (player) {
videoObserver.observe(player, { childList: true, subtree: true });
}
findVideo();
}
function applySpeed(speed) {
currentSpeed = speed;
const v = findVideo() || document.querySelector('video');
if (v) {
v.playbackRate = speed;
requestAnimationFrame(() => {
if (v.playbackRate !== speed) v.playbackRate = speed;
});
}
syncDanmaku(speed);
updateUI();
showToast(speed);
savedSpeed = speed;
try { localStorage.setItem(SPEED_KEY, String(speed)); } catch (e) {}
}
function syncDanmaku(speed) {
try {
const app = document.getElementById('__nuxt');
if (!app?.__vue_app__) return;
walkVue(app.__vue_app__._instance?.vnode, comp => {
if (comp.props && 'speed' in comp.props) {
comp.props.speed = 100 * speed;
}
});
} catch (e) {}
}
function walkVue(vnode, fn) {
if (!vnode) return;
if (vnode.component) {
fn(vnode.component);
if (vnode.component.subTree) walkVue(vnode.component.subTree, fn);
}
if (Array.isArray(vnode.children)) vnode.children.forEach(c => walkVue(c, fn));
if (vnode.dynamicChildren) vnode.dynamicChildren.forEach(c => walkVue(c, fn));
}
let toastTimer = null;
function showToast(speed, persistent) {
if (!toastEl) {
toastEl = document.createElement('div');
toastEl.style.cssText =
'position:absolute;top:12px;left:50%;transform:translateX(-50%);' +
'z-index:10000;background:rgba(0,0,0,0.85);color:#fff;' +
'font-size:15px;font-weight:600;padding:6px 16px;border-radius:6px;' +
'pointer-events:none;opacity:0;transition:opacity 0.2s;' +
'font-family:-apple-system,BlinkMacSystemFont,sans-serif;' +
'white-space:nowrap;letter-spacing:0.5px;';
}
if (!toastEl.parentNode) {
const vc = document.querySelector('.custom-video-player');
if (vc) vc.appendChild(toastEl);
}
if (!toastEl.parentNode) return;
clearTimeout(toastTimer);
if (persistent) {
toastEl.textContent = speed + '×';
toastEl.style.opacity = '1';
} else {
toastEl.textContent = speed === 1.0 ? '1×' : speed + '×';
toastEl.style.opacity = '1';
toastTimer = setTimeout(function () { toastEl.style.opacity = '0'; }, 700);
}
}
// Update UI
function updateUI() {
if (speedBtn) {
speedBtn.textContent = currentSpeed === 1.0 ? '倍速' : currentSpeed + '×';
}
if (speedMenu) {
const resOpt = document.querySelector('.resolution-option');
const defaultColor = resOpt ? getComputedStyle(resOpt).color : '#ccc';
speedMenu.querySelectorAll('.nml-speed-opt').forEach(opt => {
const s = parseFloat(opt.textContent);
opt.style.color = s === currentSpeed ? '#ff6fae' : defaultColor;
opt.style.fontWeight = s === currentSpeed ? '600' : '';
});
}
}
// Build UI
function createSpeedSelector() {
const controlBtn = document.querySelector('.custom-video-player .control-btn');
if (!controlBtn) return null;
const wrapper = document.createElement('div');
wrapper.className = 'nml-speed-wrapper';
wrapper.setAttribute('data-v-65d53e0f', '');
wrapper.style.cssText = 'position:relative;display:inline-flex;align-items:center;';
speedBtn = controlBtn.cloneNode(true);
while (speedBtn.firstChild) speedBtn.removeChild(speedBtn.firstChild);
speedBtn.textContent = '倍速';
speedBtn.removeAttribute('title');
speedBtn.removeAttribute('aria-label');
speedBtn.addEventListener('mousedown', handleSpeedBtnMouseDown);
speedBtn.addEventListener('click', e => {
e.stopPropagation();
e.preventDefault();
});
wrapper.appendChild(speedBtn);
// Menu
speedMenu = document.createElement('div');
speedMenu.setAttribute('data-v-65d53e0f', '');
speedMenu.style.cssText =
'display:none;position:absolute;bottom:100%;right:0;margin-bottom:6px;' +
'background:rgba(28,28,28,0.96);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);' +
'border:1px solid rgba(255,255,255,0.12);border-radius:8px;' +
'padding:4px;min-width:82px;z-index:10001;' +
'box-shadow:0 4px 24px rgba(0,0,0,0.5);';
const resMenu = document.querySelector('.resolution-menu');
const resOpt = resMenu?.querySelector('.resolution-option');
SPEEDS.forEach(s => {
const opt = document.createElement('div');
opt.className = 'nml-speed-opt';
opt.setAttribute('data-v-65d53e0f', '');
if (resOpt) {
const cs = getComputedStyle(resOpt);
opt.style.cssText = cs.cssText;
} else {
opt.style.cssText =
'padding:8px 14px;border-radius:6px;font-size:13px;' +
'text-align:center;cursor:pointer;white-space:nowrap;' +
'transition:background 0.15s,color 0.15s;color:#ccc;';
}
if (s === currentSpeed) {
opt.style.color = '#ff6fae';
opt.style.fontWeight = '600';
}
opt.textContent = s + '×';
opt.addEventListener('mousedown', e => {
e.stopPropagation();
e.preventDefault();
applySpeed(s);
speedMenu.style.display = 'none';
});
opt.addEventListener('click', e => {
e.stopPropagation();
e.preventDefault();
});
opt.addEventListener('mouseenter', function () {
this.style.background = 'rgba(255,255,255,0.1)';
this.style.color = '#fff';
});
opt.addEventListener('mouseleave', function () {
this.style.background = '';
const defColor = resOpt ? getComputedStyle(resOpt).color : '#ccc';
this.style.color = parseFloat(this.textContent) === currentSpeed
? '#ff6fae' : defColor;
});
speedMenu.appendChild(opt);
});
wrapper.appendChild(speedMenu);
// Combined outside-click-to-close for both menus
document.addEventListener('mousedown', closeMenusOnOutside, true);
document.addEventListener('touchstart', closeMenusOnOutside, true);
return wrapper;
}
function closeMenusOnOutside(e) {
// Never interfere with search-bar interactions
if (e.target.closest('#nav-searchform, .center-search__bar, .search-panel')) return;
// Close speed menu
if (speedMenu && speedMenu.style.display !== 'none') {
const sw = speedMenu.parentNode;
if (sw && !sw.contains(e.target)) {
speedMenu.style.display = 'none';
}
}
// Close resolution menu — only when it's actually visible
const resMenu = document.querySelector('.resolution-menu');
if (resMenu && resMenu.offsetParent !== null) {
const resBtn = document.querySelector('.resolution-selector .control-btn');
if (resBtn && !resBtn.contains(e.target) && !resMenu.contains(e.target)) {
resBtn.click();
}
}
}
function handleSpeedBtnMouseDown(e) {
e.stopPropagation();
e.preventDefault();
if (!speedMenu) return;
// Close resolution menu before opening speed menu
const resBtn = document.querySelector('.resolution-selector .control-btn');
const resMenu = document.querySelector('.resolution-menu');
if (resMenu && resBtn) {
resBtn.click();
}
const isHidden = speedMenu.style.display === 'none';
speedMenu.style.display = isHidden ? '' : 'none';
}
// Mount
function mountUI() {
if (mounted) return true;
// Defensive: remove any stale duplicates before creating a new one
var stale = document.querySelectorAll('.nml-speed-wrapper');
for (var si = 0; si < stale.length; si++) stale[si].remove();
speedBtn = null;
speedMenu = null;
const resSelector = document.querySelector('.resolution-selector');
const danmakuToggle = document.querySelector('.danmaku-toggle-btn');
const volControl = document.querySelector('.volume-control');
let insertAfter = resSelector;
if (!insertAfter) insertAfter = danmakuToggle;
if (!insertAfter && volControl) {
insertAfter = volControl.previousElementSibling;
}
if (!insertAfter || !insertAfter.parentNode) return false;
const speedSelector = createSpeedSelector();
if (!speedSelector) return false;
insertAfter.parentNode.insertBefore(speedSelector, insertAfter.nextSibling);
updateUI();
startVideoObserver();
mounted = true;
return true;
}
// Keyboard shortcuts
document.addEventListener('keydown', e => {
const tag = document.activeElement?.tagName;
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;
if ((e.key === 'q' || e.key === 'Q') && !e.repeat) {
e.preventDefault();
_qLongPress = false;
_qTimer = setTimeout(function () {
_qLongPress = true;
_qTimer = null;
doTripleAction();
}, 3000);
setTimeout(function () {
if (_qTimer) nmlCenterToast('继续长按以三连...', 1000);
}, 1800);
return;
}
if (!findVideo() && !document.querySelector('.custom-video-player video')) return;
if (e.key === 'j') { window.scrollBy({ top: 120, behavior: 'smooth' }); return; }
if (e.key === 'k') { window.scrollBy({ top: -120, behavior: 'smooth' }); return; }
if (e.key === 'h') { window.scrollBy({ left: -120, behavior: 'smooth' }); return; }
if (e.key === 'l') { window.scrollBy({ left: 120, behavior: 'smooth' }); return; }
if (e.key === '>' || (e.key === '.' && e.shiftKey)) {
e.preventDefault();
const idx = SPEEDS.indexOf(currentSpeed);
if (idx < SPEEDS.length - 1) applySpeed(SPEEDS[idx + 1]);
return;
}
if (e.key === '<' || (e.key === ',' && e.shiftKey)) {
e.preventDefault();
const idx = SPEEDS.indexOf(currentSpeed);
if (idx > 0) applySpeed(SPEEDS[idx - 1]);
return;
}
if (e.key === 'ArrowLeft') {
e.preventDefault();
seekRelative(-10);
return;
}
if (e.key === 'ArrowRight') {
e.preventDefault();
seekRelative(10);
return;
}
if (e.key === 'ArrowUp') {
e.preventDefault();
adjustVolume(0.05);
return;
}
if (e.key === 'ArrowDown') {
e.preventDefault();
adjustVolume(-0.05);
return;
}
if (e.key === ' ' || e.code === 'Space') {
e.preventDefault();
const v = findVideo() || document.querySelector('video');
if (v) { v.paused ? v.play() : v.pause(); }
return;
}
if (e.key === 'f' || e.key === 'F') {
e.preventDefault();
toggleFullscreen();
return;
}
});
function seekRelative(delta) {
const v = findVideo() || document.querySelector('video');
if (!v) return;
const newTime = Math.max(0, Math.min(v.currentTime + delta, v.duration || Infinity));
v.currentTime = newTime;
}
function adjustVolume(delta) {
const v = findVideo() || document.querySelector('video');
if (!v) return;
const newVol = Math.max(0, Math.min(1, Math.round((v.volume + delta) * 100) / 100));
v.volume = newVol;
v.muted = newVol === 0;
const slider = document.querySelector('.custom-video-player .volume-slider');
if (slider) {
slider.value = v.muted ? 0 : newVol;
slider.dispatchEvent(new Event('input', { bubbles: true }));
}
saveVolume(newVol);
showToastVolume(newVol);
}
function showToastVolume(vol) {
if (!toastEl) {
toastEl = document.createElement('div');
toastEl.style.cssText =
'position:absolute;top:12px;left:50%;transform:translateX(-50%);' +
'z-index:10000;background:rgba(0,0,0,0.85);color:#fff;' +
'font-size:15px;font-weight:600;padding:6px 16px;border-radius:6px;' +
'pointer-events:none;opacity:0;transition:opacity 0.2s;' +
'font-family:-apple-system,BlinkMacSystemFont,sans-serif;' +
'white-space:nowrap;letter-spacing:0.5px;';
}
if (!toastEl.parentNode) {
const vc = document.querySelector('.custom-video-player');
if (vc) vc.appendChild(toastEl);
}
if (!toastEl.parentNode) return;
var pct = Math.round(vol * 100);
toastEl.textContent = 'Vol ' + pct;
toastEl.style.opacity = '1';
clearTimeout(toastTimer);
toastTimer = setTimeout(function () { toastEl.style.opacity = '0'; }, 700);
}
var _qTimer = null;
var _qLongPress = false;
var _likePressTimer = null;
var _tripleToastEl = null;
var _tripleDone = false;
function nmlCenterToast(msg, duration) {
if (!_tripleToastEl) {
_tripleToastEl = document.createElement('div');
_tripleToastEl.style.cssText =
'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);' +
'z-index:99999;background:rgba(0,0,0,0.88);color:#fff;' +
'font-size:16px;font-weight:600;padding:12px 28px;border-radius:10px;' +
'pointer-events:none;opacity:0;transition:opacity 0.25s;' +
'font-family:-apple-system,BlinkMacSystemFont,sans-serif;' +
'text-align:center;white-space:nowrap;letter-spacing:1px;' +
'border:1px solid rgba(255,111,174,0.35);';
var pw = document.querySelector('.player-wrapper');
(pw || document.body).appendChild(_tripleToastEl);
}
if (_tripleToastEl.parentNode === document.body && document.fullscreenElement) {
var pw2 = document.querySelector('.player-wrapper');
if (pw2 && pw2 !== document.body) pw2.appendChild(_tripleToastEl);
}
if (!msg) {
_tripleToastEl.style.opacity = '0';
clearTimeout(_tripleToastEl._t);
return;
}
_tripleToastEl.textContent = msg;
_tripleToastEl.style.opacity = '1';
clearTimeout(_tripleToastEl._t);
_tripleToastEl._t = setTimeout(function () { _tripleToastEl.style.opacity = '0'; }, duration || 2000);
}
function getActionBtn(idx) {
var btns = document.querySelectorAll('.action-bar .action-btn');
return btns[idx] || null;
}
var _nmlLikedVideos = {};
try {
_nmlLikedVideos = JSON.parse(localStorage.getItem('nml_liked_videos') || '{}');
} catch(e) {}
function nmlSaveLikedVideo(vid, info) {
var existing = _nmlLikedVideos[vid];
_nmlLikedVideos[vid] = Object.assign(
{ id: vid, liked_at: existing ? existing.liked_at : Date.now() },
existing || {},
info
);
try { localStorage.setItem('nml_liked_videos', JSON.stringify(_nmlLikedVideos)); } catch(e) {}
}
function nmlGetLikedVideos() {
// Return as array sorted by most recent first
return Object.values(_nmlLikedVideos).sort(function(a, b) {
return (b.liked_at || 0) - (a.liked_at || 0);
});
}
var _nmlLikeCheckDone = {};
function nmlCheckAndRecordLike() {
var vid = getVideoId();
if (!vid || _nmlLikeCheckDone[vid]) return;
_nmlLikeCheckDone[vid] = true;
var token = getToken();
if (!token) return;
// Check if this video is already liked via API
fetch(getApiBase() + '/videos/' + vid + '/like/check', {
headers: { 'Authorization': 'Bearer ' + token }
})
.then(function(r) { return r.ok ? r.json() : null; })
.then(function(data) {
if (data && (data.liked || data.is_liked)) {
fetch(getApiBase() + '/videos/' + vid)
.then(function(r) { return r.ok ? r.json() : null; })
.then(function(vdata) {
var info = nmlExtractVideoInfo(vdata);
if (info) nmlSaveLikedVideo(vid, info);
}).catch(function() {});
}
}).catch(function() {});
}
function nmlExtractVideoInfo(vdata) {
if (!vdata || !vdata.video) return null;
var v = vdata.video;
return {
title: v.title,
cover_url: v.cover_url,
author: v.user ? v.user.nickname : '',
author_id: v.user_id,
views: v.views || 0,
likes: v.likes || 0,
duration: v.duration || '',
created_at: v.created_at || ''
};
}
function nmlRefreshAllLikedStats(container) {
var liked = nmlGetLikedVideos();
if (liked.length === 0) return;
var apiBase = getApiBase();
function nmlIsZeroDuration(d) {
return !d || d === '0:00' || d === '00:00' || d === '0';
}
function nmlFetchAndUpdate(vid, retries) {
retries = retries || 0;
fetch(apiBase + '/videos/' + vid)
.then(function(r) { return r.ok ? r.json() : null; })
.then(function(data) {
if (!data || !data.video) return;
var vd = data.video;
if (vd.id != vid) return;
var info = nmlExtractVideoInfo(data);
if (!info) return;
if (info.views == null && info.likes == null) return;
// Check for zero-valued fields that indicate stale / bad data
var durZero = nmlIsZeroDuration(info.duration);
var statsZero = (!info.views && !info.likes);
if ((durZero || statsZero) && retries < 1) {
// Retry once after a delay — the video may still be transcoding
setTimeout(function() {
nmlFetchAndUpdate(vid, retries + 1);
}, 2000);
return;
}
nmlSaveLikedVideo(vid, info);
var card = container.querySelector('.nml-lc-' + vid);
if (!card) return;
// Author + stats
var metaEl = card.querySelector('.nml-lc-meta');
if (metaEl) {
var author = info.author || '';
var viewsStr = nmlFormatCount(info.views || 0);
var likesStr = nmlFormatCount(info.likes || 0);
var parts = [];
if (author) parts.push('' + author.replace(/&/g,'&').replace(//g,'>') + '');
parts.push('' + viewsStr + ' 观看');
parts.push('' + likesStr + ' 点赞');
metaEl.innerHTML = parts.join('');
}
// Cover image
if (info.cover_url) {
var img = card.querySelector('img');
var newCover = info.cover_url;
if (newCover && !newCover.startsWith('http')) {
newCover = 'https://file.nuomill.com' + newCover;
}
if (img && newCover && img.src !== newCover) {
img.src = newCover;
}
}
// Duration badge
var durEl = card.querySelector('.nml-lc-dur');
if (durEl && info.duration && !nmlIsZeroDuration(info.duration)) {
durEl.textContent = info.duration;
durEl.style.display = '';
} else if (durEl && retries >= 1 && nmlIsZeroDuration(info.duration)) {
// Still zero after retry — hide it
durEl.style.display = 'none';
}
// Title (fix placeholder)
if (info.title) {
var titleEl = card.querySelector('.nml-lc-title');
if (titleEl) {
var curTitle = titleEl.textContent;
if (curTitle.indexOf('视频 ') === 0 && info.title !== curTitle) {
titleEl.textContent = info.title;
}
}
}
}).catch(function() {
// Network error — retry once
if (retries < 1) {
setTimeout(function() {
nmlFetchAndUpdate(vid, retries + 1);
}, 2000);
}
});
}
liked.forEach(function(v) {
nmlFetchAndUpdate(v.id, 0);
});
}
function nmlTrackLikeForVideo(vid) {
if (!vid) return;
fetch(getApiBase() + '/videos/' + vid)
.then(function(r) { return r.ok ? r.json() : null; })
.then(function(data) {
var info = nmlExtractVideoInfo(data);
if (info) nmlSaveLikedVideo(vid, info);
}).catch(function() {});
}
function clickLike() {
var btn = getActionBtn(0);
if (!btn) { nmlCenterToast('⚠ 未找到点赞按钮', 1800); return Promise.resolve(false); }
btn.dispatchEvent(new MouseEvent('click', { bubbles: true }));
// Track like locally (delay to let the like API complete)
var vid = getVideoId();
if (vid) setTimeout(function() { nmlTrackLikeForVideo(vid); }, 300);
return Promise.resolve(true);
}
var _nmlLikeClickInterceptInstalled = false;
function nmlInstallLikeClickInterceptor() {
if (_nmlLikeClickInterceptInstalled) return;
_nmlLikeClickInterceptInstalled = true;
document.addEventListener('click', function(e) {
var actionBtns = document.querySelectorAll('.action-bar .action-btn');
var likeBtn = actionBtns[0];
if (!likeBtn) return;
var el = e.target;
while (el && el !== document.body) {
if (el === likeBtn) {
var vid = getVideoId();
if (vid) setTimeout(function() { nmlTrackLikeForVideo(vid); }, 300);
return;
}
el = el.parentElement;
}
}, true);
}
function clickFavorite() {
var btn = getActionBtn(1);
if (!btn) return Promise.resolve(false);
if ((btn.textContent || '').indexOf('已收藏') !== -1) return Promise.resolve(true);
btn.dispatchEvent(new MouseEvent('click', { bubbles: true }));
return Promise.resolve(true);
}
function getApiBase() {
try {
var cfg = window.__NUXT__ && window.__NUXT__.config;
return (cfg && cfg.public && cfg.public.apiBase) || 'https://api.nuomill.com/api/v1';
} catch (e) { return 'https://api.nuomill.com/api/v1'; }
}
function getToken() {
var m = document.cookie.match(/(?:^|;\s*)token=([^;]*)/);
return m ? decodeURIComponent(m[1]) : '';
}
function getVideoId() {
var m = location.pathname.match(/\/video\/(\d+)/);
return m ? m[1] : null;
}
function tryFeed3Nuomi() {
var vid = getVideoId();
if (!vid) return Promise.resolve(false);
var token = getToken();
if (!token) return Promise.resolve(false);
var headers = { 'Content-Type': 'application/json' };
if (token) headers['Authorization'] = 'Bearer ' + token;
return fetch(getApiBase() + '/videos/' + vid + '/feed', {
method: 'POST',
headers: headers,
body: JSON.stringify({ amount: 3 })
}).then(function (r) {
if (!r.ok) return Promise.reject(r);
return r.status !== 204 ? r.json() : {};
}).then(function () {
// Update Vue state for instant UI feedback
var app = document.getElementById('__nuxt');
if (app && app.__vue_app__) {
walkVue(app.__vue_app__._instance?.vnode, function (comp) {
var st = comp.setupState;
if (st && 'te' in st && 'pe' in st && 'oe' in st && 'ee' in st && 're' in st) {
st.te.value += 3;
st.pe.value++;
st.oe.value = Math.max(0, st.oe.value - 3);
}
});
}
return true;
}).then(function (ok) {
if (ok) { var vid = getVideoId(); if (vid) nmlRecordNuomiFeed(vid, 3); }
return ok;
}).catch(function () { return false; });
}
var _nmlNuomiFed = {};
try {
_nmlNuomiFed = JSON.parse(localStorage.getItem('nml_nuomi_fed_cache') || '{}');
} catch(e) {}
function nmlRecordNuomiFeed(vid, amount) {
_nmlNuomiFed[vid] = (_nmlNuomiFed[vid] || 0) + amount;
try { localStorage.setItem('nml_nuomi_fed_cache', JSON.stringify(_nmlNuomiFed)); } catch(e) {}
}
function nmlGetNuomiFed(vid) {
return _nmlNuomiFed[vid] || 0;
}
var _nmlStatusCache = {};
function nmlFetchVideoStatus(vid, token) {
var key = vid + '|';
if (_nmlStatusCache[key]) return Promise.resolve(_nmlStatusCache[key]);
var headers = { 'Authorization': 'Bearer ' + token };
var apiBase = getApiBase();
return Promise.all([
fetch(apiBase + '/videos/' + vid + '/like/check', { headers: headers })
.then(function(r) { return r.ok ? r.json() : null; })
.catch(function() { return null; }),
fetch(apiBase + '/favorites/check/' + vid, { headers: headers })
.then(function(r) { return r.ok ? r.json() : null; })
.catch(function() { return null; })
]).then(function(results) {
var likeData = results[0];
var favData = results[1];
var status = {
liked: !!(likeData && (likeData.liked || likeData.is_liked)),
favorited: !!(favData && (favData.favorited || favData.is_favorited || favData.favorite)),
nuomiFed: nmlGetNuomiFed(vid)
};
_nmlStatusCache[key] = status;
return status;
});
}
function nmlInjectHistoryBadge(item, status) {
var coverEl = item.querySelector('.history-cover');
if (!coverEl) return;
var oldBadge = coverEl.querySelector('.nml-history-badges');
if (oldBadge) oldBadge.remove();
var badges = document.createElement('div');
badges.className = 'nml-history-badges';
badges.style.cssText = 'position:absolute;top:4px;right:4px;display:flex;gap:3px;z-index:10;pointer-events:none;';
if (status.liked) {
var likeBadge = document.createElement('span');
likeBadge.textContent = '❤';
likeBadge.title = '已点赞';
likeBadge.style.cssText = 'background:rgba(0,0,0,0.65);color:#ff6b9d;font-size:14px;padding:2px 6px;border-radius:4px;line-height:1;';
badges.appendChild(likeBadge);
}
if (status.favorited) {
var favBadge = document.createElement('span');
favBadge.textContent = '⭐';
favBadge.title = '已收藏';
favBadge.style.cssText = 'background:rgba(0,0,0,0.65);color:#f5c542;font-size:14px;padding:2px 6px;border-radius:4px;line-height:1;';
badges.appendChild(favBadge);
}
if (status.nuomiFed > 0) {
var nuomiBadge = document.createElement('span');
nuomiBadge.textContent = '🍚' + status.nuomiFed;
nuomiBadge.title = '已投喂 ' + status.nuomiFed + ' 糯米';
nuomiBadge.style.cssText = 'background:rgba(0,0,0,0.65);color:#fff;font-size:12px;padding:2px 6px;border-radius:4px;line-height:1;';
badges.appendChild(nuomiBadge);
}
if (badges.children.length > 0) {
coverEl.style.position = 'relative';
coverEl.appendChild(badges);
}
}
var _nmlHistoryEnhancing = false;
var _nmlHistoryRetries = 0;
function nmlEnhanceHistoryPage() {
if (_nmlHistoryEnhancing) return;
var token = getToken();
if (!token) return;
var domItems = document.querySelectorAll('.history-item');
if (domItems.length === 0) {
if (_nmlHistoryRetries++ < 30) {
setTimeout(nmlEnhanceHistoryPage, 600);
}
return;
}
_nmlHistoryEnhancing = true;
_nmlHistoryRetries = 0;
fetch(getApiBase() + '/history?page=1&page_size=100', {
headers: { 'Authorization': 'Bearer ' + token }
})
.then(function(r) {
if (!r.ok) throw new Error('History API failed: ' + r.status);
return r.json();
})
.then(function(data) {
var historyItems = data.history || [];
if (historyItems.length === 0) return;
var batch = [];
historyItems.forEach(function(h, idx) {
var vid = h.video && h.video.id;
if (!vid) return;
batch.push({ idx: idx, vid: vid });
});
// Fetch status for all videos in parallel
return Promise.all(batch.map(function(b) {
return nmlFetchVideoStatus(b.vid, token).then(function(status) {
return { idx: b.idx, status: status };
});
})).then(function(results) {
var domItems = document.querySelectorAll('.history-item');
results.forEach(function(r) {
if (domItems[r.idx]) {
nmlInjectHistoryBadge(domItems[r.idx], r.status);
}
});
});
})
.catch(function(err) {
console.warn('[NuomillEnhancer] History enhancement failed:', err);
})
.finally(function() {
_nmlHistoryEnhancing = false;
});
}
function nmlFixFollowPages() {
var isFollowers = location.pathname === '/followers';
var isFollowing = location.pathname === '/following';
if (!isFollowers && !isFollowing) return;
var token = getToken();
if (!token) return;
var title = isFollowers ? '我的粉丝' : '我的关注';
var attempts = 0;
(function tryInject() {
var mainEl = document.querySelector('#__nuxt .main') || document.querySelector('#__nuxt main');
if (!mainEl) {
if (attempts++ < 30) setTimeout(tryInject, 200);
return;
}
// Already injected?
if (mainEl.querySelector('.nml-follow-page')) return;
// Build page content
var html =
'
' +
'
' + title + '
' +
'
' +
'
加载中...
' +
'
';
mainEl.innerHTML = html;
var listEl = mainEl.querySelector('.nml-follow-list');
var loadingEl = mainEl.querySelector('.nml-follow-loading');
// Get user ID
function fetchWithUserId(userId) {
if (!userId) { loadingEl.textContent = '请先登录'; return; }
var endpoint = isFollowers
? '/follow/' + userId + '/followers?page=1&page_size=100'
: '/follow/' + userId + '/following?page=1&page_size=100';
fetch(getApiBase() + endpoint, { headers: { 'Authorization': 'Bearer ' + token } })
.then(function(r) { return r.ok ? r.json() : null; })
.then(function(data) {
loadingEl.style.display = 'none';
var list = data ? (data.followers || data.following || []) : [];
if (list.length === 0) {
listEl.innerHTML = '' +
(isFollowers ? '还没有粉丝' : '还没有关注任何人') + '
';
return;
}
var rows = '';
list.forEach(function(u) {
var avatar = u.avatar || '';
if (avatar && !avatar.startsWith('http')) avatar = 'https://file.nuomill.com' + avatar;
var nickname = (u.nickname || u.username || '').replace(/&/g,'&').replace(//g,'>');
var bio = (u.bio || '').replace(/&/g,'&').replace(//g,'>');
if (bio.length > 60) bio = bio.slice(0, 60) + '...';
var initial = nickname.charAt(0).toUpperCase();
rows +=
'' +
'
' +
(avatar ? '
' +
'' + initial + '' :
'' + initial + '') +
'' +
'
' +
'' + nickname + '' +
(bio ? '' + bio + '' : '') +
'' +
(isFollowing ? '
已关注' : '') +
'
';
});
listEl.innerHTML = rows;
listEl.querySelectorAll('.nml-follow-row').forEach(function(row) {
row.addEventListener('click', function(e) {
var uid = this.getAttribute('data-uid');
if (!uid) return;
try {
var app = document.getElementById('__nuxt');
if (app && app.__vue_app__) {
var router = app.__vue_app__.config.globalProperties.$router;
if (router) { router.push('/user/' + uid); return; }
}
} catch (ex) {}
window.location.href = '/user/' + uid;
});
});
// Bind unfollow
if (isFollowing) {
listEl.querySelectorAll('.nml-unfollow-btn').forEach(function(btn) {
btn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
var uid = parseInt(this.getAttribute('data-uid'));
var row = this.closest('.nml-follow-row');
fetch(getApiBase() + '/follow/' + uid, {
method: 'DELETE',
headers: { 'Authorization': 'Bearer ' + token }
}).then(function(r) {
if (r.ok && row) row.remove();
}).catch(function() {});
});
});
}
var c = list.length >= 100 ? '99+' : String(list.length);
mainEl.querySelector('h2').textContent = title + '(' + c + ')';
}).catch(function() {
loadingEl.textContent = '加载失败,请刷新重试';
});
}
// Try Pinia store first, then fall back to API
var userId = null;
try {
var app = document.getElementById('__nuxt');
if (app && app.__vue_app__) {
var pinia = app.__vue_app__.config.globalProperties.$pinia;
if (pinia) {
var userStore = pinia._s.get('user');
if (userStore && userStore.userInfo && userStore.userInfo.id) {
userId = userStore.userInfo.id;
}
}
}
} catch(e) {}
if (userId) {
fetchWithUserId(userId);
} else {
fetch(getApiBase() + '/users/profile', { headers: { 'Authorization': 'Bearer ' + token } })
.then(function(r) { return r.ok ? r.json() : null; })
.then(function(data) {
fetchWithUserId(data && data.user ? data.user.id : null);
}).catch(function() {
loadingEl.textContent = '加载失败,请刷新重试';
});
}
})();
}
function nmlEnhanceUserProfile() {
var match = location.pathname.match(/^\/user\/(\d+)/);
if (!match) return;
var profileUserId = parseInt(match[1]);
var currentUserId = null;
try {
var app = document.getElementById('__nuxt');
if (app && app.__vue_app__) {
var pinia = app.__vue_app__.config.globalProperties.$pinia;
if (pinia) {
var userStore = pinia._s.get('user');
if (userStore && userStore.userInfo) {
currentUserId = userStore.userInfo.id;
}
}
}
} catch(e) {}
var isOwnProfile = currentUserId === profileUserId;
if (isOwnProfile) {
var _statAttempts = 0;
function tryMakeStatsClickable() {
var statRow = document.querySelector('.user-stat-row');
if (!statRow) {
if (_statAttempts++ < 30) setTimeout(tryMakeStatsClickable, 500);
return;
}
if (statRow._nmlStatsClickable) return;
statRow._nmlStatsClickable = true;
function wrapWithLink(stat, path) {
var a = document.createElement('a');
a.href = path;
a.style.cssText = 'color:inherit;text-decoration:none;';
a.addEventListener('mouseenter', function() { this.style.color = 'var(--brand_pink)'; });
a.addEventListener('mouseleave', function() { this.style.color = ''; });
a.addEventListener('click', function(e) {
e.preventDefault();
try {
var app = document.getElementById('__nuxt');
if (app && app.__vue_app__) {
var router = app.__vue_app__.config.globalProperties.$router;
if (router) { router.push(path); return; }
}
} catch(ex) {}
window.location.href = path;
});
stat.parentNode.insertBefore(a, stat);
a.appendChild(stat);
}
var stats = statRow.querySelectorAll('.stat');
if (stats[0]) wrapWithLink(stats[0], '/followers');
if (stats[1]) wrapWithLink(stats[1], '/following');
}
setTimeout(tryMakeStatsClickable, 800);
}
var attempts = 0;
function tryInjectTab() {
var tabsContainer = document.querySelector('.content-tabs');
if (!tabsContainer) {
if (attempts++ < 30) setTimeout(tryInjectTab, 500);
return;
}
// Don't inject twice
if (tabsContainer.querySelector('.nml-likes-tab')) return;
var nativeBtn = tabsContainer.querySelector('.tab-btn');
if (!nativeBtn) return;
var tabBtn = nativeBtn.cloneNode(true);
tabBtn.className = 'tab-btn nml-likes-tab';
tabBtn.textContent = isOwnProfile ? '我的点赞' : 'TA的点赞';
tabBtn.classList.remove('active');
tabsContainer.appendChild(tabBtn);
var contentArea = tabsContainer.nextElementSibling;
var contentWrappers = tabsContainer.parentElement.querySelectorAll('[class*="tab-content"]');
var likedGrid = document.createElement('div');
likedGrid.className = 'nml-liked-content';
likedGrid.style.cssText = 'display:none;margin-top:16px;';
likedGrid.setAttribute('data-v-809cc4e2', '');
// Create video grid container
var gridEl = document.createElement('div');
gridEl.className = 'nml-liked-video-grid';
gridEl.style.cssText = 'display:grid;grid-template-columns:repeat(5,1fr);gap:16px;';
likedGrid.appendChild(gridEl);
tabsContainer.parentElement.appendChild(likedGrid);
var allTabs = tabsContainer.querySelectorAll('.tab-btn');
var _nmlIgnoreObserverUntil = 0;
tabBtn.addEventListener('click', function() {
_nmlIgnoreObserverUntil = Date.now() + 300;
allTabs.forEach(function(t) { t.classList.remove('active'); });
tabBtn.classList.add('active');
var parent = tabsContainer.parentElement;
for (var ci = 0; ci < parent.children.length; ci++) {
var child = parent.children[ci];
if (child !== tabsContainer && !child.classList.contains('nml-liked-content')) {
child.style.display = 'none';
}
}
likedGrid.style.display = 'block';
nmlRenderLikedVideos(gridEl, isOwnProfile);
});
var _tabObserver = new MutationObserver(function(mutations) {
if (Date.now() < _nmlIgnoreObserverUntil) return;
mutations.forEach(function(m) {
var t = m.target;
if (t.classList.contains('tab-btn') &&
t.classList.contains('active') &&
t !== tabBtn) {
tabBtn.classList.remove('active');
likedGrid.style.display = 'none';
var parent = tabsContainer.parentElement;
for (var ci = 0; ci < parent.children.length; ci++) {
var child = parent.children[ci];
if (child !== tabsContainer && !child.classList.contains('nml-liked-content')) {
child.style.display = '';
}
}
}
});
});
allTabs.forEach(function(t) {
_tabObserver.observe(t, { attributes: true, attributeFilter: ['class'] });
});
var liked = nmlGetLikedVideos();
if (liked.length > 0) {
nmlRenderLikedVideos(gridEl, isOwnProfile);
}
}
setTimeout(tryInjectTab, 800);
}
function nmlFormatCount(n) {
if (n == null) return '0';
if (n >= 10000) return (n / 10000).toFixed(1) + '万';
return String(n);
}
function nmlRenderLikedVideos(container, isOwnProfile) {
var liked = nmlGetLikedVideos();
if (!isOwnProfile) {
container.innerHTML = '无法查看此用户的点赞记录
仅可查看自己的点赞列表
';
return;
}
if (liked.length === 0) {
container.innerHTML = '还没有点赞过视频
去发现喜欢的视频吧 ♪
';
return;
}
var html = '';
liked.forEach(function(v) {
var cover = v.cover_url || '';
if (cover && !cover.startsWith('http')) {
cover = 'https://file.nuomill.com' + cover;
}
if (!cover) cover = 'data:image/svg+xml,';
var title = (v.title || '视频 ' + v.id).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
var author = (v.author || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
var hasStats = (v.views != null && v.likes != null);
var viewsStr = hasStats ? nmlFormatCount(v.views) : '...';
var likesStr = hasStats ? nmlFormatCount(v.likes) : '...';
var dur = v.duration || '';
var durHtml = dur ? '' + dur.replace(/&/g,'&').replace(/' : '';
html +=
'' +
'
' +
'
.replace(/)
' +
durHtml +
'
' +
'
' +
'
' + title + '
' +
'
' +
(author ? '' + author + '' : '') +
'' + viewsStr + ' 观看' +
'' + likesStr + ' 点赞' +
'
' +
'
';
});
container.innerHTML = html;
function updateGrid() {
var w = container.clientWidth;
var cols = w < 420 ? 2 : w < 768 ? 2 : w < 1100 ? 3 : w < 1400 ? 4 : 5;
container.style.gridTemplateColumns = 'repeat(' + cols + ',1fr)';
}
updateGrid();
window.addEventListener('resize', updateGrid);
// Kick off async refresh of all card stats from API
nmlRefreshAllLikedStats(container);
}
function doTripleAction() {
if (_tripleDone) return;
_tripleDone = true;
clickLike().then(function (liked) {
return clickFavorite().then(function (faved) {
return tryFeed3Nuomi().then(function (fed) {
var msg = liked ? '❤ 已点赞' : '';
if (faved) msg += ' ⭐ 已收藏';
if (fed) msg += ' 🍚 已投喂3糯米';
else if (liked) msg += ' + 收藏 + 投喂3糯米';
nmlCenterToast(msg, 2500);
setTimeout(function () { _tripleDone = false; }, 1000);
});
});
});
}
function cancelQLongPress() {
if (_qTimer) { clearTimeout(_qTimer); _qTimer = null; }
nmlCenterToast('', 0); // clear any half-way toast
}
document.addEventListener('keyup', function (e) {
if (e.key === 'q' || e.key === 'Q') {
if (_qTimer) {
clearTimeout(_qTimer);
_qTimer = null;
if (!_qLongPress) {
// Short press: just like
clickLike().then(function (ok) {
if (ok) nmlCenterToast('❤ 已点赞', 1500);
});
}
}
_qLongPress = false;
}
});
// Long-press on the like button itself (mouse + touch)
var _likeBtnObserver = null;
var _allLikePressTimers = [];
var _longPressFired = false;
function setupLikeBtnLongPress(btn) {
if (!btn || btn._nmlLongPressSetup) return;
btn._nmlLongPressSetup = true;
function onDown(e) {
if (e.button !== undefined && e.button !== 0) return;
_longPressFired = false;
clearTimeout(_likePressTimer);
var t = setTimeout(function () {
_longPressFired = true;
doTripleAction();
var idx = _allLikePressTimers.indexOf(t);
if (idx !== -1) _allLikePressTimers.splice(idx, 1);
}, 3000);
_likePressTimer = t;
_allLikePressTimers.push(t);
setTimeout(function () {
var idx2 = _allLikePressTimers.indexOf(t);
if (idx2 !== -1) nmlCenterToast('继续长按以三连...', 1200);
}, 1800);
}
function onUp() {
clearTimeout(_likePressTimer);
for (var i = 0; i < _allLikePressTimers.length; i++) clearTimeout(_allLikePressTimers[i]);
_allLikePressTimers = [];
}
function onCaptureClick(e) {
if (_longPressFired) {
e.stopPropagation();
e.stopImmediatePropagation();
e.preventDefault();
_longPressFired = false;
}
}
btn.addEventListener('mousedown', onDown);
btn.addEventListener('mouseup', onUp);
btn.addEventListener('mouseleave', onUp);
btn.addEventListener('click', onCaptureClick, true); // capture phase
btn.addEventListener('touchstart', onDown, { passive: true });
btn.addEventListener('touchend', onUp);
btn.addEventListener('touchcancel', onUp);
btn._nmlDownHandler = onDown;
btn._nmlUpHandler = onUp;
btn._nmlClickHandler = onCaptureClick;
}
function trySetupLikeLongPress() {
var all = document.querySelectorAll('.action-btn');
var btn = all[0] || null;
if (btn) setupLikeBtnLongPress(btn);
}
function nmlCleanupTripleAction() {
cancelQLongPress();
clearTimeout(_likePressTimer);
for (var t = 0; t < _allLikePressTimers.length; t++) clearTimeout(_allLikePressTimers[t]);
_allLikePressTimers = [];
_tripleDone = false;
_qLongPress = false;
if (_likeBtnObserver) { _likeBtnObserver.disconnect(); _likeBtnObserver = null; }
// Remove old listeners before resetting flag so they can be re-bound
var allBtns = document.querySelectorAll('.action-btn');
for (var i = 0; i < allBtns.length; i++) {
var b = allBtns[i];
if (b._nmlDownHandler) b.removeEventListener('mousedown', b._nmlDownHandler);
if (b._nmlUpHandler) b.removeEventListener('mouseup', b._nmlUpHandler);
if (b._nmlUpHandler) b.removeEventListener('mouseleave', b._nmlUpHandler);
if (b._nmlClickHandler) b.removeEventListener('click', b._nmlClickHandler, true);
if (b._nmlDownHandler) b.removeEventListener('touchstart', b._nmlDownHandler);
if (b._nmlUpHandler) b.removeEventListener('touchend', b._nmlUpHandler);
if (b._nmlUpHandler) b.removeEventListener('touchcancel', b._nmlUpHandler);
b._nmlLongPressSetup = false;
b._nmlDownHandler = null;
b._nmlUpHandler = null;
b._nmlClickHandler = null;
}
}
// Channel panel: hover logo to show category nav
const CATEGORIES = [
{ name: '动漫', slug: 'anime' },
{ name: '游戏', slug: 'game' },
{ name: '音乐', slug: 'music' },
{ name: '舞蹈', slug: 'dance' },
{ name: '科技', slug: 'tech' },
{ name: '吐槽', slug: 'commentary' },
{ name: '漫画', slug: 'manga' },
{ name: '鬼畜', slug: 'kichiku' },
{ name: 'mmd', slug: 'mmd' },
{ name: '日常', slug: 'daily' },
{ name: 'AI-娱乐', slug: 'ai-entertainment' },
{ name: '音mad', slug: 'otomad' }
];
let channelPanel = null;
let channelTimer = null;
function buildChannelPanel() {
if (channelPanel) return;
channelPanel = document.createElement('div');
channelPanel.className = 'nml-channel-panel';
channelPanel.setAttribute('data-v-e737bc8a', '');
channelPanel.innerHTML =
'' +
'' +
CATEGORIES.map(function (c) {
return '
' + c.name + '
';
}).join('') +
'
';
// Click: store target channel in sessionStorage, then navigate to homepage
channelPanel.addEventListener('click', function (e) {
var link = e.target.closest('.nml-chan-link');
if (!link) return;
var slug = link.getAttribute('data-slug');
if (slug) {
try { sessionStorage.setItem('nml_pending_channel', slug); } catch (e) {}
}
window.location.href = '/';
});
}
function setupChannelPanel() {
// Don't create if native header-channel already exists (e.g. on homepage)
if (document.querySelector('.header-channel')) return;
var logo = document.querySelector('.entry-title');
if (!logo) return;
buildChannelPanel();
// Insert panel right after the logo
logo.style.position = 'relative';
if (!channelPanel.parentNode) {
logo.appendChild(channelPanel);
}
logo.addEventListener('mouseenter', function () {
clearTimeout(channelTimer);
channelPanel.classList.add('show');
});
logo.addEventListener('mouseleave', function () {
channelTimer = setTimeout(function () {
channelPanel.classList.remove('show');
}, 200);
});
channelPanel.addEventListener('mouseenter', function () {
clearTimeout(channelTimer);
});
channelPanel.addEventListener('mouseleave', function () {
channelPanel.classList.remove('show');
});
}
// Channel panel styles
(function injectChannelStyles() {
var s = document.createElement('style');
s.textContent =
'.nml-channel-panel{' +
'display:none;position:absolute;top:100%;left:0;margin-top:4px;' +
'background:var(--bg1,#fff);border:1px solid var(--line_regular,#e5e5e5);' +
'border-radius:10px;padding:12px 16px;z-index:1003;' +
'box-shadow:0 8px 32px rgba(0,0,0,0.12);white-space:nowrap;' +
'min-width:360px;' +
'}' +
'.nml-channel-panel.show{display:flex;gap:24px;}' +
'.nml-channel-panel .channel-icons{display:flex;gap:16px;flex-shrink:0;}' +
'.nml-channel-panel .channel-icons__item{display:flex;flex-direction:column;align-items:center;gap:6px;cursor:pointer;}' +
'.nml-channel-panel .icon-bg{width:42px;height:42px;border-radius:10px;display:flex;align-items:center;justify-content:center;transition:transform 0.2s;}' +
'.nml-channel-panel .icon-bg__dynamic{background:#ff6fae;color:#fff;}' +
'.nml-channel-panel .icon-bg__popular{background:#ff9500;color:#fff;}' +
'.nml-channel-panel .icon-title{font-size:12px;color:var(--text2,#666);}' +
'.nml-channel-panel .channel-icons__item:hover .icon-bg{transform:scale(1.08);}' +
'.nml-channel-panel .right-channel-container{display:flex;}' +
'.nml-channel-panel .channel-items__left{display:grid;grid-template-columns:repeat(3,1fr);gap:4px 8px;}' +
'.nml-channel-panel .channel-link{font-size:13px;color:var(--text2,#666);cursor:pointer;padding:4px 8px;border-radius:6px;transition:all 0.15s;}' +
'.nml-channel-panel .channel-link:hover{background:var(--graph_bg_thin,rgba(0,0,0,0.04));color:var(--brand_pink,#ff6fae);}';
document.head.appendChild(s);
})();
// Search box: keep history panel positioned below the search bar
(function injectSearchStyles() {
var s = document.createElement('style');
s.id = 'nml-search-fix';
s.textContent =
/* Absolute-position the search panel right below #nav-searchform */
'.search-panel{position:absolute!important;top:100%!important;left:0!important;right:0!important;width:auto!important;z-index:1001!important;}' +
'#nav-searchform{position:relative!important;overflow:visible!important;}' +
'.center-search__bar{overflow:visible!important;}';
document.head.appendChild(s);
})();
// Mobile responsive styles (<=760px)
(function injectMobileStyles() {
var s = document.createElement('style');
s.id = 'nml-mobile-styles';
s.textContent =
'@media (max-width: 760px) {' +
/* Player area: full-width, no rounded corners */
'.player-wrapper{width:100%!important;max-width:100%!important;margin-left:0!important;margin-right:0!important;}' +
'.player-wrap{border-radius:0!important;margin-left:-10px!important;margin-right:-10px!important;margin-bottom:8px!important;}' +
/* Player controls: prevent button overflow */
/* Hide volume slider on mobile (hardware buttons + keyboard arrows) */
'.custom-video-player .volume-control{display:none!important;}' +
/* Allow controls to wrap onto a second line if needed */
'.custom-video-player .controls{flex-wrap:wrap!important;height:auto!important;padding:4px 4px!important;}' +
'.custom-video-player .controls-row{flex-wrap:wrap!important;gap:3px!important;padding:0 2px!important;}' +
/* Make buttons compact */
'.custom-video-player .control-btn{width:30px!important;height:30px!important;padding:2px!important;font-size:11px!important;min-width:30px!important;}' +
/* Progress bar area: keep it full-width */
'.custom-video-player .progress-area{width:100%!important;flex-basis:100%!important;}' +
/* Non-fullscreen: minimal controls, only progress + fullscreen */
/* Hide all control buttons */
'.player-wrapper:not(:fullscreen) .custom-video-player .control-btn{display:none!important;}' +
/* Show only the last control button (fullscreen / enlarge) */
'.player-wrapper:not(:fullscreen) .custom-video-player .controls-row .control-btn:last-of-type{display:flex!important;width:34px!important;height:34px!important;}' +
/* Also try by title */
'.player-wrapper:not(:fullscreen) .custom-video-player .control-btn[title*="屏"]{display:flex!important;}' +
'.player-wrapper:not(:fullscreen) .custom-video-player .control-btn[title*="fullscreen"]{display:flex!important;}' +
'.player-wrapper:not(:fullscreen) .custom-video-player .control-btn[title*="Fullscreen"]{display:flex!important;}' +
/* Hide volume, resolution, speed, danmaku toggle */
'.player-wrapper:not(:fullscreen) .custom-video-player .volume-control{display:none!important;}' +
'.player-wrapper:not(:fullscreen) .resolution-selector{display:none!important;}' +
'.player-wrapper:not(:fullscreen) .nml-speed-wrapper{display:none!important;}' +
'.player-wrapper:not(:fullscreen) .danmaku-toggle-btn{display:none!important;}' +
/* Hide danmaku layer */
'.player-wrapper:not(:fullscreen) .danmaku-container{display:none!important;}' +
/* When fullscreen, restore everything */
'.player-wrapper:fullscreen .custom-video-player .control-btn{display:flex!important;}' +
'.player-wrapper:fullscreen .custom-video-player .volume-control{display:flex!important;}' +
'.player-wrapper:fullscreen .resolution-selector{display:flex!important;}' +
'.player-wrapper:fullscreen .nml-speed-wrapper{display:flex!important;}' +
'.player-wrapper:fullscreen .danmaku-toggle-btn{display:flex!important;}' +
'.player-wrapper:fullscreen .danmaku-container{display:block!important;}' +
/* Danmaku input area */
'.danmaku-input-wrapper{padding:6px 8px!important;gap:4px!important;flex-wrap:wrap!important;}' +
'.danmaku-input{flex:1!important;font-size:13px!important;height:34px!important;min-width:0!important;padding:6px 48px 6px 10px!important;}' +
'.danmaku-input-wrapper .send-btn{width:32px!important;height:32px!important;min-width:32px!important;padding:3px!important;}' +
'.danmaku-input-wrapper .settings-btn{width:28px!important;height:28px!important;min-width:28px!important;padding:2px!important;flex-shrink:0!important;}' +
/* Danmaku settings panel: fix for mobile */
/* The panel is injected by Vue; make it screen-friendly */
'.danmaku-input-wrapper .settings-panel{' +
'position:fixed!important;top:auto!important;bottom:0!important;left:0!important;right:0!important;' +
'width:100%!important;max-width:100%!important;max-height:65vh!important;overflow-y:auto!important;' +
'border-radius:16px 16px 0 0!important;z-index:10020!important;' +
'padding:12px 14px!important;box-sizing:border-box!important;' +
'}' +
/* Darken backdrop when settings panel is open */
'.danmaku-input-wrapper .settings-panel::before{' +
'content:"";position:fixed;inset:0;bottom:65vh;background:rgba(0,0,0,0.4);z-index:-1;pointer-events:none;' +
'}' +
/* Video info action bar: compact */
'.action-bar{gap:6px!important;flex-wrap:wrap!important;}' +
'.action-btn{font-size:12px!important;padding:6px 12px!important;min-height:32px!important;gap:4px!important;}' +
'.action-btn svg{width:15px!important;height:15px!important;}' +
/* Sidebar / related: tighten spacing */
'.detail-sidebar .section-title{font-size:14px!important;margin-bottom:10px!important;}' +
'.related-cover{width:140px!important;height:79px!important;}' +
'.related-title{font-size:13px!important;}' +
/* Channel panel: narrower, 2 columns, hide icons */
'.nml-channel-panel{min-width:auto!important;width:92vw!important;left:50%!important;transform:translateX(-50%)!important;gap:0!important;padding:8px 12px!important;}' +
'.nml-channel-panel .channel-icons{display:none!important;}' +
'.nml-channel-panel .channel-items__left{grid-template-columns:repeat(2,1fr)!important;gap:2px 4px!important;}' +
'.nml-channel-panel .channel-link{font-size:12px!important;padding:4px 6px!important;}' +
/* Kaomoji panel: viewport-constrained */
'.nml-kao-panel{min-width:auto!important;max-width:96vw!important;left:2vw!important;right:2vw!important;padding:6px!important;border-radius:8px!important;}' +
'.nml-kao-tabs{flex-wrap:nowrap!important;overflow-x:auto!important;max-width:100%!important;padding-bottom:4px!important;gap:2px!important;}' +
'.nml-kao-tab{font-size:11px!important;padding:3px 8px!important;white-space:nowrap!important;flex-shrink:0!important;}' +
'.nml-kao-grid{max-height:180px!important;gap:3px!important;}' +
'.nml-kao-item{font-size:13px!important;padding:4px 8px!important;}' +
/* Advanced danmaku settings: 3 columns → stack */
'.nml-adv-controls{padding:6px!important;border-radius:6px!important;}' +
'.nml-adv-columns{flex-direction:column!important;gap:6px!important;}' +
'.nml-adv-time-row{flex-direction:column!important;gap:4px!important;}' +
'.nml-adv-col-row input[type=range]{height:5px!important;}' +
'.nml-adv-col-title{font-size:10px!important;}' +
/* Danmaku list panel */
'.nml-dm-list-panel{width:100%!important;margin-left:0!important;margin-right:0!important;}' +
'.nml-dm-list-body{max-height:200px!important;padding:6px 8px!important;}' +
'.nml-dm-params{padding-left:0!important;font-size:10px!important;white-space:normal!important;}' +
'.nml-dm-entry{font-size:12px!important;padding:4px 0!important;}' +
/* Speed menu */
'.nml-speed-wrapper .control-btn{font-size:12px!important;padding:2px 6px!important;}' +
'.nml-speed-wrapper .nml-speed-opt{font-size:12px!important;padding:6px 10px!important;}' +
/* Easing / Mode buttons: tighter on mobile */
'.nml-easing-btns{gap:2px!important;}' +
'.nml-easing-btn{font-size:10px!important;padding:2px 5px!important;}' +
'.nml-dm-mode-btn{font-size:11px!important;padding:2px 8px!important;}' +
/* Settings row: compact sliders */
'.setting-row{gap:6px!important;margin-bottom:2px!important;}' +
'.setting-row>span:first-child{font-size:12px!important;min-width:36px!important;}' +
'.setting-row input[type=range]{flex:1!important;min-width:0!important;}' +
'.setting-row .setting-val{font-size:11px!important;min-width:32px!important;text-align:right!important;}' +
/* Header restructure */
/* Hide logo area, right-entry--outside, download float */
'.left-entry{display:none!important;}' +
'.header-bar .right-entry--outside{display:none!important;}' +
'.mobile-download-float{display:none!important;}' +
/* Avatar: pin to top-left corner */
'.header-avatar-wrap{position:absolute!important;left:10px!important;top:50%!important;transform:translateY(-50%)!important;z-index:10!important;padding-right:0!important;}' +
'.header-avatar-wrap .default-login,.header-avatar-wrap .mini-avatar--small{left:0!important;top:50%!important;transform:translateY(-50%)!important;}' +
'.header-avatar-wrap:hover .mini-avatar--small{height:38px!important;left:0!important;top:50%!important;transform:translateY(-50%)!important;width:38px!important;}' +
/* Moved message button: icon-only, matches theme-toggle style */
'.nml-header-msg{display:flex!important;align-items:center!important;justify-content:center!important;' +
'color:var(--text2)!important;width:32px!important;height:32px!important;' +
'border-radius:50%!important;cursor:pointer!important;' +
'transition:all 0.2s!important;flex-shrink:0!important;margin-right:2px!important;}' +
'.nml-header-msg:hover{background:var(--graph_bg_thick)!important;color:var(--text1)!important;}' +
'.nml-header-msg svg{width:20px!important;height:20px!important;}' +
'.nml-header-msg span{display:none!important;}' +
/* Search bar: fill remaining header width, leave space for avatar (38px) + right icons */
'.center-search-container{flex:1!important;margin-left:44px!important;margin-right:4px!important;max-width:none!important;min-width:0!important;}' +
'.center-search-container .center-search__bar{max-width:none!important;}' +
/* Header: tighter padding */
'.header-bar{padding:0 8px!important;height:50px!important;}' +
/* Right entry: tighter spacing */
'.right-entry{margin-left:4px!important;}' +
'.theme-toggle{margin-right:2px!important;width:32px!important;height:32px!important;}' +
'.content-switch{gap:2px!important;}' +
'.content-switch__item{font-size:11px!important;padding:0 8px!important;min-width:0!important;height:28px!important;}' +
/* Channel bar: horizontal scroll */
'.header-channel{overflow-x:auto!important;overflow-y:hidden!important;white-space:nowrap!important;-webkit-overflow-scrolling:touch!important;scrollbar-width:none!important;flex-wrap:nowrap!important;}' +
'.header-channel::-webkit-scrollbar{display:none!important;}' +
'.header-channel>*{flex-shrink:0!important;}' +
/* Bottom nav: keep original message btn hidden */
'.mobile-nav-bar .nml-msg-hidden{display:none!important;}' +
/* Creator center: mobile layout */
/* Stack sidebar + content vertically */
'.creator-layout{flex-direction:column!important;height:auto!important;}' +
'.creator-container{height:auto!important;min-height:100vh!important;}' +
/* Sidebar: collapse to horizontal tab bar */
'.creator-aside{width:100%!important;border-right:none!important;border-bottom:1px solid var(--line_light)!important;}' +
'.aside-header{display:none!important;}' +
'.creator-menu{' +
'display:flex!important;flex-direction:row!important;overflow-x:auto!important;' +
'-webkit-overflow-scrolling:touch!important;scrollbar-width:none!important;' +
'border-right:none!important;flex:none!important;' +
'}' +
'.creator-menu::-webkit-scrollbar{display:none!important;}' +
'.creator-menu .el-menu-item{' +
'flex-shrink:0!important;white-space:nowrap!important;' +
'height:40px!important;line-height:40px!important;padding:0 16px!important;' +
'font-size:13px!important;border-bottom:2px solid transparent!important;' +
'}' +
'.creator-menu .el-menu-item.is-active{border-bottom-color:var(--brand_pink)!important;}' +
/* Main content: reduce padding */
'.creator-main{padding:10px!important;overflow-y:visible!important;}' +
'.content-wrapper{padding:12px!important;border-radius:6px!important;}' +
'.content-header{margin-bottom:12px!important;padding-bottom:10px!important;}' +
'.content-header h2{font-size:17px!important;}' +
/* Toolbar: wrap buttons */
'.toolbar{display:flex!important;flex-wrap:wrap!important;gap:8px!important;margin-bottom:12px!important;}' +
/* Table: horizontal scroll */
'.tab-content{overflow-x:auto!important;-webkit-overflow-scrolling:touch!important;}' +
'.tab-content .el-table{min-width:600px!important;}' +
/* Pagination: center on mobile */
'.pagination{justify-content:center!important;margin-top:16px!important;}' +
/* Collection grid: single column */
'.collection-grid{grid-template-columns:1fr!important;gap:12px!important;}' +
/* Card: tighter */
'.collection-card .card-header{flex-wrap:wrap!important;gap:6px!important;}' +
'.col-title{font-size:14px!important;}' +
'.description{font-size:13px!important;height:36px!important;}' +
/* Cover: smaller */
'.cover-wrapper{width:100px!important;height:56px!important;}' +
/* Add video section */
'.add-video-section{flex-wrap:wrap!important;gap:8px!important;}' +
'}';
document.head.appendChild(s);
})();
(function injectAvatarLabelFix() {
var s = document.createElement('style');
s.id = 'nml-avatar-label-fix';
s.textContent =
'.header-avatar-wrap:not(:has(.popShow)) .header-avatar-badge{height:16px!important;width:16px!important;}' +
'.header-avatar-wrap:not(:has(.popShow)) .header-avatar-badge svg{width:9px!important;height:9px!important;}';
document.head.appendChild(s);
})();
function trySelectChannel() {
var slug = null;
try { slug = sessionStorage.getItem('nml_pending_channel'); } catch (e) {}
if (!slug) return;
var nameMap = {};
CATEGORIES.forEach(function (c) { nameMap[c.slug] = c.name; });
if (slug === 'dynamic') { clearPending(); window.location.href = '/dynamic'; return; }
if (slug === 'popular' || slug === 'hot') { clearPending(); window.location.href = '/hot'; return; }
var targetName = nameMap[slug];
if (!targetName) { clearPending(); return; }
var container = document.querySelector('.header-channel');
var searchRoot = container || document;
var allEls = searchRoot.querySelectorAll('a, button, span, div');
for (var i = 0; i < allEls.length; i++) {
if ((allEls[i].textContent || '').trim() === targetName) {
allEls[i].click();
clearPending();
return;
}
}
for (var j = 0; j < allEls.length; j++) {
if ((allEls[j].textContent || '').indexOf(targetName) !== -1) {
allEls[j].click();
clearPending();
return;
}
}
fetch('/api/categories/slug/' + slug)
.then(function (r) { return r.json(); })
.then(function (data) {
var cat = data.category || data;
if (cat && cat.id) {
clearPending();
try {
var app = document.getElementById('__nuxt').__vue_app__;
app.config.globalProperties.$router.push('/category/' + cat.id);
} catch (e2) {
window.location.href = '/category/' + cat.id;
}
}
}).catch(function () {});
}
function clearPending() {
try { sessionStorage.removeItem('nml_pending_channel'); } catch (e) {}
}
// Kaomoji panel
const KAOMOJI = [
{ c: '开心', list: [
'╰(*°▽°*)╯', '(^▽^)', '(◕‿◕)', '✧(≖ ◡ ≖✿)', '(。•̀ᴗ-✿)', '(◠‿◠)',
'(๑˃̵ᴗ˂̵)و', '(≧∇≦)', '(◍•ᴗ•◍)', '(。・ω・。)', '(ノ◕ヮ◕)ノ',
'(●\'◡\'●)', '(◕ᴗ◕✿)', '♪(^∇^*)', '☆*:.。.o(≧▽≦)o.。.:*☆',
'ヽ(>∀<☆)ノ', '♪(๑ᴖ◡ᴖ๑)♪', '(▰˘◡˘▰)', '╭(●`∀´●)╯',
'ヾ(@^∇^@)ノ', 'o(*≧▽≦)ツ', '(ᗒᗨᗕ)', '٩(◕‿◕。)۶',
'(★ω★)', '╰(▔∀▔)╯', '(✪ω✪)', 'o(≧∇≦o)',
'(๑•̀ㅂ•́)و✧', '٩(ˊᗜˋ*)و', 'ヽ(✿゚▽゚)ノ',
'( ^ω^)', '( ̄︶ ̄)', '( ´∀`)', '(o゚ω゚o)', '⌒(。・.・。)⌒','♪(╯^-^)╯♪'
]},
{ c: '大笑', list: [
'(σ゚д゚)σ', 'ヾ(@>▽<@)ノ', '♪ヽ(^^ヽ)♪', '(・∀・)',
'(●´∀`)●', 'Ψ( ̄∀ ̄)Ψ', 'ヽ(´▽`)/', '(๑´ㅂ`๑)',
'哈哈哈哈', '恍恍惚惚', '红红火火恍恍惚惚',
'啊哈哈哈哈(((o(*゚▽゚*)o)))', '笑死(≧∀≦)',
'(σ゚∀゚)σ', '(((o(*゚▽゚*)o)))', 'ヽ(〃∀〃)ノ',
'(ノ≧∀≦)ノ', '٩( ᐛ )و', '└(^o^)┘',
'(๑>ᴗ<๑)', '(◡‿◡✿)', '∩(︶▽︶)∩',
'( ゚∀゚)', '(●`∀´●)', '╭(●`∀´●)╯', '( ゜∀゜)'
]},
{ c: '喜欢', list: [
'(。♥‿♥。)', '(❤ω❤)', '(´▽`ʃ♡ƪ)', '♡(◡‿◡✿)',
'(⁄ ⁄>⁄ ▽ ⁄<⁄ ⁄)', '(。・//ε//・。)', '(♡°▽°♡)', '(✿◠‿◠)',
'♥(ノ´∀`)', '(´ ε ` )♡', '(。・ω・。)ノ♡',
'(*^3^)/~☆', '(◕‿◕✿)', '(っ˘з(˘⌣˘ )', '(*^3^)',
'ヽ(愛´∀`愛)ノ', '(。・ω・。)ノ♡', 'チュ~(´ε`*)',
'(๑ơ ₃ ơ)♥', '(●♡∀♡)', '(. ❛ ᴗ ❛.)', '(⁄ ⁄•⁄ω⁄•⁄ ⁄)'
]},
{ c: '难过', list: [
'(╥_╥)', '(╯︵╰,)', '(。•́︿•̀。)', '(╥﹏╥)', 'ಥ_ಥ',
'(个_个)', '(╯_╰)', '(。ŏ﹏ŏ)', '(╥ω╥)', '(´;︵;`)',
'。゚(゚´Д`゚)゚。', '(´°̥̥̥̥̥̥̥̥ω°̥̥̥̥̥̥̥̥`)',
'(;´༎ຶД༎ຶ`)', '(´;ω;`)', '(༎ຶ ෴ ༎ຶ)',
'。・゚゚・(>д<)・゚゚・。', '(;_;)', '(ノД`)',
'(ノ_<。)','。・゜・(ノД`)・゜・。', '(iДi)',
'( >﹏< )', 'ヽ(*。>Д<)o゜'
]},
{ c: '生气', list: [
'(╬ Ò﹏Ó)', '(#`д´)ノ', '(╯°□°)╯︵ ┻━┻', '(≖_≖ )',
'(¬_¬)', '(`皿´#)', '(⋋▂⋌)', 'ヽ(`⌒´)ノ',
'(╬゚◥益◤゚)', '(◣_◢)', '(`Δ´)!',
'(#`皿´)', '(╬ ̄皿 ̄)', '凸(`0´)凸',
'━━━╋══◥◣_◢◤', '╋══◥◣_◢◤', '(≧m≦)',
'ヽ(●-`Д´-)ノ', 's(・`ヘ´・;)ゞ', '(; ̄Д ̄)',
'(#°Д°)', 'ヽ(・ω・´メ)', '(。・`ω´・)'
]},
{ c: '惊讶', list: [
'(⊙_⊙)', '(º ロ º)', '(°ロ°)', '(⊙﹏⊙)', '(〇o〇;)',
'( Д ) ゚ ゚', '∑(O_O;)', '(;° ロ°)', '(⊙.☉)',
'∑(゚Д゚)', '(((;꒪ꈊ꒪;)))', '(゚ロ゚)',
'щ(゚Д゚щ)', '(。ӧ◡ӧ。)', '!!!∑(゚Д゚ノ)ノ',
'‼(•\'╻\'• )', '〣( ºΔº )〣', 'щ(゜ロ゜щ)',
'Σ(°△°|||)', '!!!w(゚Д゚)w', '!!!(ʘ言ʘ╬)',
'(゚Д゚≡゚д゚)', 'щ(゚Д゚щ)'
]},
{ c: '害羞', list: [
'(〃ω〃)', '(。・//ω//・。)', '(〃∀〃)', '(。-人-。)',
'(๑•́ ₃ •̀๑)', '(*/ω\*)', '(〃⌒ー⌒〃)',
'(❁´◡`❁)', '(。•́︿•̀。)', '( ̄▽ ̄*)ゞ',
'(>/////<)', '(*/∇\*)', '(*ノωノ)',
'(〃▽〃)','(´,,•ω•,,)♡', '(⁄ ⁄>⁄ ▽ ⁄<⁄ ⁄)',
'(◞‸◟)', '(:3[▓▓]', '(*ノ▽ノ)', '(´_っ`)'
]},
{ c: '眨眼', list: [
'(◕‿↼)', '(。•̀ᴗ-)✧', '◔ ⌣ ◔', '(・ω<)',
'(。•ᴗ•。)♡', '( ̄ε ̄")', '(。・ω・。)ノ♡',
'(^◡^)っ', '(๑¯◡¯๑)', '(✿◠‿◠)',
'(≖‿≖)✧', '(・ω<)☆', '(●´ω`●)',
'(・`ω´・)', '(^_-)','(. ❛ ᴗ ❛.)',
'☆~(ゝ。∂)', '(=∀=*)', '(ΦωΦ)', '(ΦωΦ)'
]},
{ c: '困惑', list: [
'( ̄ω ̄;)', '(@_@)', '(・_・;)', '(¯ . ¯;)',
'(;¬_¬)', '(´・ω・`)?', '┐( ̄ヘ ̄")┌', '╮( ̄▽ ̄")╭',
'( ̄~ ̄;)', '(´-ω-`)', '(。-ω-)zzz',
'( ̄▽ ̄*)ゞ', '( ̄□ ̄;)', '(´-﹏-`;)',
'(;一_一)', '(´゚ω゚`)', 'щ(ºДºщ)',
'(O_o)', '( ・◇・)?','( ̄. ̄)',
'(´・ω・`)'
]},
{ c: '无语', list: [
'( ̄▽ ̄")', '(;´Д`)', '(´-ι_-`)', '( ̄ω ̄)',
'(=_=;)', '(´・_・`)', '( ˘・з・)', '( ̄. ̄)',
'┐(´-`)┌', '╮( ̄▽ ̄)╭', '( ̄ェ ̄;)',
'(눈_눈)', '≖_≖', 'ー( ̄~ ̄)ξ',
'( ̄^ ̄)', 'ヘ(´o`)ヘ', '(´_ゝ`)',
'( ̄へ ̄)', '(›´ω`‹ )', '(  ̄  ̄ )', '(。_。)'
]},
{ c: '傲娇', list: [
'哼!( ̄^ ̄)ゞ', '( ̄ε ̄")', '(,-`д´-)', '( `ー´)',
'(-`ω´-)', '( ̄へ  ̄ )', '╭(╯^╰)╮',
'(。-`ω´-)', '(`・ω・´)', '( ̄~ ̄)',
'(≧へ≦)╯', '(-`ェ´-╬)', '(`へ´*)ノ',
'( ・`⌓´・)', '(〃` 3´〃)', '(`ε´)',
'(,,Ծ‸Ծ,,)', 'ヽ(`⌒´)ノ', '(◣_◢)',
'(`・ω・´)', '(`・ω・´)', '(。-`ω´-)'
]},
{ c: '舞蹈', list: [
'┗(^0^)┓', '♪(┌・。・)┌', '♪ヽ(^^ヽ)♪', 'ƪ(˘⌣˘)ʃ',
'ヾ(⌐■_■)ノ♪', '♬♩♫♪☻(●´∀`●)☺♪♫♩♬',
'┏(^0^)┛', 'ヘ(^_^ヘ)', 'ヾ(´〇`)ノ',
'♪♪♪ ヽ(ˇ∀ˇ )ゞ', '♫꒰・‿・๑꒱', '♪(o・ω・)ノ',
'(〜^∇^)〜', '♪ヽ( ⌒o⌒)人(⌒-⌒ )v ♪',
'ヘ( ̄ω ̄ヘ)', 'ƪ(‾ε‾")ʃ', '┏(^0^)┛┗(^0^)┓',
'ヘ(^_^ヘ) ヘ(^o^ヘ)', '♪ ヾ(⌒ε⌒*)ゞ', 'ヾ(´・ω・`)ノ'
]},
{ c: '摊手', list: [
'¯\\_(ツ)_/¯', '┐(´д`)┌', '╮(╯_╰)╭', '(;-_-)︵',
'(ツ)', 'ヽ(。_°)ノ', '┐( ̄ヮ ̄)┌', '(´-ι_-`)',
'~( ̄▽ ̄~)', '┐(´~`)┌', '╮( ̄~ ̄)╭',
'┐( ̄ー ̄)┌', '( ̄ο ̄)', '(。-ω-)',
'┐(´∀`)┌', '~(´ー`~)', '╮(╯∀╰)╭',
'┐(シ)┌', '( ´_ゝ`)', '┐( ̄∀ ̄)┌'
]},
{ c: '动物', list: [
'(=\\^・ω・\\^=)', '(◕ᴥ◕)', '(=\'◉ω◉\'=)', 'V●ᴥ●V',
'(=^ ◡ ^=)', '(=\\^._.\\^=)∫', '( ̄(00) ̄)', '(°(oo)°)',
'くコ:彡', '/(=;x;=)\', '(=▼ω▼=)', '(=ФωФ=)',
'(^=˃ᆺ˂)', '(=\'×\'=)', 'U•ᴥ•U', '(ᵔᴥᵔ)',
'ฅ(^ω^ฅ)', '/(・×・)\', '/(˃ᆺ˂)\', '(=`ェ´=)'
]},
{ c: '颜艺', list: [
'( ͡° ͜ʖ ͡°)', '( ͡~ ͜ʖ ͡~)', '( ͠° ͟ʖ ͡°)',
'ヽ(͡◕ ͜ʖ ͡◕)ノ', '( ͡°╭ͮ ͟ʖ╮ ͡° )',
'(☞゚ヮ゚)☞', '☜(゚ヮ゚☜)', '(⌐■_■)',
'(•̀ᴗ•́)و ̑̑', '(◕ᴥ◕ʋ)', '( ͡ಠ ʖ̯ ͡ಠ)',
'凸(¬‿¬)凸', 't(-_-t)', '(╭ರ_⊙)',
'(♯`∧´)', '(ز ͡° ͜ʖ ͡°)ز', '┌∩┐(◣_◢)┌∩┐',
'( ͡° ͜ʖ ͡°)=ε/̵͇̿̿/\'̿̿ ̿ ̿̿', '(ง\'̀-\'́)ง', 'щ(゚Д゚щ)', '( ゚∀。)'
]},
{ c: '经典', list: [
'(づ。◕‿‿◕。)づ', '(づ ̄ ³ ̄)づ', '(◡ ω ◡)',
'(ノ´ヮ`)ノ*: ・゚', '(>^ω^<)', '*:・゚✧(ꈍᴗꈍ)✧・゚:*',
'☆*。★゚*♪ヾ(☆ゝз・)ノ', '✧*:・゚ヾ(●´∀`●)',
'°˖✧◝(⁰▿⁰)◜✧˖°', '(✿⊙‿⊙)',
'(ノ◕ヮ◕)ノ*:・゚✧', '(ノ>ω<)ノ :。・:*:・゚\'',
'★,。・:*:・゚☆ ヽ(〃・ω・)ノ', ':・゚✧゚・: *ヽ(◕ヮ◕ヽ)',
'~(^з^)-☆', '(>ω^<)ノ彡☆',
'☆⌒(≧▽° )', '(*´▽`*)ノシ',
'*・゜゚・*:.。..。.:*・\'(*゚▽゚*)\'・*:.。. .。.:*・゜゚・*'
]},
{ c: '卖萌', list: [
'(◕‿◕✿)', '✿(。◕‿◕。)✿', '(◕ω◕✿)', '(◕ᴗ◕✿)',
'(◡ ω ◡)', '(★^ー^★)', '✿♥‿♥✿',
'(。・ω・。)', '(^ω^)', '(≧ω≦)',
'٩(ˊᗜˋ*)و', 'ヾ(@⌒ー⌒@)ノ', '(っ´▽`)っ',
'\(^ω^\)', '∪・ω・∪', '(๑•̀ㅂ•́)و✧',
'(ᗒᗨᗕ)', '(●´艸`)', '(´。• ᵕ •。`)',
'( ˘•ω•˘ )', '(。・ω・。)'
]},
{ c: '躺平', list: [
'_(:3 」∠)_', '_(:3⌒゙)_', '_(´ཀ`」 ∠)_',
'(:3[▓▓]', '(:3[」_]', '(:3_ヽ)_',
'_ノ乙(、ン、)_', '(๑•́ ₃ •̀๑)エー', '(:3[▓▓▓]',
'(。-ω-)zzz', '( ̄ρ ̄)..zzZZ', '(◎−◎;)',
'(´。`)',
'(ˇ▽ˇ)ZZz', '∪・ω・∪zzZ', '(´〜`*) zzz',
'ZZzz(。-_-。)ZZzz', '(。-ω-)'
]},
{ c: '战斗', list: [
'(╯°□°)╯︵ ┻━┻', '┬─┬ノ( º _ ºノ)', '(ノಠ益ಠ)ノ',
'(ง •̀_•́)ง', '(╯°益°)╯彡┻━┻',
'┬─┬ ノ( ゜-゜ノ)', '(ʘ言ʘ╬)',
'(ノ`Д´)ノ彡┻━┻', '┬──┬ ノ(ò_óノ)',
'(ノ°Д°)ノ︵ ┻━┻', '┻━┻ ︵ヽ(´Д`ヽ)',
'(╯ಠ_ಠ)╯︵ ┻━┻', '(ノ`□´)ノ⌒┻━┻',
'(╯°□°)╯︵(\\.o.)\\', '┬─┬⃰͡ (ᵔᵕᵔ͜ )',
'┻━┻︵ \\(°□°)/ ︵ ┻━┻', '(ノ-_-)ノ~┻━┻',
'ヽ(゚Д゚)ノ', '┗(`Д゚┗(`゚Д゚)┓゚Д´)┛'
]},
{ c: 'fumo', list: [
'(ᗜᴗᗜ)', '(ᗜˬᗜ)', '(ᗜ_ᗜ)',
'(ᗜ‸ᗜ)'
]}
];
let kaoPanel = null;
let kaoActiveInput = null;
let kaoHideTimer = null;
let kaoActiveCat = 0;
function buildKaoPanel() {
if (kaoPanel) return;
kaoPanel = document.createElement('div');
kaoPanel.className = 'nml-kao-panel';
kaoPanel.addEventListener('mousedown', function (e) { e.preventDefault(); });
// Category tabs
var tabs = document.createElement('div');
tabs.className = 'nml-kao-tabs';
KAOMOJI.forEach(function (cat, i) {
var tab = document.createElement('span');
tab.className = 'nml-kao-tab' + (i === 0 ? ' active' : '');
tab.textContent = cat.c;
tab.addEventListener('mousedown', function (e) {
e.stopPropagation();
e.preventDefault();
switchKaoCat(i);
});
tabs.appendChild(tab);
});
kaoPanel.appendChild(tabs);
// Kaomoji grid
var grid = document.createElement('div');
grid.className = 'nml-kao-grid';
renderKaoGrid(grid, 0);
kaoPanel.appendChild(grid);
document.body.appendChild(kaoPanel);
// Styles
var s = document.createElement('style');
s.textContent =
'.nml-kao-panel{' +
'display:none;position:fixed;z-index:10010;' +
'background:var(--bg1,#1a1a1a);border:2px solid var(--brand_pink,#ff6fae);' +
'border-radius:10px;padding:10px;min-width:400px;max-width:680px;' +
'box-shadow:0 8px 32px rgba(0,0,0,0.5),0 0 0 1px rgba(255,111,174,0.3) inset;' +
'}' +
'.nml-kao-panel.show{display:block;}' +
'.nml-kao-tabs{' +
'display:flex;flex-wrap:wrap;gap:3px;margin-bottom:10px;' +
'border-bottom:2px dashed var(--brand_pink,rgba(255,111,174,0.4));padding-bottom:8px;' +
'}' +
'.nml-kao-tab{' +
'font-size:12px;padding:4px 10px;border-radius:12px;cursor:pointer;' +
'color:var(--text2,#999);transition:all 0.15s;user-select:none;' +
'border:1px solid transparent;' +
'}' +
'.nml-kao-tab:hover{background:var(--graph_bg_thin,rgba(255,255,255,0.06));color:var(--text1,#ddd);border-color:var(--brand_pink,rgba(255,111,174,0.3));}' +
'.nml-kao-tab.active{background:var(--brand_pink,#ff6fae);color:#fff;border-color:var(--brand_pink,#ff6fae);}' +
'.nml-kao-grid{' +
'display:flex;flex-wrap:wrap;gap:4px;max-height:280px;overflow-y:auto;' +
'scrollbar-width:thin;scrollbar-color:var(--brand_pink,#ff6fae) transparent;' +
'}' +
'.nml-kao-grid::-webkit-scrollbar{width:4px;}' +
'.nml-kao-grid::-webkit-scrollbar-thumb{background:var(--brand_pink,#ff6fae);border-radius:4px;}' +
'.nml-kao-item{' +
'font-size:14px;padding:5px 10px;border-radius:6px;cursor:pointer;' +
'color:var(--text1,#ddd);transition:all 0.12s;user-select:none;' +
'white-space:nowrap;line-height:1.7;border:1px solid transparent;' +
'}' +
'.nml-kao-item:hover{background:var(--brand_pink,#ff6fae);color:#fff;border-color:rgba(255,255,255,0.2);}';
document.head.appendChild(s);
}
function renderKaoGrid(grid, catIdx) {
grid.innerHTML = '';
var list = KAOMOJI[catIdx].list;
list.forEach(function (kao) {
var item = document.createElement('span');
item.className = 'nml-kao-item';
item.textContent = kao;
item.addEventListener('mousedown', function (e) {
e.stopPropagation();
e.preventDefault();
insertKaomoji(kao);
});
grid.appendChild(item);
});
}
function switchKaoCat(idx) {
kaoActiveCat = idx;
var tabs = kaoPanel.querySelectorAll('.nml-kao-tab');
tabs.forEach(function (t, i) { t.classList.toggle('active', i === idx); });
var grid = kaoPanel.querySelector('.nml-kao-grid');
renderKaoGrid(grid, idx);
}
function insertKaomoji(text) {
var el = kaoActiveInput;
if (!el) return;
el.focus();
if (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT') {
var s = el.selectionStart;
var e = el.selectionEnd;
el.value = el.value.substring(0, s) + text + el.value.substring(e);
el.selectionStart = el.selectionEnd = s + text.length;
} else if (el.isContentEditable) {
var sel = window.getSelection();
if (sel.rangeCount) {
var r = sel.getRangeAt(0);
r.deleteContents();
var tn = document.createTextNode(text);
r.insertNode(tn);
r.setStartAfter(tn);
r.collapse(true);
sel.removeAllRanges();
sel.addRange(r);
}
}
el.dispatchEvent(new Event('input', { bubbles: true }));
}
function positionKaoPanel(input) {
var rect = input.getBoundingClientRect();
var top = rect.bottom + 6;
var left = rect.left;
if (left + 420 > window.innerWidth) left = window.innerWidth - 430;
if (left < 4) left = 4;
if (top + 320 > window.innerHeight) top = rect.top - 330;
if (top < 4) top = 4;
kaoPanel.style.top = top + 'px';
kaoPanel.style.left = left + 'px';
kaoPanel.classList.add('show');
}
function hideKaoPanel() {
if (kaoPanel) kaoPanel.classList.remove('show');
kaoActiveInput = null;
}
function onCommentFocus(e) {
buildKaoPanel();
kaoActiveInput = e.target;
positionKaoPanel(e.target);
}
function onCommentBlur() {
clearTimeout(kaoHideTimer);
var active = document.activeElement;
kaoHideTimer = setTimeout(function () {
if (kaoPanel && !kaoPanel.contains(active)) {
hideKaoPanel();
}
}, 150);
}
function isCommentLike(el) {
if (el.tagName !== 'TEXTAREA' && !el.isContentEditable) return false;
if (el.closest('[class*=search], [class*=login], [class*=sign], input[type=search]')) return false;
if (el.closest('.comment-input, .comment-editor, [class*=comment], [class*=reply], [class*=chat-input], [class*=message-input], [class*=post-comment], [class*=editor], [class*=rich-text]')) return true;
if (el.tagName === 'TEXTAREA') {
var ph = (el.placeholder || '').toLowerCase();
if (/评论|回复|留言|说点什么|发(表|布|送)|聊|输入|看法|弹幕|吐槽|写(下|点)|参与|讨论/.test(ph)) return true;
}
return false;
}
document.addEventListener('click', function (e) {
var el = e.target;
if (isCommentLike(el)) {
onCommentFocus({ target: el });
}
}, true);
document.addEventListener('focusin', function (e) {
var el = e.target;
if (isCommentLike(el)) {
onCommentFocus({ target: el });
}
});
document.addEventListener('focusout', function (e) {
if (isCommentLike(e.target)) {
onCommentBlur();
}
});
var keepActive = false;
var styleObserver = null;
function hookControlsStyle() {
if (styleObserver) return;
var controls = document.querySelector('.custom-video-player .controls');
if (!controls) return;
styleObserver = new MutationObserver(function (mutations) {
if (!keepActive) return;
mutations.forEach(function (m) {
if (m.type === 'attributes' && m.attributeName === 'style') {
if (m.target.style.display === 'none') {
m.target.style.display = '';
}
}
});
});
styleObserver.observe(controls, {
attributes: true,
attributeFilter: ['style']
});
}
function startGuard() {
if (keepActive) return;
keepActive = true;
hookControlsStyle();
}
function stopGuard() {
keepActive = false;
}
function tryHookControlsStyle() {
if (styleObserver) return;
if (document.querySelector('.custom-video-player .controls')) {
hookControlsStyle();
}
}
document.addEventListener('focusin', function (e) {
if (e.target.classList && e.target.classList.contains('danmaku-input')) {
startGuard();
}
});
document.addEventListener('focusout', function (e) {
if (e.target.classList && e.target.classList.contains('danmaku-input')) {
setTimeout(function () {
var a = document.activeElement;
if (a && (a.classList.contains('danmaku-input') ||
a.closest('.danmaku-input-wrapper') ||
a.closest('.settings-panel') ||
a.closest('.nml-kao-panel') ||
a.closest('.nml-dm-mode-section'))) return;
stopGuard();
}, 300);
}
});
function nmlCleanupKeepControls() {
keepActive = false;
if (styleObserver) {
styleObserver.disconnect();
styleObserver = null;
}
if (_hookInterval) {
clearInterval(_hookInterval);
_hookInterval = null;
}
}
tryHookControlsStyle();
var _hookRetries = 0;
var _hookInterval = setInterval(function () {
if (styleObserver || _hookRetries++ > 30) {
clearInterval(_hookInterval);
return;
}
tryHookControlsStyle();
}, 500);
document.addEventListener('focusin', function (e) {
if (e.target.classList && e.target.classList.contains('nav-search-input')) {
var form = document.getElementById('nav-searchform');
if (form) form.classList.add('nml-search-active');
}
});
document.addEventListener('focusout', function (e) {
if (e.target.classList && e.target.classList.contains('nav-search-input')) {
setTimeout(function () {
if (document.activeElement && document.activeElement.closest('#nav-searchform')) return;
var form = document.getElementById('nav-searchform');
if (form) form.classList.remove('nml-search-active');
}, 200);
}
});
var miniPlayerEl = null;
var miniVideoEl = null;
var miniSyncTimer = null;
var miniVisible = false;
var miniObserver = null;
function buildMiniPlayer() {
if (miniPlayerEl) return;
var EDGE = 6;
miniPlayerEl = document.createElement('div');
miniPlayerEl.className = 'nml-mini-player';
miniPlayerEl.innerHTML =
'' +
'' +
'' +
'';
miniPlayerEl.querySelector('.nml-mini-close').addEventListener('click', function (e) {
e.stopPropagation(); hideMiniPlayer();
});
miniPlayerEl.querySelector('.nml-mini-back').addEventListener('click', function (e) {
e.stopPropagation();
var pw = document.querySelector('.player-wrapper');
if (pw) pw.scrollIntoView({ behavior: 'smooth', block: 'center' });
});
miniPlayerEl.querySelector('.nml-mini-pause-btn').addEventListener('click', function (e) {
e.stopPropagation();
var ov = findVideo() || document.querySelector('.custom-video-player video');
if (!ov) return;
if (ov.paused) { ov.play(); miniVideoEl.play(); }
else { ov.pause(); miniVideoEl.pause(); }
});
miniVideoEl = document.createElement('video');
miniVideoEl.playsInline = true;
miniVideoEl.setAttribute('playsinline', '');
miniVideoEl.style.pointerEvents = 'none';
miniPlayerEl.appendChild(miniVideoEl);
document.body.appendChild(miniPlayerEl);
if (!document.getElementById('nml-mini-style')) {
var ms = document.createElement('style');
ms.id = 'nml-mini-style';
ms.textContent =
'.nml-mini-player{' +
'display:none;position:fixed;bottom:20px;right:20px;z-index:9999;' +
'width:300px;height:169px;background:#000;border-radius:10px;overflow:visible;' +
'box-shadow:0 4px 24px rgba(0,0,0,.5);' +
'min-width:160px;min-height:90px;max-width:80vw;max-height:80vh;' +
'}' +
'.nml-mini-player video{' +
'width:100%;height:100%;object-fit:contain;pointer-events:none;' +
'}' +
'.nml-mini-close,.nml-mini-back{' +
'position:absolute;top:4px;z-index:20;width:26px;height:26px;' +
'border:none;background:rgba(0,0,0,.55);color:#fff;border-radius:50%;' +
'cursor:pointer;font-size:15px;line-height:26px;text-align:center;padding:0;' +
'opacity:0;transition:opacity .2s;' +
'}' +
'.nml-mini-close{right:4px;}' +
'.nml-mini-back{right:34px;font-size:14px;}' +
'.nml-mini-player:hover .nml-mini-close,.nml-mini-player:hover .nml-mini-back{opacity:1;}' +
'.nml-mini-close:hover,.nml-mini-back:hover{background:rgba(0,0,0,.8);}' +
'.nml-mini-pause-bg{' +
'position:absolute;inset:0;z-index:3;' +
'background:rgba(0,0,0,.35);opacity:0;transition:opacity .2s;' +
'pointer-events:none;' +
'}' +
'.nml-mini-player.paused .nml-mini-pause-bg,.nml-mini-player:hover .nml-mini-pause-bg{opacity:1;}' +
'.nml-mini-pause-btn{' +
'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:15;' +
'width:44px;height:44px;border:none;border-radius:50%;' +
'background:rgba(255,255,255,.15);cursor:pointer;' +
'display:flex;align-items:center;justify-content:center;' +
'opacity:0;transition:opacity .2s;padding:0;' +
'}' +
'.nml-mini-player.paused .nml-mini-pause-btn,.nml-mini-player:hover .nml-mini-pause-btn{opacity:1;}' +
'.nml-mini-pause-btn:hover{background:rgba(255,255,255,.3);}' +
'.nml-mini-player.paused .nml-mini-pause-icon{display:block;}' +
'.nml-mini-player.paused .nml-mini-play-icon{display:none;}' +
'.nml-mini-player:not(.paused) .nml-mini-pause-icon{display:none;}' +
'.nml-mini-player:not(.paused) .nml-mini-play-icon{display:block;}' +
'@media(max-width:768px){.nml-mini-player{width:200px;height:113px;bottom:12px;right:8px;}}';
document.head.appendChild(ms);
}
var actionInfo = null;
miniPlayerEl.addEventListener('mousedown', function (e) {
if (e.target.closest('.nml-mini-close,.nml-mini-back,.nml-mini-pause-btn')) return;
var rect = miniPlayerEl.getBoundingClientRect();
var l = e.clientX - rect.left < EDGE, r = rect.right - e.clientX < EDGE,
t = e.clientY - rect.top < EDGE, b = rect.bottom - e.clientY < EDGE;
var dir = '';
if (l) dir += 'L'; else if (r) dir += 'R';
if (t) dir += 'T'; else if (b) dir += 'B';
// Fix to absolute coords
miniPlayerEl.style.right = 'auto';
miniPlayerEl.style.bottom = 'auto';
miniPlayerEl.style.left = rect.left + 'px';
miniPlayerEl.style.top = rect.top + 'px';
if (dir) {
miniPlayerEl.style.width = rect.width + 'px';
miniPlayerEl.style.height = rect.height + 'px';
actionInfo = {
type: 'resize', dir: dir,
sx: e.clientX, sy: e.clientY,
sl: rect.left, st: rect.top, sw: rect.width, sh: rect.height
};
} else {
actionInfo = {
type: 'drag',
sx: e.clientX, sy: e.clientY,
sl: rect.left, st: rect.top
};
}
e.preventDefault();
});
// Hover cursor
miniPlayerEl.addEventListener('mousemove', function (e) {
if (actionInfo) return;
var rect = miniPlayerEl.getBoundingClientRect();
var l = e.clientX - rect.left < EDGE, r = rect.right - e.clientX < EDGE,
t = e.clientY - rect.top < EDGE, b = rect.bottom - e.clientY < EDGE;
if ((l && t) || (r && b)) miniPlayerEl.style.cursor = 'nwse-resize';
else if ((r && t) || (l && b)) miniPlayerEl.style.cursor = 'nesw-resize';
else if (l || r) miniPlayerEl.style.cursor = 'ew-resize';
else if (t || b) miniPlayerEl.style.cursor = 'ns-resize';
else miniPlayerEl.style.cursor = 'move';
});
document.addEventListener('mousemove', function (e) {
if (!actionInfo) return;
var a = actionInfo;
if (a.type === 'drag') {
miniPlayerEl.style.left = (a.sl + e.clientX - a.sx) + 'px';
miniPlayerEl.style.top = (a.st + e.clientY - a.sy) + 'px';
} else {
var RATIO = 16 / 9;
var dx = e.clientX - a.sx, dy = e.clientY - a.sy;
var nw = a.sw, nh = a.sh, nl = a.sl, nt = a.st;
var hasH = a.dir.indexOf('L') !== -1 || a.dir.indexOf('R') !== -1;
var hasV = a.dir.indexOf('T') !== -1 || a.dir.indexOf('B') !== -1;
if (hasH && !hasV) {
// Horizontal edge — width drives, height follows ratio
if (a.dir.indexOf('R') !== -1) nw = a.sw + dx;
else nw = a.sw - dx;
nw = Math.max(160, Math.min(nw, window.innerWidth * 0.8));
nh = nw / RATIO;
if (a.dir.indexOf('L') !== -1) nl = a.sl + a.sw - nw;
} else if (hasV && !hasH) {
// Vertical edge — height drives, width follows ratio
if (a.dir.indexOf('B') !== -1) nh = a.sh + dy;
else nh = a.sh - dy;
nh = Math.max(90, Math.min(nh, window.innerHeight * 0.8));
nw = nh * RATIO;
if (a.dir.indexOf('T') !== -1) nt = a.st + a.sh - nh;
} else {
// Corner — use the larger delta direction
if (Math.abs(dx) > Math.abs(dy)) {
if (a.dir.indexOf('R') !== -1) nw = a.sw + dx;
else nw = a.sw - dx;
nw = Math.max(160, Math.min(nw, window.innerWidth * 0.8));
nh = nw / RATIO;
if (a.dir.indexOf('L') !== -1) nl = a.sl + a.sw - nw;
} else {
if (a.dir.indexOf('B') !== -1) nh = a.sh + dy;
else nh = a.sh - dy;
nh = Math.max(90, Math.min(nh, window.innerHeight * 0.8));
nw = nh * RATIO;
if (a.dir.indexOf('T') !== -1) nt = a.st + a.sh - nh;
}
}
miniPlayerEl.style.width = nw + 'px';
miniPlayerEl.style.height = nh + 'px';
miniPlayerEl.style.left = nl + 'px';
miniPlayerEl.style.top = nt + 'px';
}
});
document.addEventListener('mouseup', function () { actionInfo = null; });
// Update pause/play icon state
miniVideoEl.addEventListener('play', function () { miniPlayerEl.classList.remove('paused'); });
miniVideoEl.addEventListener('pause', function () { miniPlayerEl.classList.add('paused'); });
}
function showMiniPlayer() {
if (window.innerWidth <= 768) return;
var v = findVideo() || document.querySelector('.custom-video-player video');
if (!v || v.paused || v.ended) return;
buildMiniPlayer();
if (!miniVisible) {
// Reset position to default
miniPlayerEl.style.left = '';
miniPlayerEl.style.top = '';
miniPlayerEl.style.right = '20px';
miniPlayerEl.style.bottom = '20px';
}
var src = v.src || v.currentSrc;
if (!src && v.querySelector('source')) {
src = v.querySelector('source').src;
}
if (miniVideoEl.src !== src) {
miniVideoEl.src = src;
}
miniVideoEl.currentTime = v.currentTime;
miniVideoEl.playbackRate = v.playbackRate;
miniVideoEl.volume = v.volume;
miniVideoEl.muted = v.muted;
var p = miniVideoEl.play();
if (p && p.then) {
p.then(function () {
miniPlayerEl.style.display = 'block';
miniVisible = true;
}).catch(function () {
// autoplay blocked, show anyway
miniPlayerEl.style.display = 'block';
miniVisible = true;
});
} else {
miniPlayerEl.style.display = 'block';
miniVisible = true;
}
// Sync loop
if (!miniSyncTimer) {
miniSyncTimer = setInterval(function () {
var ov = findVideo() || document.querySelector('.custom-video-player video');
if (!ov || !miniVisible) return;
if (Math.abs(miniVideoEl.currentTime - ov.currentTime) > 0.8) {
miniVideoEl.currentTime = ov.currentTime;
}
if (ov.paused && !miniVideoEl.paused) miniVideoEl.pause();
else if (!ov.paused && miniVideoEl.paused) miniVideoEl.play().catch(function () {});
if (ov.playbackRate !== miniVideoEl.playbackRate) miniVideoEl.playbackRate = ov.playbackRate;
}, 500);
}
}
function hideMiniPlayer() {
if (miniPlayerEl) miniPlayerEl.style.display = 'none';
miniVisible = false;
if (miniVideoEl) miniVideoEl.pause();
if (miniSyncTimer) {
clearInterval(miniSyncTimer);
miniSyncTimer = null;
}
}
function setupMiniObserver() {
if (miniObserver) miniObserver.disconnect();
var pw = document.querySelector('.player-wrapper') || document.querySelector('.custom-video-player');
if (!pw) { setTimeout(setupMiniObserver, 1000); return; }
miniObserver = new IntersectionObserver(function (entries) {
var entry = entries[0];
if (!entry) return;
var v = findVideo() || document.querySelector('.custom-video-player video');
if (entry.isIntersecting || !v || v.paused || v.ended) {
hideMiniPlayer();
} else {
showMiniPlayer();
}
}, { threshold: 0.3 });
miniObserver.observe(pw);
}
function nmlCleanupMiniPlayer() {
hideMiniPlayer();
if (miniObserver) { miniObserver.disconnect(); miniObserver = null; }
if (miniPlayerEl && miniPlayerEl.parentNode) { miniPlayerEl.remove(); miniPlayerEl = null; miniVideoEl = null; }
_miniSetupTries = 0;
if (_miniSetupTimer) { clearInterval(_miniSetupTimer); _miniSetupTimer = null; }
startMiniSetup();
}
function startMiniSetup() {
if (_miniSetupTimer) return;
_miniSetupTimer = setInterval(function () {
if (location.pathname.indexOf('/video/') === 0) {
if (document.querySelector('.custom-video-player video')) {
setupMiniObserver();
clearInterval(_miniSetupTimer);
_miniSetupTimer = null;
} else if (_miniSetupTries++ > 60) {
clearInterval(_miniSetupTimer);
_miniSetupTimer = null;
}
}
}, 500);
}
var _miniSetupTries = 0;
var _miniSetupTimer = null;
startMiniSetup();
// KaTeX math rendering
var _katexReady = false;
var _katexQueue = [];
function onKaTeXReady(fn) {
if (_katexReady) { fn(); return; }
_katexQueue.push(fn);
}
(function loadKaTeX() {
if (document.getElementById('nml-katex-css')) return;
var link = document.createElement('link');
link.id = 'nml-katex-css';
link.rel = 'stylesheet';
link.href = 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css';
document.head.appendChild(link);
var script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js';
script.onload = function () {
_katexReady = true;
var q = _katexQueue;
_katexQueue = [];
q.forEach(function (fn) { fn(); });
document.querySelectorAll('.comment-text[data-nml-md]').forEach(function (el) {
if (el._nmlHasMath) { el._nmlMdRendered = false; renderCommentText(el); }
});
};
document.head.appendChild(script);
})();
function renderMath(formula, displayMode) {
if (typeof katex === 'undefined') {
return displayMode ? '$$' + formula + '$$' : '$' + formula + '$';
}
try {
return katex.renderToString(formula, { displayMode: displayMode, throwOnError: false });
} catch (e) {
return '' + formula.replace(/&/g, '&').replace(//g, '>') + '';
}
}
function renderMarkdown(text) {
if (!text) return '';
var html = text;
html = html.replace(/&/g, '&').replace(//g, '>');
html = html.replace(/```(\w*)\n([\s\S]*?)```/g, function (m, lang, code) {
return '' + code.replace(/\n$/, '') + '
';
});
html = html.replace(/`([^`\n]+)`/g, '$1');
var mathBlocks = [];
html = html.replace(/\$\$([\s\S]*?)\$\$/g, function (m, formula) {
mathBlocks.push({ display: true, formula: formula.trim() });
return 'M' + (mathBlocks.length - 1) + '';
});
html = html.replace(/\$(?=[^0-9\s])([^\$\n]+?)\$/g, function (m, formula) {
mathBlocks.push({ display: false, formula: formula.trim() });
return 'M' + (mathBlocks.length - 1) + '';
});
html = html.replace(/!\[([^\]]*)\]\(([^)\s]+(?:\s+"[^"]*")?)\)/g, '
');
html = html.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, '$1');
html = html.replace(/\*\*([^*\n]+)\*\*/g, '$1');
html = html.replace(/__([^_\n]+)__/g, '$1');
html = html.replace(/\*([^*\n]+)\*/g, '$1');
html = html.replace(/_([^_\n]+)_/g, '$1');
html = html.replace(/~~([^~\n]+)~~/g, '$1');
var lines = html.split('\n');
var result = [];
var inList = false, listType = '';
var inQuote = false;
var i = 0;
while (i < lines.length) {
var line = lines[i];
if (/^\s*$/.test(line)) {
if (inQuote) { result.push(''); inQuote = false; }
if (inList) { result.push(listType === 'ul' ? '' : ''); inList = false; listType = ''; }
i++;
continue;
}
if (/^```/.test(line)) {
i++;
while (i < lines.length && !/^```/.test(lines[i])) i++;
i++;
continue;
}
var hMatch = line.match(/^(#{1,6})\s+(.+)/);
if (hMatch) {
if (inQuote) { result.push(''); inQuote = false; }
if (inList) { result.push(listType === 'ul' ? '' : ''); inList = false; listType = ''; }
var level = hMatch[1].length;
result.push('' + hMatch[2] + '');
i++;
continue;
}
var qMatch = line.match(/^>\s?(.*)/);
if (qMatch) {
if (inList) { result.push(listType === 'ul' ? '' : ''); inList = false; listType = ''; }
if (!inQuote) { result.push(''); inQuote = true; }
result.push('' + qMatch[1] + '
');
i++;
continue;
} else if (inQuote && !/^>/.test(line) && !/^\s*$/.test(line)) {
// Continue quote on non-empty non-quote line
result.push('' + line + '
');
i++;
continue;
} else if (inQuote) {
result.push('
');
inQuote = false;
}
// Unordered list
var ulMatch = line.match(/^[\-\*]\s(.+)/);
if (ulMatch) {
if (!inList || listType !== 'ul') {
if (inList) result.push(listType === 'ul' ? '' : '');
result.push('');
inList = true; listType = 'ul';
}
result.push('- ' + ulMatch[1] + '
');
i++;
continue;
}
// Ordered list
var olMatch = line.match(/^\d+\.\s(.+)/);
if (olMatch) {
if (!inList || listType !== 'ol') {
if (inList) result.push(listType === 'ul' ? '
' : '');
result.push('');
inList = true; listType = 'ol';
}
result.push('- ' + olMatch[1] + '
');
i++;
continue;
} else if (inList) {
result.push(listType === 'ul' ? '' : '
');
inList = false; listType = '';
}
// Horizontal rule
if (/^[-*_]{3,}\s*$/.test(line)) {
result.push('
');
i++;
continue;
}
// Regular paragraph
result.push('' + line + '
');
i++;
}
// Close any remaining open blocks
if (inQuote) result.push('');
if (inList) result.push(listType === 'ul' ? '' : '');
var finalHtml = result.join('\n');
// Restore math blocks
finalHtml = finalHtml.replace(/M(\d+)/g, function (m, i) {
var b = mathBlocks[parseInt(i)];
if (!b) return m;
var formula = b.formula.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
return renderMath(formula, b.display);
});
return finalHtml;
}
function renderCommentText(el) {
if (!el || el._nmlMdRendered) return;
el._nmlMdRendered = true;
var raw = el.textContent || '';
if (/\$/.test(raw)) el._nmlHasMath = true;
if (!/[\*\_\[\]`#>~\-$\\]/.test(raw)) return;
var html = renderMarkdown(raw);
el.innerHTML = html;
el.querySelectorAll('a').forEach(function (a) {
a.setAttribute('target', '_blank');
a.setAttribute('rel', 'noopener noreferrer');
});
}
function renderAllComments() {
document.querySelectorAll('.comment-text:not([data-nml-md])').forEach(function (el) {
el.setAttribute('data-nml-md', '1');
renderCommentText(el);
});
document.querySelectorAll('.reply-list .comment-text:not([data-nml-md])').forEach(function (el) {
el.setAttribute('data-nml-md', '1');
renderCommentText(el);
});
}
var mdCommentObserver = new MutationObserver(function () {
renderAllComments();
});
function hookMdRenderer() {
mdCommentObserver.disconnect();
var list = document.querySelector('.comments-list');
if (list) {
mdCommentObserver.observe(list, { childList: true, subtree: true });
}
renderAllComments();
// Also render replies
var replyLists = document.querySelectorAll('.reply-list');
replyLists.forEach(function (rl) {
mdCommentObserver.observe(rl, { childList: true, subtree: true });
});
}
// Retry hook for SPA lazy-load
var _mdRetries = 0;
var _mdInterval = setInterval(function () {
if (document.querySelector('.comments-section')) {
hookMdRenderer();
clearInterval(_mdInterval);
} else if (_mdRetries++ > 60) {
clearInterval(_mdInterval);
}
}, 500);
// Inject MD toolbar & renderer styles
(function () {
var s = document.createElement('style');
s.id = 'nml-md-styles';
s.textContent =
/* Rendered MD styles */
'.comment-text[data-nml-md] p{margin:0 0 .5em;}' +
'.comment-text[data-nml-md] p:last-child{margin-bottom:0;}' +
'.comment-text[data-nml-md] strong{font-weight:600;}' +
'.comment-text[data-nml-md] em{font-style:italic;}' +
'.comment-text[data-nml-md] del{text-decoration:line-through;opacity:.7;}' +
'.comment-text[data-nml-md] code{' +
'background:var(--graph_bg_regular,rgba(0,0,0,.06));padding:1px 4px;border-radius:3px;' +
'font-family:Consolas,Monaco,monospace;font-size:.9em;' +
'}' +
'.comment-text[data-nml-md] pre{background:var(--graph_bg_thin,rgba(0,0,0,.04));padding:8px 12px;' +
'border-radius:6px;overflow-x:auto;margin:.5em 0;' +
'}' +
'.comment-text[data-nml-md] pre code{background:none;padding:0;}' +
'.comment-text[data-nml-md] blockquote{' +
'border-left:3px solid var(--brand_pink,#f69);padding:2px 0 2px 12px;margin:.5em 0;' +
'color:var(--text2,#666);' +
'}' +
'.comment-text[data-nml-md] ul,.comment-text[data-nml-md] ol{padding-left:1.5em;margin:.3em 0;}' +
'.comment-text[data-nml-md] li{margin:2px 0;}' +
'.comment-text[data-nml-md] h1,.comment-text[data-nml-md] h2,.comment-text[data-nml-md] h3,' +
'.comment-text[data-nml-md] h4,.comment-text[data-nml-md] h5,.comment-text[data-nml-md] h6{' +
'font-weight:600;line-height:1.3;margin:.6em 0 .3em;' +
'}' +
'.comment-text[data-nml-md] h1{font-size:1.4em;}.comment-text[data-nml-md] h2{font-size:1.25em;}' +
'.comment-text[data-nml-md] h3{font-size:1.1em;}' +
'.comment-text[data-nml-md] hr{border:none;border-top:1px solid var(--line_regular,#e5e5e5);margin:.5em 0;}' +
'.comment-text[data-nml-md] img{max-width:100%;border-radius:6px;margin:.3em 0;}' +
'.comment-text[data-nml-md] a{color:var(--text_link,#008ac5);text-decoration:none;}' +
'.comment-text[data-nml-md] a:hover{text-decoration:underline;}' +
/* KaTeX math styles */
'.comment-text .katex-display{margin:.5em 0;overflow-x:auto;overflow-y:hidden;}' +
'.comment-text .katex-display>.katex{max-width:100%;}' +
'.comment-text .katex{font-size:1.05em;}' +
'.nml-math-error{color:#ff4444;background:rgba(255,0,0,0.08);padding:1px 5px;border-radius:3px;font-family:Consolas,Monaco,monospace;font-size:.9em;}';
document.head.appendChild(s);
})();
var _creatorPageSizes = [10, 20, 50, 100];
var _creatorSizeSelect = null;
function buildCreatorSizeSelect(currentSize) {
if (_creatorSizeSelect) return _creatorSizeSelect;
var sel = document.createElement('select');
sel.className = 'nml-page-size-select';
sel.style.cssText = 'margin-left:12px;padding:4px 8px;border:1px solid var(--line_regular,#ddd);border-radius:6px;font-size:13px;color:var(--text1);background:var(--bg1);cursor:pointer;height:32px;';
_creatorPageSizes.forEach(function (s) {
var opt = document.createElement('option');
opt.value = s;
opt.textContent = s + ' 条/页';
if (s === currentSize) opt.selected = true;
sel.appendChild(opt);
});
sel.addEventListener('change', function () {
var newSize = parseInt(sel.value);
try { localStorage.setItem('nml_creator_page_size', String(newSize)); } catch (e) {}
var app = document.getElementById('__nuxt');
if (app && app.__vue_app__) {
walkVue(app.__vue_app__._instance?.vnode, function (comp) {
if (comp.setupState && 'd' in comp.setupState && comp.setupState.i) {
comp.setupState.d.value = newSize;
comp.setupState.i.value = 1;
}
});
}
});
_creatorSizeSelect = sel;
return sel;
}
function injectCreatorPageSize() {
var pagination = document.querySelector('.creator-container .pagination, .el-pagination');
if (!pagination) return;
if (pagination.parentNode.querySelector('.nml-page-size-select')) return;
var currentSize = 10;
try {
var saved = localStorage.getItem('nml_creator_page_size');
if (saved) currentSize = parseInt(saved) || 10;
} catch (e) {}
var sel = buildCreatorSizeSelect(currentSize);
pagination.parentNode.insertBefore(sel, pagination.nextSibling);
if (currentSize !== 10) {
setTimeout(function () {
var app = document.getElementById('__nuxt');
if (app && app.__vue_app__) {
walkVue(app.__vue_app__._instance?.vnode, function (comp) {
if (comp.setupState && 'd' in comp.setupState && comp.setupState.i) {
comp.setupState.d.value = currentSize;
comp.setupState.i.value = 1;
}
});
}
}, 500);
}
}
// Watch for creator page
var _creatorCheckInterval = setInterval(function () {
if (location.pathname === '/creator' || location.pathname.startsWith('/creator')) {
injectCreatorPageSize();
}
}, 800);
let danmakuMode = 0;
let advSX = 0, advSY = 50, advMX = 50, advMY = 50, advEX = 100, advEY = 50;
let advSR = 0, advMR = 0, advER = 0;
let advEasing1 = 0, advEasing2 = 0;
let advTimeSM = 25, advTimeME = 25;
var ZW = ['','','','','','','','','','',''];
var EASINGS = ['linear','ease','ease-in','ease-out','ease-in-out',
'cubic-bezier(.68,-.55,.27,1.55)','cubic-bezier(.36,0,.66,-.56)',
'cubic-bezier(.34,1.56,.64,1)','steps(3,end)','cubic-bezier(.45,0,.55,1)',
'cubic-bezier(.22,.61,.36,1)'];
var EASING_NAMES = ['直线','缓动','渐入','渐出','渐入出','回弹','弹力','弹入','阶梯','平滑','标准'];
function zwVal(ch) { var i = ZW.indexOf(ch); return i >= 0 ? i : 0; }
function zwChar(v) { return ZW[v] || ZW[0]; }
function b11(a,b) { return a*11 + b; }
function encodeContent(text, mode) {
if (mode === 0 || !text) return text;
if (mode >= 1 && mode <= 3) return ZW[mode - 1] + text;
if (mode === 4) {
var p = [advSX, advSY, advMX, advMY, advEX, advEY,
Math.round(advSR / 3), Math.round(advMR / 3), Math.round(advER / 3),
advEasing1, advEasing2, advTimeSM, advTimeME];
var s = ZW[10];
p.forEach(function (v) {
v = Math.round(Math.max(0, Math.min(120, v)));
s += zwChar(Math.floor(v / 11)) + zwChar(v % 11);
});
return s + text;
}
return text;
}
function decodeContent(el) {
var text = el.textContent || '';
if (!text) return { mode: 0 };
var c0 = text.charCodeAt(0);
if (c0 === 0x200B) return { mode: 1 };
if (c0 === 0x200C) return { mode: 2 };
if (c0 === 0x200D) return { mode: 3 };
if (c0 === 0x2064 && text.length >= 27) {
var ch = text, i = 1;
var sx = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var sy = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var mx = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var my = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var ex = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var ey = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var sr = b11(zwVal(ch[i]), zwVal(ch[i+1])) * 3; i += 2;
var mr = b11(zwVal(ch[i]), zwVal(ch[i+1])) * 3; i += 2;
var er = b11(zwVal(ch[i]), zwVal(ch[i+1])) * 3; i += 2;
var e1 = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var e2 = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var ts = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var te = b11(zwVal(ch[i]), zwVal(ch[i+1]));
return { mode: 4, sx: sx, sy: sy, mx: mx, my: my, ex: ex, ey: ey,
sr: sr, mr: mr, er: er,
easing1: Math.min(e1, 10), easing2: Math.min(e2, 10),
timeSM: Math.min(ts, 100), timeME: Math.min(te, 100) };
}
if (c0 === 0x2064 && text.length >= 15) {
var ch = text, i = 1;
var sx = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var sy = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var ex = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var ey = b11(zwVal(ch[i]), zwVal(ch[i+1])); i += 2;
var sr = b11(zwVal(ch[i]), zwVal(ch[i+1])) * 3; i += 2;
var er = b11(zwVal(ch[i]), zwVal(ch[i+1])) * 3; i += 2;
var ease = b11(zwVal(ch[i]), zwVal(ch[i+1]));
var avgX = Math.round((sx + ex) / 2);
var avgY = Math.round((sy + ey) / 2);
var avgR = Math.round((sr + er) / 2);
return { mode: 4, sx: sx, sy: sy, mx: avgX, my: avgY, ex: ex, ey: ey,
sr: sr, mr: avgR, er: er,
easing1: Math.min(ease, 10), easing2: Math.min(ease, 10),
timeSM: 25, timeME: 25 };
}
if ((c0 === 0xFEFF || c0 === 0x2064) && text.length >= 6) {
var x = b11(zwVal(text[1]), zwVal(text[2]));
var y = b11(zwVal(text[3]), zwVal(text[4]));
var r = zwVal(text[5]) * 33;
return { mode: 4, sx: x, sy: y, mx: x, my: y, ex: x, ey: y,
sr: r, mr: r, er: r,
easing1: 0, easing2: 0, timeSM: 25, timeME: 25 };
}
return { mode: 0 };
}
function injectContentMarker() {
if (danmakuMode === 0) return;
var input = document.querySelector('.danmaku-input');
if (!input) return;
var text = input.value;
if (!text) return;
var first = text.charCodeAt(0);
if (first >= 0x200B && first <= 0x2064 && first !== 0x20 && first !== 0x0A) return;
var encoded = encodeContent(text, danmakuMode);
console.log('[nml-dm] SEND mode=' + danmakuMode +
(danmakuMode === 4 ? ' sx=' + advSX + ' sy=' + advSY + ' mx=' + advMX + ' my=' + advMY +
' ex=' + advEX + ' ey=' + advEY + ' t1=' + (advTimeSM/10).toFixed(1) + 's t2=' + (advTimeME/10).toFixed(1) + 's' : '') +
' text=' + text.slice(0, 15));
input.value = encoded;
input.dispatchEvent(new Event('input', { bubbles: true }));
setTimeout(function () {
input.value = text;
input.dispatchEvent(new Event('input', { bubbles: true }));
}, 60);
}
document.addEventListener('click', function (e) {
if (e.target.closest('.send-btn')) injectContentMarker();
}, true);
document.addEventListener('keydown', function (e) {
if (e.key === 'Enter' && e.target.classList.contains('danmaku-input')) injectContentMarker();
}, true);
var _advDmId = 0;
(function () {
var kf = document.createElement('style');
kf.id = 'nml-dm-keyframes';
kf.textContent = '@keyframes nml-reverse-dm{from{left:-50%;}to{left:110%;}}';
document.head.appendChild(kf);
})();
var activeDmLanes = { 1: [], 2: [], 3: [] };
var DM_LANE_TOPS = {
1: [2,7,12,17,22,27,32,37,42],
2: [68,73,78,83,88,93],
3: [8,13,18,23,28,33,38,43,48,53,58,63,68,73,78,83,88]
};
function assignDmLane(mode, clone) {
var lanes = activeDmLanes[mode];
var tops = DM_LANE_TOPS[mode];
// Purge clones that have been removed from DOM
for (var i = lanes.length - 1; i >= 0; i--) {
if (!lanes[i].parentNode) lanes.splice(i, 1);
}
// Find first free top value
var used = {};
for (var j = 0; j < lanes.length; j++) used[lanes[j]._nmlLaneTop] = true;
var freeTop = null;
for (var k = 0; k < tops.length; k++) {
if (!used[tops[k]]) { freeTop = tops[k]; break; }
}
if (freeTop == null) {
// All occupied — evict oldest
var oldest = lanes.shift();
if (oldest.parentNode) oldest.remove();
freeTop = oldest._nmlLaneTop;
}
clone._nmlLaneTop = freeTop;
lanes.push(clone);
return freeTop;
}
function applyDanmakuStyle(el, data) {
var text = el.textContent || '';
var first = text.charCodeAt(0);
var stripLen = 0;
if (first === 0x200B || first === 0x200C || first === 0x200D) stripLen = 1;
else if (first === 0x2064 || first === 0xFEFF) stripLen = text.length >= 27 ? 27 : (text.length >= 15 ? 15 : (text.length >= 6 ? 6 : 0));
if (stripLen > 0) { text = text.slice(stripLen); el.textContent = text; }
var clone = el.cloneNode(true);
var fs = el.style.fontSize || '24px';
var col = el.style.color || '#fff';
var base = 'position:absolute!important;z-index:999!important;' +
'white-space:nowrap!important;pointer-events:none!important;' +
'font-size:' + fs + '!important;color:' + col + '!important;' +
'text-shadow:1px 0 2px #000,-1px 0 2px #000,0 1px 2px #000,0 -1px 2px #000;';
var player = document.querySelector('.player-wrapper');
var video = document.querySelector('.custom-video-player video');
if (data.mode === 1 || data.mode === 2) {
var laneTop = assignDmLane(data.mode, clone);
clone.style.cssText = base +
'left:50%!important;top:' + laneTop + '%!important;' +
'transform:translateX(-50%)!important;';
} else if (data.mode === 3) {
var laneTop3 = assignDmLane(3, clone);
var ss = document.querySelector('.danmaku-input-wrapper input[type=range]');
var dur = 720 / (parseFloat(ss?.value) || 120);
clone.style.cssText = base +
'top:' + laneTop3 + '%!important;animation:nml-reverse-dm ' + dur + 's linear forwards!important;';
} else if (data.mode === 4) {
var t1 = (data.timeSM || 25) / 10;
var t2 = (data.timeME || 25) / 10;
var totalMs = (t1 + t2) * 1000;
var midPct = t1 / (t1 + t2);
var mx = data.mx != null ? data.mx : (data.sx + data.ex) / 2;
var my = data.my != null ? data.my : (data.sy + data.ey) / 2;
var mr = data.mr != null ? data.mr : (data.sr + data.er) / 2;
var e1 = EASINGS[data.easing1 != null ? data.easing1 : (data.easing || 0)] || 'linear';
var e2 = EASINGS[data.easing2 != null ? data.easing2 : (data.easing || 0)] || 'linear';
clone.style.cssText = base;
try {
var anim = clone.animate([
{ left: data.sx + '%', top: data.sy + '%', transform: 'translate(-50%,-50%) rotate(' + data.sr + 'deg)', offset: 0 },
{ left: mx + '%', top: my + '%', transform: 'translate(-50%,-50%) rotate(' + mr + 'deg)', offset: midPct, easing: e1 },
{ left: data.ex + '%', top: data.ey + '%', transform: 'translate(-50%,-50%) rotate(' + data.er + 'deg)', offset: 1, easing: e2 }
], { duration: totalMs, fill: 'forwards' });
if (video) {
var syncFn = function sync() {
if (!clone.parentNode) return;
try {
if (video.paused) { if (anim.playState === 'running') anim.pause(); }
else if (anim.playState === 'paused') anim.play();
} catch (e) {}
requestAnimationFrame(sync);
};
requestAnimationFrame(syncFn);
}
anim.onfinish = function () {
clone.style.transition = 'opacity 0.3s';
clone.style.opacity = '0';
setTimeout(function () { if (clone.parentNode) clone.remove(); if (el.parentNode) el.remove(); }, 300);
};
} catch (e) {
el.style.display = '';
}
}
if (player) player.appendChild(clone);
el.style.display = 'none';
if (data.mode === 1 || data.mode === 2) {
if (video) {
var startTime = video.currentTime;
(function tick() {
if (!clone.parentNode) return;
if (video.paused) { startTime = video.currentTime; requestAnimationFrame(tick); return; }
if (video.currentTime - startTime >= 4) {
clone.style.transition = 'opacity 0.6s';
clone.style.opacity = '0';
setTimeout(function () { if (clone.parentNode) clone.remove(); if (el.parentNode) el.remove(); }, 600);
return;
}
requestAnimationFrame(tick);
})();
}
} else if (data.mode === 3) {
if (video) {
(function sync() {
if (!clone.parentNode) return;
clone.style.setProperty('animation-play-state', video.paused ? 'paused' : 'running', 'important');
requestAnimationFrame(sync);
})();
}
clone.addEventListener('animationend', function () {
clone.style.transition = 'opacity 0.3s';
clone.style.opacity = '0';
setTimeout(function () { if (clone.parentNode) clone.remove(); if (el.parentNode) el.remove(); }, 300);
});
}
}
var danmakuObserveCount = 0;
var danmakuLayerObserver = new MutationObserver(function (mutations) {
mutations.forEach(function (m) {
m.addedNodes.forEach(function (node) {
if (node.nodeType !== 1 || !node.style) return;
var data = decodeContent(node);
danmakuObserveCount++;
var visibleText = node.textContent || '';
var first = visibleText.charCodeAt(0);
if (first === 0x200B || first === 0x200C || first === 0x200D) visibleText = visibleText.slice(1);
else if (first === 0x2064 || first === 0xFEFF) visibleText = visibleText.slice(visibleText.length >= 27 ? 27 : (visibleText.length >= 15 ? 15 : 6));
console.log('[nml-dm] OBSERVE #' + danmakuObserveCount + ' mode=' + data.mode +
(data.mode === 4 ? ' sx=' + data.sx + ' sy=' + data.sy + ' mx=' + (data.mx||'?') + ' my=' + (data.my||'?') +
' ex=' + data.ex + ' ey=' + data.ey : '') +
' text=' + visibleText.slice(0, 25));
addDanmakuEntry(data, visibleText);
if (data.mode > 0) applyDanmakuStyle(node, data);
});
});
});
function hookDanmakuLayer() {
danmakuLayerObserver.disconnect();
var layer = null;
var candidates = document.querySelectorAll('.player-wrapper > div, .player-wrapper > * > div');
for (var i = 0; i < candidates.length; i++) {
try {
var cs = getComputedStyle(candidates[i]);
if (cs.pointerEvents === 'none' && (cs.position === 'absolute' || cs.position === 'fixed')) {
if (candidates[i].childElementCount > 0 || candidates[i].offsetHeight > 100) {
layer = candidates[i];
break;
}
}
} catch (e) {}
}
if (!layer) {
var dmEls = document.querySelectorAll('[class*=danmaku]');
for (var j = 0; j < dmEls.length; j++) {
if (!dmEls[j].classList.contains('danmaku-input-wrapper') && !dmEls[j].closest('.danmaku-input-wrapper')) {
layer = dmEls[j];
break;
}
}
}
if (!layer) layer = document.querySelector('.player-wrapper');
if (layer) {
danmakuLayerObserver.observe(layer, { childList: true, subtree: true });
} else {
setTimeout(hookDanmakuLayer, 1000);
}
}
var dmList = [];
var dmListPanel = null;
var dmListExpanded = false;
var MODE_LABELS = ['','顶部','底部','逆向','高级'];
var MODE_CLASS = ['','nml-dm-tag-top','nml-dm-tag-bottom','nml-dm-tag-rev','nml-dm-tag-adv'];
function addDanmakuEntry(data, text) {
var video = document.querySelector('.custom-video-player video');
dmList.push({
time: video ? video.currentTime : 0,
mode: data.mode,
text: text,
params: data.mode === 4 ? { sx: data.sx, sy: data.sy, mx: data.mx, my: data.my, ex: data.ex, ey: data.ey, sr: data.sr, mr: data.mr, er: data.er, easing1: data.easing1, easing2: data.easing2, timeSM: data.timeSM, timeME: data.timeME } : null
});
updateDanmakuListCount();
if (dmListPanel && dmListExpanded) renderDanmakuList();
}
function renderDanmakuList() {
if (!dmListPanel) return;
var body = dmListPanel.querySelector('.nml-dm-list-body');
if (!body || !dmListExpanded) return;
var html = '';
for (var i = dmList.length - 1; i >= 0; i--) {
var d = dmList[i];
var t = formatTime(d.time);
var tagCls = MODE_CLASS[d.mode] || '';
var tagLabel = MODE_LABELS[d.mode] || '滚动';
var textEscaped = escapeHtml(d.text.length > 30 ? d.text.slice(0, 30) + '...' : d.text);
html += '' +
'
' + t + '' +
'
' + tagLabel + '' +
'
' + textEscaped + '';
if (d.params) {
html += '
起点(' + d.params.sx + '%,' + d.params.sy + '%) ' +
'→中间(' + (d.params.mx != null ? d.params.mx : '?') + '%,' + (d.params.my != null ? d.params.my : '?') + '%) ' +
'→终点(' + d.params.ex + '%,' + d.params.ey + '%) ' +
'旋转' + d.params.sr + '°→' + (d.params.mr != null ? d.params.mr : '?') + '°→' + d.params.er + '° ' +
'缓1:' + (EASING_NAMES[d.params.easing1 != null ? d.params.easing1 : d.params.easing] || '?') + ' ' +
'缓2:' + (EASING_NAMES[d.params.easing2 != null ? d.params.easing2 : d.params.easing] || '?') + ' ' +
(d.params.timeSM != null ? (d.params.timeSM/10).toFixed(1) + 's' : '?') + '/' + (d.params.timeME != null ? (d.params.timeME/10).toFixed(1) + 's' : '?') + '
';
}
html += '
';
}
body.innerHTML = html;
}
function formatTime(sec) {
var m = Math.floor(sec / 60);
var s = Math.floor(sec % 60);
return m + ':' + (s < 10 ? '0' : '') + s;
}
function escapeHtml(str) {
return str.replace(/&/g, '&').replace(//g, '>');
}
function buildDanmakuListPanel() {
if (dmListPanel) return;
dmListPanel = document.createElement('div');
dmListPanel.className = 'nml-dm-list-panel';
dmListPanel.style.cssText = 'display:block!important;visibility:visible!important;opacity:1!important;';
dmListPanel.innerHTML =
'' +
'';
dmListPanel.querySelector('.nml-dm-list-header').addEventListener('click', function () {
dmListExpanded = !dmListExpanded;
var body = dmListPanel.querySelector('.nml-dm-list-body');
var arrow = dmListPanel.querySelector('.nml-dm-list-arrow');
if (dmListExpanded) {
body.style.display = '';
arrow.textContent = '▾';
} else {
body.style.display = 'none';
arrow.textContent = '▸';
}
dmListPanel.querySelector('.nml-dm-list-title').textContent = '弹幕列表 (' + dmList.length + ')';
renderDanmakuList();
});
// Styles
var ds = document.createElement('style');
ds.textContent =
'.nml-dm-list-panel{margin-bottom:16px;border:1px solid var(--line_regular,#e5e5e5);border-radius:8px;overflow:hidden;}' +
'.nml-dm-list-header{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;' +
'cursor:pointer;user-select:none;background:var(--graph_bg_thin,rgba(0,0,0,0.02));transition:background 0.15s;}' +
'.nml-dm-list-header:hover{background:var(--graph_bg_thin,rgba(0,0,0,0.05));}' +
'.nml-dm-list-title{font-size:14px;font-weight:600;color:var(--text1,#333);}' +
'.nml-dm-list-arrow{font-size:14px;color:var(--text2,#999);}' +
'.nml-dm-list-body{max-height:320px;overflow-y:auto;padding:8px 14px;' +
'scrollbar-width:thin;scrollbar-color:var(--brand_pink,#ff6fae) transparent;}' +
'.nml-dm-list-body::-webkit-scrollbar{width:4px;}' +
'.nml-dm-list-body::-webkit-scrollbar-thumb{background:var(--brand_pink,#ff6fae);border-radius:4px;}' +
'.nml-dm-entry{padding:6px 0;border-bottom:1px solid var(--line_regular,rgba(0,0,0,0.05));font-size:13px;}' +
'.nml-dm-entry:last-child{border-bottom:none;}' +
'.nml-dm-time{color:var(--text2,#999);margin-right:8px;font-family:monospace;}' +
'.nml-dm-tag{display:inline-block;padding:1px 6px;border-radius:3px;font-size:11px;margin-right:6px;}' +
'.nml-dm-tag-top{background:rgba(0,200,100,0.15);color:#00c864;}' +
'.nml-dm-tag-bottom{background:rgba(0,150,255,0.15);color:#0096ff;}' +
'.nml-dm-tag-rev{background:rgba(255,150,0,0.15);color:#ff9600;}' +
'.nml-dm-tag-adv{background:rgba(255,111,174,0.15);color:#ff6fae;}' +
'.nml-dm-text{color:var(--text1,#333);}' +
'.nml-dm-params{font-size:11px;color:var(--text2,#999);margin-top:2px;padding-left:60px;}';
document.head.appendChild(ds);
}
function findRightAd() {
var related = document.querySelector('[class*=related], [class*=recommend]');
if (related) {
var ad = related.previousElementSibling;
while (ad) {
if (ad.matches('.ad-slot')) return ad;
ad = ad.previousElementSibling;
}
}
var ads = document.querySelectorAll('.ad-slot');
return ads.length > 0 ? ads[ads.length - 1] : null;
}
function injectDanmakuListPanel() {
buildDanmakuListPanel();
if (document.contains(dmListPanel)) { updateDanmakuListCount(); return; }
var ad = findRightAd();
if (!ad) { console.log('[nml-dm-list] no right .ad-slot found'); return; }
ad.parentNode.insertBefore(dmListPanel, ad.nextSibling);
ad.style.display = 'none';
updateDanmakuListCount();
console.log('[nml-dm-list] injected, entries=' + dmList.length);
}
function updateDanmakuListCount() {
if (!dmListPanel) return;
var title = dmListPanel.querySelector('.nml-dm-list-title');
if (title) title.textContent = '弹幕列表 (' + dmList.length + ')';
}
// Guard
setInterval(function () {
if (!location.href.includes('/video/')) return;
var ad = findRightAd();
if (ad && ad.style.display !== 'none') ad.style.display = 'none';
if (!dmListPanel || !document.contains(dmListPanel)) {
console.log('[nml-dm-list] re-injecting');
dmListPanel = null;
injectDanmakuListPanel();
}
}, 1500);
let dmModeBar = null;
function buildDanmakuModeBar() {
if (dmModeBar) return;
dmModeBar = document.createElement('div');
dmModeBar.className = 'nml-dm-mode-section';
var modeRow = document.createElement('div');
modeRow.className = 'setting-row nml-dm-mode-row';
modeRow.setAttribute('data-v-65d53e0f', '');
modeRow.innerHTML = '模式';
var btnGroup = document.createElement('div');
btnGroup.className = 'nml-dm-mode-btns';
var modes = [
{ id: 0, label: '滚动' },
{ id: 1, label: '顶部' },
{ id: 2, label: '底部' },
{ id: 3, label: '逆向' },
{ id: 4, label: '高级' }
];
modes.forEach(function (m) {
var btn = document.createElement('button');
btn.setAttribute('data-v-65d53e0f', '');
btn.className = 'nml-dm-mode-btn' + (m.id === danmakuMode ? ' active' : '');
btn.textContent = m.label;
btn.addEventListener('mousedown', function (e) {
e.stopPropagation(); e.preventDefault();
danmakuMode = m.id;
btnGroup.querySelectorAll('.nml-dm-mode-btn').forEach(function (b) { b.classList.remove('active'); });
btn.classList.add('active');
toggleAdvControls();
});
btnGroup.appendChild(btn);
});
modeRow.appendChild(btnGroup);
dmModeBar.appendChild(modeRow);
var advControls = document.createElement('div');
advControls.className = 'nml-adv-controls';
advControls.setAttribute('data-v-65d53e0f', '');
advControls.style.display = danmakuMode === 4 ? '' : 'none';
// Compact slider builder
function buildAdvSlider(cfg) {
var row = document.createElement('div');
row.className = 'nml-adv-col-row';
row.setAttribute('data-v-65d53e0f', '');
var disp = cfg.fmt ? cfg.fmt(cfg.val) : cfg.val + (cfg.unit || '');
row.innerHTML = '' + cfg.label + '' +
'' +
'' + disp + '';
var slider = row.querySelector('input');
var valSpan = row.querySelector('.setting-val');
slider.addEventListener('input', function () {
var v = parseInt(slider.value);
valSpan.textContent = cfg.fmt ? cfg.fmt(v) : v + (cfg.unit || '');
cfg.set(v);
});
return row;
}
// Three-column waypoint layout
var columns = document.createElement('div');
columns.className = 'nml-adv-columns';
var waypoints = [
{ title: '起点', cfgs: [
{ ref: function() { return advSX; }, set: function(v) { advSX = v; }, label: 'X', min: 0, max: 100, val: advSX, unit: '%' },
{ ref: function() { return advSY; }, set: function(v) { advSY = v; }, label: 'Y', min: 0, max: 100, val: advSY, unit: '%' },
{ ref: function() { return advSR; }, set: function(v) { advSR = v; }, label: '∠', min: 0, max: 360, step: 3, val: advSR, unit: '°' }
]},
{ title: '中间', cfgs: [
{ ref: function() { return advMX; }, set: function(v) { advMX = v; }, label: 'X', min: 0, max: 100, val: advMX, unit: '%' },
{ ref: function() { return advMY; }, set: function(v) { advMY = v; }, label: 'Y', min: 0, max: 100, val: advMY, unit: '%' },
{ ref: function() { return advMR; }, set: function(v) { advMR = v; }, label: '∠', min: 0, max: 360, step: 3, val: advMR, unit: '°' }
]},
{ title: '终点', cfgs: [
{ ref: function() { return advEX; }, set: function(v) { advEX = v; }, label: 'X', min: 0, max: 100, val: advEX, unit: '%' },
{ ref: function() { return advEY; }, set: function(v) { advEY = v; }, label: 'Y', min: 0, max: 100, val: advEY, unit: '%' },
{ ref: function() { return advER; }, set: function(v) { advER = v; }, label: '∠', min: 0, max: 360, step: 3, val: advER, unit: '°' }
]}
];
waypoints.forEach(function (wp) {
var col = document.createElement('div');
col.className = 'nml-adv-col';
var title = document.createElement('div');
title.className = 'nml-adv-col-title';
title.textContent = wp.title;
col.appendChild(title);
wp.cfgs.forEach(function (cfg) { col.appendChild(buildAdvSlider(cfg)); });
columns.appendChild(col);
});
advControls.appendChild(columns);
// Timing row (full width)
var timeRow = document.createElement('div');
timeRow.className = 'nml-adv-time-row';
var timeCfgs = [
{ ref: function() { return advTimeSM; }, set: function(v) { advTimeSM = v; }, label: '起→中', min: 5, max: 100, val: advTimeSM, fmt: function(v) { return (v/10).toFixed(1) + 's'; } },
{ ref: function() { return advTimeME; }, set: function(v) { advTimeME = v; }, label: '中→终', min: 5, max: 100, val: advTimeME, fmt: function(v) { return (v/10).toFixed(1) + 's'; } }
];
timeCfgs.forEach(function (cfg) {
var d = document.createElement('div');
d.className = 'setting-row';
d.setAttribute('data-v-65d53e0f', '');
var disp = cfg.fmt ? cfg.fmt(cfg.val) : cfg.val + (cfg.unit || '');
d.innerHTML = '' + cfg.label + '' +
'' +
'' + disp + '';
var slider = d.querySelector('input');
var valSpan = d.querySelector('.setting-val');
slider.addEventListener('input', function () {
var v = parseInt(slider.value);
valSpan.textContent = cfg.fmt ? cfg.fmt(v) : v + (cfg.unit || '');
cfg.set(v);
});
timeRow.appendChild(d);
});
advControls.appendChild(timeRow);
function buildEasingRow(label, getter, setter) {
var row = document.createElement('div');
row.className = 'setting-row';
row.setAttribute('data-v-65d53e0f', '');
row.innerHTML = '' + label + '';
var btns = document.createElement('div');
btns.className = 'nml-easing-btns';
EASING_NAMES.forEach(function (name, i) {
var eb = document.createElement('button');
eb.setAttribute('data-v-65d53e0f', '');
eb.className = 'nml-easing-btn' + (i === getter() ? ' active' : '');
eb.textContent = name;
eb.addEventListener('mousedown', function (e) {
e.stopPropagation(); e.preventDefault();
setter(i);
btns.querySelectorAll('.nml-easing-btn').forEach(function (b) { b.classList.remove('active'); });
eb.classList.add('active');
});
btns.appendChild(eb);
});
row.appendChild(btns);
return row;
}
advControls.appendChild(buildEasingRow('缓动1', function() { return advEasing1; }, function(v) { advEasing1 = v; }));
advControls.appendChild(buildEasingRow('缓动2', function() { return advEasing2; }, function(v) { advEasing2 = v; }));
dmModeBar.appendChild(advControls);
// Easing button styles
if (!document.getElementById('nml-easing-style')) {
var es = document.createElement('style');
es.id = 'nml-easing-style';
es.textContent =
'.nml-easing-btns{display:flex;flex-wrap:wrap;gap:3px;}' +
'.nml-easing-btn{font-size:11px;padding:2px 6px;border-radius:3px;cursor:pointer;' +
'color:var(--text2,#999);background:transparent;border:1px solid var(--line_regular,rgba(255,255,255,0.1));transition:all 0.15s;outline:none;}' +
'.nml-easing-btn:hover{color:var(--text1,#fff);border-color:var(--brand_pink,#ff6fae);}' +
'.nml-easing-btn.active{background:var(--brand_pink,#ff6fae);color:#fff;border-color:var(--brand_pink,#ff6fae);}';
document.head.appendChild(es);
}
// Style
if (!document.getElementById('nml-dm-mode-style')) {
var s = document.createElement('style');
s.id = 'nml-dm-mode-style';
s.textContent =
/* Mode section container — separated from other settings rows */
'.nml-dm-mode-section{margin-top:10px;padding-top:10px;border-top:2px dashed var(--brand_pink,rgba(255,111,174,0.35));}' +
/* Mode selector row (matches .setting-row layout) */
'.nml-dm-mode-btns{display:flex;gap:4px;}' +
'.nml-dm-mode-btn{' +
'font-size:12px;padding:3px 10px;border-radius:4px;cursor:pointer;' +
'color:var(--text2,#999);background:transparent;border:1px solid var(--line_regular,rgba(255,255,255,0.12));' +
'transition:all 0.15s;outline:none;' +
'}' +
'.nml-dm-mode-btn:hover{color:var(--text1,#fff);border-color:var(--brand_pink,#ff6fae);}' +
'.nml-dm-mode-btn.active{background:var(--brand_pink,#ff6fae);color:#fff;border-color:var(--brand_pink,#ff6fae);}' +
/* Advanced controls block — appears directly below mode row, visually grouped */
'.nml-adv-controls{' +
'margin-top:8px;padding:8px 10px;' +
'background:var(--graph_bg_thin,rgba(255,255,255,0.03));' +
'border-radius:8px;border:1px solid var(--line_regular,rgba(255,255,255,0.06));' +
'}' +
'.nml-adv-columns{display:flex;gap:6px;margin-bottom:6px;}' +
'.nml-adv-col{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;}' +
'.nml-adv-col-title{text-align:center;font-size:11px;font-weight:700;color:var(--brand_pink,#ff6fae);margin-bottom:1px;padding:2px 0;border-bottom:1px solid var(--brand_pink,rgba(255,111,174,0.25));}' +
'.nml-adv-col-row{display:flex;align-items:center;gap:2px;}' +
'.nml-adv-col-row>span:first-child{font-size:10px;color:var(--text2,#999);width:14px;flex-shrink:0;text-align:center;}' +
'.nml-adv-col-row input[type=range]{flex:1;min-width:0;height:3px;}' +
'.nml-adv-col-row .setting-val{font-size:10px;color:var(--text2,#999);width:26px;flex-shrink:0;text-align:right;}' +
'.nml-adv-time-row{display:flex;gap:8px;margin-top:6px;margin-bottom:6px;}' +
'.nml-adv-time-row .setting-row{flex:1;}' +
/* Close button (X) for settings panel */
'.nml-panel-close{' +
'position:absolute!important;top:8px;right:8px;z-index:10;' +
'width:32px;height:32px;display:flex;align-items:center;justify-content:center;' +
'background:transparent;border:none;border-radius:50%;cursor:pointer;' +
'color:var(--text3,#999);transition:all 0.15s;padding:4px;outline:none;' +
'}' +
'.nml-panel-close:hover{background:var(--graph_bg_regular,rgba(255,255,255,0.08));color:var(--text1,#fff);}' +
'.nml-panel-close:active{background:var(--graph_bg_thick,rgba(255,255,255,0.12));transform:scale(0.92);}';
document.head.appendChild(s);
}
}
function toggleAdvControls() {
var ctrls = document.querySelector('.nml-adv-controls');
if (ctrls) ctrls.style.display = danmakuMode === 4 ? '' : 'none';
}
function injectDanmakuModeBar() {
var panel = document.querySelector('.danmaku-input-wrapper .settings-panel');
if (!panel || panel.querySelector('.nml-dm-mode-row')) return;
buildDanmakuModeBar();
panel.appendChild(dmModeBar);
toggleAdvControls();
if (!panel.querySelector('.nml-panel-close')) {
var closeBtn = document.createElement('button');
closeBtn.className = 'nml-panel-close';
closeBtn.setAttribute('data-v-65d53e0f', '');
closeBtn.innerHTML = '';
closeBtn.title = '关闭设置';
closeBtn.addEventListener('mousedown', function (e) {
e.stopPropagation();
e.preventDefault();
var settingsBtn = document.querySelector('.danmaku-input-wrapper .settings-btn');
if (settingsBtn) settingsBtn.click();
});
panel.insertBefore(closeBtn, panel.firstChild);
}
}
var settingsPanelObserver = new MutationObserver(function (mutations) {
mutations.forEach(function (m) {
m.addedNodes.forEach(function (node) {
if (node.nodeType === 1 && node.classList && node.classList.contains('settings-panel')) {
injectDanmakuModeBar();
}
});
});
});
function hookSettingsPanelObserver() {
settingsPanelObserver.disconnect();
var wrapper = document.querySelector('.danmaku-input-wrapper');
if (wrapper) {
settingsPanelObserver.observe(wrapper, { childList: true, subtree: true });
// Check if panel is already open
if (wrapper.querySelector('.settings-panel')) injectDanmakuModeBar();
} else {
setTimeout(hookSettingsPanelObserver, 800);
}
}
// Init
let initAttempts = 0;
// Mobile header adaptation
var _mobileAdaptDone = false;
var _bodyObserver = null;
var _myPanelObserver = null;
function setupMessageButton() {
if (window.innerWidth > 760) return;
// Find the 消息 nav-item in bottom bar
var navItems = document.querySelectorAll('.mobile-nav-bar .nav-item');
var msgItem = null;
for (var ni = 0; ni < navItems.length; ni++) {
if ((navItems[ni].textContent || '').trim() === '消息') { msgItem = navItems[ni]; break; }
}
if (!msgItem) return;
// Hide original
msgItem.style.display = 'none';
msgItem.classList.add('nml-msg-hidden');
// Create or re-insert the header clone
var existing = document.querySelector('.nml-header-msg');
if (existing) return; // already in place
var rightEntry = document.querySelector('.right-entry');
if (!rightEntry) return;
var msgClone = document.createElement('div');
msgClone.className = 'nml-header-msg';
msgClone.innerHTML = '';
msgClone.title = '消息';
msgClone.addEventListener('click', function () {
var orig = document.querySelector('.mobile-nav-bar .nml-msg-hidden');
if (!orig) {
var items = document.querySelectorAll('.mobile-nav-bar .nav-item');
for (var i = 0; i < items.length; i++) {
if ((items[i].textContent || '').trim() === '消息') { orig = items[i]; break; }
}
}
if (orig) orig.click();
});
var themeToggle = rightEntry.querySelector('.theme-toggle');
if (themeToggle) {
rightEntry.insertBefore(msgClone, themeToggle);
} else {
rightEntry.appendChild(msgClone);
}
}
function checkAndFixMessageButton() {
if (window.innerWidth > 760) return;
var navItems = document.querySelectorAll('.mobile-nav-bar .nav-item');
var msgItem = null;
for (var i = 0; i < navItems.length; i++) {
if ((navItems[i].textContent || '').trim() === '消息') { msgItem = navItems[i]; break; }
}
if (msgItem) {
if (!msgItem.classList.contains('nml-msg-hidden')) {
msgItem.style.display = 'none';
msgItem.classList.add('nml-msg-hidden');
}
if (!document.querySelector('.nml-header-msg')) {
setupMessageButton();
}
}
}
function startMobileObservers() {
if (window.innerWidth > 760) return;
if (_bodyObserver) _bodyObserver.disconnect();
checkAndFixMessageButton();
_bodyObserver = new MutationObserver(function () {
checkAndFixMessageButton();
});
_bodyObserver.observe(document.body, { childList: true, subtree: true });
}
function injectMobileChannelBtns() {
if (window.innerWidth > 760) return;
if (location.pathname !== '/' && location.pathname !== '') return;
var bar = document.querySelector('.tab-bar__left');
if (!bar) return;
if (bar.querySelector('.nml-mobile-channel')) return;
var template = bar.querySelector('.tab-btn');
if (!template) return;
var channels = [
{ name: '动态', url: '/dynamic' },
{ name: '热门', url: '/hot' }
];
channels.forEach(function (ch) {
var btn = template.cloneNode(false);
btn.className = 'tab-btn nml-mobile-channel';
btn.textContent = ch.name;
btn.classList.remove('active');
btn.addEventListener('click', function () {
try {
var app = document.getElementById('__nuxt').__vue_app__;
app.config.globalProperties.$router.push(ch.url);
} catch (e2) {
window.location.href = ch.url;
}
});
bar.appendChild(btn);
});
}
var _mobileChannelInterval = setInterval(function () {
if (window.innerWidth <= 760 &&
(location.pathname === '/' || location.pathname === '') &&
document.querySelector('.tab-bar__left') &&
!document.querySelector('.tab-bar__left .nml-mobile-channel')) {
injectMobileChannelBtns();
}
}, 800);
function adaptMobileLayout() {
if (window.innerWidth > 760) return;
setupMessageButton();
injectMobileChannelBtns();
var retries = 0;
(function retry() {
var done = true;
if (retries < 8) {
if (!document.querySelector('.nml-header-msg')) { setupMessageButton(); done = false; }
if (!document.querySelector('.nml-mobile-channel')) { injectMobileChannelBtns(); done = false; }
if (!done) { retries++; setTimeout(retry, 250); }
}
})();
startMobileObservers();
if (_mobileAdaptDone) return;
_mobileAdaptDone = true;
if (_myPanelObserver) _myPanelObserver.disconnect();
_myPanelObserver = new MutationObserver(function (mutations) {
mutations.forEach(function (m) {
m.addedNodes.forEach(function (node) {
if (node.nodeType !== 1) return;
var panel = node.classList && node.classList.contains('my-menu-panel') ? node :
node.querySelector ? node.querySelector('.my-menu-panel') : null;
if (!panel || panel.querySelector('[data-nml-creator]')) return;
var items = panel.querySelectorAll('.my-menu-item');
var historyItem = null;
for (var i = 0; i < items.length; i++) {
if ((items[i].textContent || '').indexOf('观看历史') !== -1 ||
(items[i].textContent || '').indexOf('历史') !== -1) {
historyItem = items[i]; break;
}
}
if (historyItem) {
var creatorBtn = document.createElement('button');
creatorBtn.className = 'my-menu-item';
creatorBtn.setAttribute('data-v-e4d29153', '');
creatorBtn.setAttribute('data-nml-creator', '');
creatorBtn.innerHTML = '';
creatorBtn.appendChild(document.createTextNode('创作中心'));
creatorBtn.addEventListener('click', function (e) {
e.stopPropagation();
var overlay = document.querySelector('.my-menu-overlay');
if (overlay) overlay.click();
setTimeout(function () {
var router = null;
try {
var app = document.getElementById('__nuxt').__vue_app__;
router = app.config.globalProperties.$router;
} catch (err) {}
if (router) {
router.push('/creator');
} else {
window.location.href = '/creator';
}
}, 200);
});
historyItem.parentNode.insertBefore(creatorBtn, historyItem.nextSibling);
}
});
});
});
_myPanelObserver.observe(document.body, { childList: true, subtree: true });
}
// Patch avatar panel counts when the popover opens (wait for DOM)
function setupPanelPatch() {
var panel = document.querySelector('.avatar-panel-popover');
if (!panel) { setTimeout(setupPanelPatch, 200); return; }
if (panel._nmlObs) return;
var obs = new MutationObserver(function(mutations) {
mutations.forEach(function(m) {
// Panel became visible (popShow added or display changed)
if (m.type === 'attributes' && (m.attributeName === 'class' || m.attributeName === 'style')) {
var el = m.target;
var shown = el.classList.contains('popShow') || (el.style.display && el.style.display !== 'none');
if (!shown) return;
var countItems = el.querySelectorAll('.counts-item .count-num');
if (countItems.length < 2) return;
// Get user id from Pinia
var userId = null;
var token = getToken();
try {
var app = document.getElementById('__nuxt');
if (app && app.__vue_app__) {
var pinia = app.__vue_app__.config.globalProperties.$pinia;
if (pinia) {
var us = pinia.state.value.user;
if (us && us.token) token = token || us.token;
if (us && us.userInfo && us.userInfo.id) userId = us.userInfo.id;
}
}
} catch(e) {}
if (!token || !userId) return;
fetch(getApiBase() + '/users/' + userId, {
headers: { 'Authorization': 'Bearer ' + token }
}).then(function(r) { return r.ok ? r.json() : null; })
.then(function(data) {
if (!data || !data.user) return;
countItems[0].textContent = String(data.user.following_count || 0);
countItems[1].textContent = String(data.user.followers_count || 0);
}).catch(function() {});
}
});
});
obs.observe(panel, { attributes: true, attributeFilter: ['class', 'style'] });
panel._nmlObs = true;
}
setupPanelPatch();
// Fix author-fans "0 粉丝" on video pages
function patchAuthorFans(el) {
if (!el) {
document.querySelectorAll('.author-fans').forEach(function(e) { patchAuthorFans(e); });
return;
}
if (el._nmlAF) return;
el._nmlAF = true;
var info = el.closest('.author-info');
function fetchAndSet(uid) {
fetch(getApiBase() + '/users/' + uid)
.then(function(r) { return r.ok ? r.json() : null; })
.then(function(data) {
if (!data || !data.user) return;
el.textContent = (data.user.followers_count || 0) + ' 粉丝';
}).catch(function() {});
}
var userId = null;
if (info) {
try {
var node = info;
for (var i = 0; i < 15 && node && !userId; i++) {
var comp = node.__vue_parentComponent;
if (comp) {
var c = comp;
while (c && !userId) {
if (c.props && c.props.video && c.props.video.user_id) {
userId = c.props.video.user_id; break;
}
if (c.setupState && c.setupState.video && c.setupState.video.user_id) {
userId = c.setupState.video.user_id; break;
}
c = c.parent;
}
}
node = node.parentElement;
}
} catch(e) {}
}
if (userId) { fetchAndSet(userId); return; }
var m = location.pathname.match(/\/video\/([a-zA-Z0-9_-]+)/);
if (m) {
fetch(getApiBase() + '/videos/' + m[1])
.then(function(r) { return r.ok ? r.json() : null; })
.then(function(data) {
if (data && data.video && data.video.user_id) {
fetchAndSet(data.video.user_id);
}
}).catch(function() {});
}
}
function doInit() {
adaptMobileLayout();
setupChannelPanel();
if (location.pathname === '/' || location.pathname === '') {
var channelAttempts = 0;
function tryChannel() {
trySelectChannel();
channelAttempts++;
if (channelAttempts < 15) setTimeout(tryChannel, 400);
}
tryChannel();
}
if (location.pathname.indexOf('/video/') === 0) {
hookDanmakuLayer();
hookSettingsPanelObserver();
injectDanmakuListPanel();
hookMdRenderer();
trySetupLikeLongPress();
if (mountUI()) return;
initAttempts++;
if (initAttempts < 50) setTimeout(doInit, 200);
}
if (location.pathname === '/history') {
setTimeout(nmlEnhanceHistoryPage, 800);
}
if (location.pathname.indexOf('/user/') === 0) {
setTimeout(nmlEnhanceUserProfile, 800);
}
if (location.pathname.indexOf('/video/') === 0) {
nmlInstallLikeClickInterceptor();
setTimeout(nmlCheckAndRecordLike, 1500);
setTimeout(function() { patchAuthorFans(); }, 800);
}
if (location.pathname === '/followers' || location.pathname === '/following') {
nmlFixFollowPages();
}
}
doInit();
function onNavigate() {
var url = location.href;
_mobileAdaptDone = false;
if (!url.includes('/followers') && !url.includes('/following')) {
var mainEl = document.querySelector('#__nuxt .main') || document.querySelector('#__nuxt main');
if (mainEl && mainEl.querySelector('.nml-follow-page')) {
mainEl.innerHTML = '';
}
}
setTimeout(adaptMobileLayout, 600);
if (url.includes('/video/')) {
var old = document.querySelector('.nml-speed-wrapper');
if (old) old.remove();
videoObserver.disconnect();
speedBtn = null;
speedMenu = null;
videoEl = null;
mounted = false;
initAttempts = 0;
danmakuMode = 0;
dmList = [];
activeDmLanes = { 1: [], 2: [], 3: [] };
danmakuLayerObserver.disconnect();
nmlCleanupKeepControls();
nmlCleanupMiniPlayer();
nmlCleanupTripleAction();
mdCommentObserver.disconnect();
_mdRetries = 0;
if (_mdInterval) { clearInterval(_mdInterval); _mdInterval = null; }
_mdInterval = setInterval(function () {
if (document.querySelector('.comments-section')) {
hookMdRenderer();
clearInterval(_mdInterval);
_mdInterval = null;
} else if (_mdRetries++ > 60) {
clearInterval(_mdInterval);
_mdInterval = null;
}
}, 500);
currentSpeed = savedSpeed;
setTimeout(doInit, 800);
}
if (url.includes('/history')) {
_nmlHistoryRetries = 0;
_nmlStatusCache = {};
setTimeout(nmlEnhanceHistoryPage, 800);
}
if (url.includes('/user/')) {
setTimeout(nmlEnhanceUserProfile, 800);
}
if (url.includes('/video/')) {
_nmlLikeCheckDone = {};
nmlInstallLikeClickInterceptor();
setTimeout(nmlCheckAndRecordLike, 1500);
setTimeout(function() { patchAuthorFans(); }, 800);
}
if (url.includes('/followers') || url.includes('/following')) {
nmlFixFollowPages();
}
}
var _nmlLastUrl = location.href;
var _pushState = history.pushState;
history.pushState = function () {
_pushState.apply(this, arguments);
if (location.href !== _nmlLastUrl) { _nmlLastUrl = location.href; onNavigate(); }
};
var _replaceState = history.replaceState;
history.replaceState = function () {
_replaceState.apply(this, arguments);
if (location.href !== _nmlLastUrl) { _nmlLastUrl = location.href; onNavigate(); }
};
window.addEventListener('popstate', function () {
if (location.href !== _nmlLastUrl) { _nmlLastUrl = location.href; onNavigate(); }
});
// Start
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () { setTimeout(doInit, 600); });
} else {
setTimeout(doInit, 600);
}
})();