// ==UserScript== // @name Bilibili收藏夹自动分类 // @namespace http://tampermonkey.net/ // @version 1.4 // @description B站收藏夹视频自动分类 // @author https://space.bilibili.com/1937042029,https://github.com/jqwgt // @license GPL-3.0-or-later // @match *://space.bilibili.com/*/favlist* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @connect api.bilibili.com // @downloadURL https://update.greasyfork.icu/scripts/531672/Bilibili%E6%94%B6%E8%97%8F%E5%A4%B9%E8%87%AA%E5%8A%A8%E5%88%86%E7%B1%BB.user.js // @updateURL https://update.greasyfork.icu/scripts/531672/Bilibili%E6%94%B6%E8%97%8F%E5%A4%B9%E8%87%AA%E5%8A%A8%E5%88%86%E7%B1%BB.meta.js // ==/UserScript== (function() { 'use strict'; // 添加全局样式 GM_addStyle(` .bili-classifier-container { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; color: #222; } .bili-classifier-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 25px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); z-index: 10000; max-height: 80vh; overflow-y: auto; width: 700px; max-width: 90vw; } .bili-classifier-modal h3 { margin-top: 0; color: #00a1d6; font-size: 1.5em; border-bottom: 1px solid #eee; padding-bottom: 10px; } .bili-classifier-btn { padding: 10px 16px; background: #00a1d6; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; transition: all 0.2s; margin-right: 10px; } .bili-classifier-btn:hover { background: #0087b4; transform: translateY(-1px); } .bili-classifier-btn.secondary { background: #f0f0f0; color: #666; } .bili-classifier-btn.secondary:hover { background: #e0e0e0; } .bili-classifier-btn.danger { background: #ff4d4f; } .bili-classifier-btn.danger:hover { background: #ff7875; } .bili-classifier-group { margin: 15px 0; padding: 15px; border: 1px solid #eee; border-radius: 8px; background: #fafafa; } .bili-classifier-group-header { display: flex; justify-content: space-between; margin-bottom: 10px; } .bili-classifier-input { padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; width: 200px; margin-right: 10px; } .bili-classifier-select { padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; width: 220px; margin-right: 10px; } .bili-classifier-checkbox-group { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; margin-top: 10px; } .bili-classifier-checkbox-label { display: flex; align-items: center; cursor: pointer; } .bili-classifier-checkbox { margin-right: 8px; } .bili-classifier-footer { display: flex; justify-content: flex-end; margin-top: 20px; padding-top: 15px; border-top: 1px solid #eee; } .bili-classifier-progress { position: fixed; bottom: 30px; right: 30px; background: white; padding: 15px 20px; border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); z-index: 10000; min-width: 250px; } .bili-classifier-progress-bar { width: 100%; height: 10px; background: #f0f0f0; border-radius: 5px; margin: 8px 0; overflow: hidden; } .bili-classifier-progress-fill { height: 100%; background: linear-gradient(90deg, #00a1d6, #00c4ff); border-radius: 5px; transition: width 0.3s; } .bili-classifier-float-btn { position: fixed; right: 30px; bottom: 30px; z-index: 9999; display: flex; flex-direction: column; gap: 10px; } .bili-classifier-links { display: flex; gap: 10px; margin-top: 20px; } .bili-classifier-link-btn { padding: 8px 12px; background: #f0f0f0; color: #666; border-radius: 4px; text-decoration: none; font-size: 12px; display: flex; align-items: center; gap: 5px; } .bili-classifier-link-btn:hover { background: #e0e0e0; } .bili-classifier-radio-group { display: flex; gap: 15px; margin: 15px 0; } .bili-classifier-radio-label { display: flex; align-items: center; gap: 5px; cursor: pointer; } .bili-classifier-option-group { margin: 15px 0; padding: 15px; border: 1px solid #eee; border-radius: 8px; } `); // 获取CSRF令牌 function getCsrf() { return document.cookie.match(/bili_jct=([^;]+)/)?.[1] || ''; } // 添加日志功能 function log(message, type = 'info') { const styles = { info: 'color: #00a1d6', error: 'color: #ff0000', success: 'color: #00ff00' }; console.log(`%c[收藏夹分类] ${message}`, styles[type]); } // 获取用户收藏夹 async function getUserFavLists() { const mid = window.location.pathname.split('/')[1]; return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://api.bilibili.com/x/v3/fav/folder/created/list-all?up_mid=${mid}`, responseType: 'json', onload: function(response) { resolve(response.response.data.list || []); }, onerror: reject }); }); } // 获取视频详细信息 // 获取视频详细信息 增加跳过异常 async function getVideoInfo(aid) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://api.bilibili.com/x/web-interface/view?aid=${aid}`, responseType: 'json', onload: function(response) { if (!response.response.data) { log(`视频 ${aid} 可能已失效或无法访问,跳过处理`, 'error'); reject(new Error(`视频 ${aid} 可能已失效或无法访问`)); return; } const data = response.response.data; log(`获取视频 ${aid} 详细信息:`, 'info'); console.table({ 标题: data.title, 分区ID: data.tid, 分区名: data.tname, 播放量: data.stat.view, }); resolve(data); }, onerror: function(error) { log(`视频 ${aid} 信息获取失败,跳过处理`, 'error'); reject(error); } }); }); } // 获取收藏夹中的视频 async function getFavVideos(mediaId, ps = 20, pn = 1, videos = []) { if (!document.getElementById('reading-progress')) { createReadingProgressDiv(); } return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://api.bilibili.com/x/v3/fav/resource/list?media_id=${mediaId}&pn=${pn}&ps=${ps}&order=mtime&type=0&platform=web`, responseType: 'json', onload: async function(response) { const data = response.response.data; log(`收藏夹API返回数据:`, 'info'); console.log(data); if (!data || !data.medias) { reject('获取视频列表失败'); return; } let currentCount = videos.length; let processedCount = 0; for (let video of data.medias) { try { const videoInfo = await getVideoInfo(video.id); videos.push({ aid: video.id, title: video.title, tid: videoInfo.tid, tname: videoInfo.tname, play: videoInfo.stat.view }); currentCount++; } catch (err) { log(`跳过视频 ${video.id}: ${err.message}`, 'error'); } finally { processedCount++; updateReadingProgress(`正在读取视频,已获取 ${currentCount} 个视频,处理进度 ${processedCount}/${data.medias.length}`); await new Promise(r => setTimeout(r, 300)); } } if (data.has_more) { await getFavVideos(mediaId, ps, pn + 1, videos).then(resolve); } else { document.getElementById('reading-progress')?.remove(); resolve(videos); } }, onerror: reject }); }); } // 创建新收藏夹 async function createFolder(title) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: 'https://api.bilibili.com/x/v3/fav/folder/add', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: `csrf=${getCsrf()}&title=${encodeURIComponent(title)}`, responseType: 'json', onload: function(response) { resolve(response.response.data.id); }, onerror: reject }); }); } // 添加视频到收藏夹 async function addToFav(aid, fid) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: 'https://api.bilibili.com/x/v3/fav/resource/deal', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: `csrf=${getCsrf()}&rid=${aid}&type=2&add_media_ids=${fid}`, responseType: 'json', onload: function(response) { resolve(response.response); }, onerror: reject }); }); } // 从收藏夹移除视频 async function removeFromFav(aid, fid) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: 'https://api.bilibili.com/x/v3/fav/resource/deal', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: `csrf=${getCsrf()}&rid=${aid}&type=2&del_media_ids=${fid}`, responseType: 'json', onload: function(response) { resolve(response.response); }, onerror: reject }); }); } // 创建配置界面 function createConfigUI(tidGroups) { const modal = document.createElement('div'); modal.className = 'bili-classifier-container bili-classifier-modal'; let html = `