您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动获取并播放B站动态视频
// ==UserScript== // @name B站动态视频添加到稍后观看 // @namespace http://tampermonkey.net/ // @version 0.4 // @description 自动获取并播放B站动态视频 // @author Your name // @match *://t.bilibili.com/* // @match *://www.bilibili.com/* // @match *://www.bilibili.com/video/* // @grant GM_xmlhttpRequest // @connect api.bilibili.com // @license MIT // ==/UserScript== (function() { 'use strict'; // 常量定义 const CONSTANTS = { API: { WATCH_LATER: 'https://api.bilibili.com/x/v2/history/toview/web', DYNAMIC_FEED: 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all', ADD_TO_WATCH: 'https://api.bilibili.com/x/v2/history/toview/add', }, STORAGE_KEY: 'BILIBILI_ADDED_VIDEOS', MAX_PAGES: 20, REQUEST_DELAY: 300, STORAGE_EXPIRE_DAYS: 7, }; // 优化的 StorageManager const StorageManager = { setWithExpiry(key, value, days = CONSTANTS.STORAGE_EXPIRE_DAYS) { const item = { value, expiry: new Date().getTime() + (days * 24 * 60 * 60 * 1000), } try { localStorage.setItem(key, JSON.stringify(item)); return true; } catch (error) { console.error('存储数据失败:', error); return false; } }, getWithExpiry(key) { try { const itemStr = localStorage.getItem(key); if (!itemStr) return null; const item = JSON.parse(itemStr); const now = new Date().getTime(); if (now > item.expiry) { localStorage.removeItem(key); return null; } return item.value; } catch (error) { console.error('读取数据失败:', error); return null; } }, clearExpired(key) { const item = this.getWithExpiry(key); if (!item) { console.log('数据已过期或不存在,已清除'); } } }; // 工具函数 const utils = { async sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }, async retry(fn, times = 3, delay = 1000) { for (let i = 0; i < times; i++) { try { return await fn(); } catch (err) { if (i === times - 1) throw err; console.log(`操作失败,${delay/1000}秒后重试:`, err); await this.sleep(delay); } } } }; // API 请求封装 const api = { // 获取 CSRF token getCsrfToken() { const cookies = document.cookie.split(';'); for (const cookie of cookies) { const [name, value] = cookie.trim().split('='); if (name === 'bili_jct') { return value; } } return ''; }, async request(url, options = {}) { const defaultOptions = { credentials: 'include', headers: { 'Accept': 'application/json', 'Cache-Control': 'no-cache' } }; // 如果是 POST 请求,添加 CSRF token if (options.method === 'POST') { const csrf = this.getCsrfToken(); if (!csrf) { throw new Error('未找到 CSRF token,请确保已登录'); } // 处理表单数据 if (options.body) { options.body += `&csrf=${csrf}`; } else { options.body = `csrf=${csrf}`; } // 设置 Content-Type if (!options.headers) { options.headers = {}; } options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; } const response = await fetch(url, { ...defaultOptions, ...options }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); if (data.code !== 0) { throw new Error(`API error! code: ${data.code}, message: ${data.message}`); } return data.data; }, async getWatchLaterList() { try { const data = await this.request(CONSTANTS.API.WATCH_LATER); return new Set(data.list?.map(item => item.aid.toString()) || []); } catch (error) { console.error('获取稍后观看列表失败:', error); return new Set(); } }, async addToWatchLater(aid) { try { const csrf = this.getCsrfToken(); if (!csrf) { console.error('未登录状态,请先登录'); return false; } const response = await this.request(CONSTANTS.API.ADD_TO_WATCH, { method: 'POST', body: `aid=${aid}&csrf=${csrf}` }); return true; } catch (error) { console.error('添加失败:', error); if (error.message.includes('未登录')) { alert('请先登录 B 站账号!'); } return false; } } }; // 主要业务逻辑 class VideoManager { constructor() { this.videoList = []; this.currentPage = 1; this.lastOffset = ''; this.watchLaterList = null; // 新增:缓存稍后观看列表 } // 新增:获取并缓存稍后观看列表 async initWatchLaterList() { this.watchLaterList = await api.getWatchLaterList(); console.log(`已获取稍后观看列表,共 ${this.watchLaterList.size} 个视频`); } async fetchVideos() { return utils.retry(async () => { const data = await api.request(`${CONSTANTS.API.DYNAMIC_FEED}?timezone_offset=-480&type=all&page=${this.currentPage}&offset=${this.lastOffset}`); const videos = data.items .filter(item => item.modules?.module_dynamic?.major?.type === 'MAJOR_TYPE_ARCHIVE') .map(item => { const archive = item.modules.module_dynamic.major.archive; return { bvid: archive.bvid, aid: archive.aid, title: archive.title, url: `https://www.bilibili.com/video/${archive.bvid}` }; }); console.log(`第 ${this.currentPage} 页找到 ${videos.length} 个视频`); this.videoList = this.videoList.concat(videos); this.lastOffset = data.offset || ''; return this.lastOffset; }); } async loadAllPages() { while (this.currentPage <= CONSTANTS.MAX_PAGES) { const hasMore = await this.fetchVideos(); if (!hasMore) break; this.currentPage++; await utils.sleep(CONSTANTS.REQUEST_DELAY); } } async processVideos() { // 先获取稍后观看列表 await this.initWatchLaterList(); if (!this.watchLaterList) { console.error('获取稍后观看列表失败'); return; } // 获取本地存储的已处理视频列表 const processedVideos = getProcessedVideos(); console.log(`当前稍后观看列表有 ${this.watchLaterList.size} 个视频`); console.log(`本地记录的已处理视频数: ${processedVideos.length}`); // 过滤需要添加的视频:既不在稍后观看列表中,也不在本地记录中 const videosToAdd = this.videoList.filter(video => { const videoId = video.aid.toString(); return !this.watchLaterList.has(videoId) && !processedVideos.includes(videoId); }); console.log(`找到 ${this.videoList.length} 个视频,其中 ${videosToAdd.length} 个需要添加`); if (videosToAdd.length > 0) { console.log('即将添加的视频:'); videosToAdd.forEach((video, index) => { console.log(`${index + 1}. ${video.title}`); }); } let successCount = 0; for (const video of videosToAdd) { console.log(`正在添加: ${video.title}`); if (await api.addToWatchLater(video.aid)) { successCount++; addToProcessedVideos(video.aid.toString()); console.log(`✅ 成功添加: ${video.title}`); } else { console.log(`❌ 添加失败: ${video.title}`); } await utils.sleep(CONSTANTS.REQUEST_DELAY); } return { total: this.videoList.length, added: successCount, existing: this.videoList.length - videosToAdd.length }; } } // UI 组件 const UI = { createButton() { const button = document.createElement('div'); button.innerHTML = ` <div style=" position: fixed; right: 20px; top: 200px; z-index: 999; width: 32px; height: 32px; background: white; border-radius: 50%; box-shadow: 0 2px 4px rgba(0,0,0,0.2); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; opacity: 0.8; "> <svg viewBox="0 0 24 24" width="20" height="20"> <path fill="#00AEEC" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/> </svg> </div> `; const buttonElement = button.firstElementChild; this.addButtonEffects(buttonElement); document.body.appendChild(button); }, addButtonEffects(button) { button.addEventListener('mouseover', () => { button.style.opacity = '1'; button.style.transform = 'scale(1.1)'; }); button.addEventListener('mouseout', () => { button.style.opacity = '0.8'; button.style.transform = 'scale(1)'; }); button.addEventListener('click', async () => { button.style.pointerEvents = 'none'; button.style.opacity = '0.5'; try { const manager = new VideoManager(); await manager.loadAllPages(); const result = await manager.processVideos(); console.log('\n处理完成:'); console.log(`✅ 成功添加: ${result.added} 个视频`); console.log(`⏭️ 已在列表中: ${result.existing} 个视频`); console.log(`📊 动态中总视频数: ${result.total}`); } catch (error) { console.error('执行失败:', error); } finally { button.style.pointerEvents = 'auto'; button.style.opacity = '0.8'; } }); } }; // 初始化 UI.createButton(); function getProcessedVideos() { const stored = localStorage.getItem(CONSTANTS.STORAGE_KEY); return stored ? JSON.parse(stored) : []; } function addToProcessedVideos(videoId) { const processed = getProcessedVideos(); if (!processed.includes(videoId)) { processed.push(videoId); localStorage.setItem(CONSTANTS.STORAGE_KEY, JSON.stringify(processed)); } } function isVideoProcessed(videoId) { return getProcessedVideos().includes(videoId); } // 获取已添加的视频列表 function getAddedVideos() { const stored = localStorage.getItem(CONSTANTS.STORAGE_KEY); return stored ? JSON.parse(stored) : []; } // 添加视频ID到记录中 function addToVideoRecord(videoId) { const added = getAddedVideos(); if (!added.includes(videoId)) { added.push(videoId); localStorage.setItem(CONSTANTS.STORAGE_KEY, JSON.stringify(added)); } } async function processVideo(item) { const videoId = item.modules?.module_dynamic?.major?.archive?.aid; if (!videoId) return; // 检查是否已经添加过 const addedVideos = getAddedVideos(); if (addedVideos.includes(videoId)) { console.log(`视频 ${videoId} 已经添加过,跳过`); return; } const isWatched = await checkIfWatched(videoId); if (!isWatched) { // 只有成功添加后才记录 const addSuccess = await addToWatchLater(videoId); if (addSuccess) { addToVideoRecord(videoId); console.log(`视频 ${videoId} 添加成功并记录`); } else { console.log(`视频 ${videoId} 添加失败,不记录`); } } } // 检查视频是否已观看 async function checkIfWatched(videoId) { try { const watchLaterList = await api.getWatchLaterList(); return watchLaterList.has(videoId.toString()); } catch (error) { console.error('检查视频状态失败:', error); return false; } } // 添加到稍后观看 async function addToWatchLater(videoId) { try { return await api.addToWatchLater(videoId); } catch (error) { console.error('添加到稍后观看失败:', error); return false; } } })();