// ==UserScript== // @name 百度网盘在线视频播放器 // @namespace https://example.com/ // @version 2.8 // @description 百度网盘视频在线播放,支持倍速调节(0.25x-4x)、画质切换(360P-1080P),记忆播放进度、默认最高画质 // @match https://pan.baidu.com/s/* // @grant unsafeWindow // @run-at document-end // @icon https://nd-static.bdstatic.com/m-static/wp-brand/favicon.ico // @license MIT // ==/UserScript== (function() { 'use strict'; var PROGRESS_EXPIRY_DAYS = 7; var EXPIRY_MS = PROGRESS_EXPIRY_DAYS * 24 * 3600 * 1000; function formatDuration(seconds) { if (!seconds || seconds <= 0) return ''; var s = Math.floor(seconds); var h = Math.floor(s / 3600); var m = Math.floor((s % 3600) / 60); var sec = s % 60; return h > 0 ? h + ':' + String(m).padStart(2, '0') + ':' + String(sec).padStart(2, '0') : m + ':' + String(sec).padStart(2, '0'); } function getFileName(file) { return file.server_filename || file.filename || file.name || file.title || '未知文件'; } function getFolderName() { var hash = location.hash; var match = hash.match(/path=([^&]+)/); if (match) { var path = decodeURIComponent(match[1]); var idx = path.indexOf('sharelink'); if (idx >= 0) { var sub = path.substring(idx); var parts = sub.split('/'); if (parts.length > 1) parts.shift(); return parts.join('/') || '根目录'; } return path || '根目录'; } return '根目录'; } function getSurId() { return (location.pathname.match(/\/s\/(\w+)/) || [])[1] || ''; } function getCurrentList() { try { var ctx = unsafeWindow.require('system-core:context/context.js'); var list = ctx.instanceForSystem.list.getCurrentList(); if (list && list.length) { sessionStorage.setItem(getSurId(), JSON.stringify(list)); return list; } } catch (e) {} try { if (unsafeWindow.__LIST__ && unsafeWindow.__LIST__.length) { sessionStorage.setItem(getSurId(), JSON.stringify(unsafeWindow.__LIST__)); return unsafeWindow.__LIST__; } } catch (e) {} var scripts = document.querySelectorAll('script'); for (var i = 0; i < scripts.length; i++) { var text = scripts[i].textContent; if (text && text.indexOf('"list":[') !== -1 && text.indexOf('"category"') !== -1) { try { var match = text.match(/"list":(\[.*?\]),/) || text.match(/"list":(\[.*?\])/); if (match) { var list = JSON.parse(match[1]); if (list && list.length) { sessionStorage.setItem(getSurId(), JSON.stringify(list)); return list; } } } catch (e) {} } } var cached = sessionStorage.getItem(getSurId()); if (cached) { try { return JSON.parse(cached); } catch (e) {} } return []; } function filterVideos(list) { if (!list || !list.length) return []; return list.filter(function(i) { if (i.hasOwnProperty('category')) return i.category === 1; var name = getFileName(i).toLowerCase(); return /\.(mp4|mkv|webm|avi|mov|flv|wmv|ts|m3u8)$/i.test(name); }); } function getCachedVideoList(surId) { var key = 'bd_video_cache_' + surId; var cached = localStorage.getItem(key); if (cached) { try { return JSON.parse(cached); } catch (e) {} } return null; } function safeJsonStringify(obj) { return JSON.stringify(obj).replace(/<\/script>/gi, '<\\/script>'); } function generateSidebarListHTML(enhancedVideos) { if (!enhancedVideos || enhancedVideos.length === 0) { return '
暂无视频文件
'; } var html = ''; enhancedVideos.forEach(function(v, i) { var nameEscaped = v.name.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); var durStr = formatDuration(v.duration); html += '
' + (i+1) + '.' + nameEscaped + '' + durStr + '
'; }); return html; } // ★★★ 清理所有过期的进度记录 ★★★ function cleanExpiredProgress() { var now = Date.now(); var keysToRemove = []; for (var i = 0; i < localStorage.length; i++) { var key = localStorage.key(i); if (key && key.indexOf('bd_play_progress_') === 0) { try { var data = JSON.parse(localStorage.getItem(key)); if (data && data.time) { var age = now - data.time; if (age > EXPIRY_MS) { keysToRemove.push(key); } } } catch (e) {} } } keysToRemove.forEach(function(key) { localStorage.removeItem(key); }); if (keysToRemove.length > 0) { console.log('已清理 ' + keysToRemove.length + ' 条过期的播放进度记录'); } } // ★★★ 保存进度(带时间戳) ★★★ function saveProgressWithExpiry(fid, progress) { var data = { time: Date.now(), progress: progress }; localStorage.setItem('bd_play_progress_' + fid, JSON.stringify(data)); } // ★★★ 读取进度(自动检查有效期) ★★★ function getProgressWithExpiry(fid) { var key = 'bd_play_progress_' + fid; var stored = localStorage.getItem(key); if (!stored) return null; try { var data = JSON.parse(stored); if (data && data.time && data.progress) { var age = Date.now() - data.time; if (age > EXPIRY_MS) { localStorage.removeItem(key); return null; } return data.progress; } return null; } catch (e) { return null; } } function buildPlayerPage(videoList, shareInfo, folderName) { if (!videoList || !videoList.length) { var surId = getSurId(); var cachedList = getCachedVideoList(surId); if (cachedList && cachedList.length) { videoList = cachedList; } else { return '百度网盘视频播放器

🎬 百度网盘视频播放器

当前文件夹没有视频文件,请返回选择包含视频的目录。

'; } } var enhancedVideos = []; videoList.forEach(function(v, idx) { var name = getFileName(v); var fid = v.fs_id || v.fid || v.id || ('fid_' + idx); if (name && fid) { enhancedVideos.push({ id: idx, name: name, fid: fid, duration: 0 }); } }); enhancedVideos.sort(function(a, b) { return a.name.localeCompare(b.name, 'zh-CN', { numeric: true }); }); enhancedVideos.forEach(function(v, i) { v.id = i; }); var videoDataLiteral = safeJsonStringify(enhancedVideos); var shareDataLiteral = safeJsonStringify(shareInfo); var folderLiteral = safeJsonStringify(folderName); var staticListHtml = generateSidebarListHTML(enhancedVideos); var hlsCdn1 = 'https://cdnjs.cloudflare.com/ajax/libs/hls.js/1.6.6/hls.min.js'; var hlsCdn2 = 'https://unpkg.com/hls.js@1.6.6/dist/hls.min.js'; var hlsCdn3 = 'https://cdn.jsdelivr.net/npm/hls.js@1.6.6/dist/hls.min.js'; return '百度网盘视频播放器' + '