在B站个人动态的视频列表里直接加入稍后看按钮
// ==UserScript== // @name BiliBili+ // @namespace [email protected] // @include /https?:\/\/www\.bilibili\.com\/account\/dynamic/ // @version 2017-6-21b // @grant none // jshint esversion:6 // @description 在B站个人动态的视频列表里直接加入稍后看按钮 // ==/UserScript== const WATCH_LATER_TEXT = " 【稍候再看】 "; const REMOVE_WATCH_LATER_TEXT = " 【移除稍候再看】 "; const WATCH_LATER_BG_COLOR = "#1672A4"; // 观察者,执行“添加稍后看按钮” let observer = new MutationObserver(AddWatchLaterButton); // 视频列表 let vidsList = document.querySelector(".stm-lst"); if(vidsList){ observer.observe( vidsList, { childList: true } ); } else { alert("[Bili+]: Vids list not exist"); } // 添加稍候看按钮 async function AddWatchLaterButton(recs){ // console.log("[Bili+]: recs %o", recs); // DEBUG // 先获取稍后看列表 let addedVids = await GettingAddedWatchList(); // console.log("[Bili+]: addedVids %o", addedVids); // DEBUG for(let rec of recs){ rec.addedNodes.forEach(vidRow => { // 如果是视频,加个按钮 if(vidRow.className == "stm-lst-item"){ let vid = vidRow.querySelector(".hint>a"); let av = vid.href; av = av.match(/\d+/)[0]; // console.log("[Bili+]: av " + av); // DEBUG if(!vid.parentNode.querySelector(".watch-later-btn")){ let watchLaterBtn = document.createElement("button"); watchLaterBtn.className = "watch-later-btn"; watchLaterBtn.style.trasition = "0.5s"; if(!addedVids.includes(parseInt(av))){ // 不在稍后看里,只改提示 watchLaterBtn.innerText = WATCH_LATER_TEXT; } else { // 已在稍后看里,改为移除按钮样式 watchLaterBtn.innerText = REMOVE_WATCH_LATER_TEXT; watchLaterBtn.style.backgroundColor = WATCH_LATER_BG_COLOR; } // 挂添加/移除事件处理 watchLaterBtn.onclick = ()=>{ ToggleWatchLater(av, watchLaterBtn); }; // 加在链接后面 vid.parentNode.appendChild(document.createElement("br")); vid.parentNode.appendChild(watchLaterBtn); } } }); } } // 获取稍后看列表 async function GettingAddedWatchList(){ return new Promise(async (resolve, reject)=>{ let r = null; let url = new URL(location.protocol + "//api.bilibili.com/x/v2/history/toview/web"); let params = { jsonp: "jsonp", sid: GetCookieValue('sid'), // 大概是用户id csrf: GetCookieValue('bili_jct') // 并不清楚这是啥 } Object.keys(params).forEach(key => url.searchParams.append(key, params[key])); try{ r = await (await fetch(url, {method: "GET", credentials: "include"})).json(); } catch (ex) { r = null; } // console.log("[Bili+]: fetch r###lt %o", r); // DEBUG if(r && r.data && r.data.list){ resolve(r.data.list.map(vid=>vid.aid)); } else { reject([]) } }); } // 切换稍后看状态 async function ToggleWatchLater(av, btn){ // console.log("[Bili+]: Toggle av %o", av); // DEBUG // 先获取稍后看列表 let addedVids = await GettingAddedWatchList(); let isWatchLater = addedVids.includes(parseInt(av)); // console.log("[Bili+]: " + (isWatchLater ? "Removing from watch later" : "Adding to watch later")); // DEBUG let r###lt = null; let url = new URL(location.protocol + "//api.bilibili.com/x/v2/history/toview/" + (isWatchLater ? "del" : "add")); let params = { aid: av, jsonp: "jsonp", csrf: GetCookieValue('bili_jct') // 并不清楚这是啥 } Object.keys(params).forEach(key => url.searchParams.append(key, params[key])); try{ r###lt = await (await fetch(url, {method: "POST", credentials: "include"})).json(); } catch (ex) { // 一般来说是网络错误 r###lt = ex; btn.innerText = " 【操作失败,请重试】 "; } if(r###lt.code == "0"){ // 添加/移除成功 btn.innerText = (isWatchLater ? WATCH_LATER_TEXT : REMOVE_WATCH_LATER_TEXT); btn.style.backgroundColor = (isWatchLater ? "" : WATCH_LATER_BG_COLOR); if(MessageBox){(new MessageBox).show(btn, "已" + (isWatchLater ? "移除" : "添加"), 700, "info")}; } else { // 服务器返回失败 if(MessageBox){(new MessageBox).show(btn, "粗错啦:\n" + r###lt.message, 3000, "error")}; btn.innerText = r###lt.message; } } // 获取cookie function GetCookieValue(name) { var valueMatch = document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)'); return valueMatch ? valueMatch.pop() : ''; }