// ==UserScript== // @name Pixiv Bookmark Batch-Delete // @version 0.0.5 // @description 随手拼凑的代码,连样式也懒得整,用来删一下自己不喜欢的收藏,通过读取文件夹的图片id再按ID删除收藏;在收藏页可以操作:在右下角先点bm按钮获取所有收藏的信息,再选择del,读取文件夹的图片id删除收藏。 // @author None // @match *://www.pixiv.net/users/* // @grant none // @compatible Chrome // @namespace https://greasyfork.org/users/732531 // @downloadURL https://update.greasyfork.icu/scripts/420880/Pixiv%20Bookmark%20Batch-Delete.user.js // @updateURL https://update.greasyfork.icu/scripts/420880/Pixiv%20Bookmark%20Batch-Delete.meta.js // ==/UserScript== try { $(); } catch (e) { let script = document.createElement('script'); script.src = 'https://code.jquery.com/jquery-2.2.4.min.js'; document.head.appendChild(script); } let LogLevel = { None: 0, Error: 1, Warning: 2, Info: 3, Elements: 4, }; function DoLog(level, msgOrElement) { if (level <= LogLevel.Elements) { let prefix = "%c"; let param = ""; if (level == LogLevel.Error) { prefix += "[Bookmark_Delete][Error]"; param = "color:#ff0000"; } else if (level == LogLevel.Warning) { prefix += "[Bookmark_Delete][Warning]"; param = "color:#ffa500"; } else if (level == LogLevel.Info) { prefix += "[Bookmark_Delete][Info]"; param = "color:#000000"; } else if (level == LogLevel.Elements) { prefix += "[Bookmark_Delete]Elements"; param = "color:#000000"; } if (level <= LogLevel.Elements) { console.log(prefix + msgOrElement, param); } else { console.log(msgOrElement); } } } let g_csrfToken = ""; let userId = ""; // bookmarks.works是图片ID与bookmarkID的映射 let bookmarks = {"works":{}, "total":0}; let WORKS = [] function getUserId() { let matched = window.location.href.match(/users\/([0-9]+)/); if (matched.length > 0) { userId = matched[1]; DoLog(LogLevel.Info, "Got userId: " + userId); } else { DoLog(LogLevel.Error, "Can not get userId."); } } function getToken() { $.get(location.href, function (data) { let matched = data.match(/token":"([a-z0-9]{32})/); if (matched.length > 0) { g_csrfToken = matched[1]; DoLog(LogLevel.Info, "Got g_csrfToken: " + g_csrfToken); } else { DoLog( LogLevel.Error, "Can not get g_csrfToken, so you can not add works to bookmark when sorting has enabled." ); } }); } function processBookmarkData(data){ bookmarks.total = data.body.total; // works是一个作品数组 let works = data.body.works; // 将WORKS信息保存,方便本地处理图片 WORKS.push(...works); for(let i=0 ;i { if (response.ok) { return response.json(); } else { return Promise.reject({ status: response.status, statusText: response.statusText, }); } }) .then((data) => { processBookmarkData(data); }); } async function getAllBookmark(){ bookmarks = {"works":{}, "total":0}; let limit = 100; await getBookmark(0,10); let total = bookmarks.total; let p = [] for(let i=0; i{ if (response.ok) { return response.json(); } else { return Promise.reject({ status: response.status, statusText: response.statusText, }); } }); } async function deleteBookmarkByIds(ids){ let p = []; let bid = []; let works = bookmarks.works; for (let id of ids){ let bid = works[id] if(bid){ p.push(deleteBookmarkBybookmarkId(bid)) } } DoLog(LogLevel.Info,`有--${ids.size}--个作品待删除,在书签中找到${p.length}个`); return Promise.all([p]); } function saveWORKS(o, file_name){ const json = JSON.stringify(o); // 创建一个Blob对象,指定文件类型为json const blob = new Blob([json], {type: 'application/json'}); // 创建一个可下载的文件链接 const url = URL.createObjectURL(blob); // 创建一个隐藏的a标签,设置href属性为文件链接,download属性为文件名 const a = document.createElement('a'); a.href = url; a.download = file_name; a.style.display = 'none'; // 将a标签添加到文档中 document.body.appendChild(a); // 模拟点击a标签,开始下载文件 a.click(); // 移除a标签和释放URL对象 document.body.removeChild(a); URL.revokeObjectURL(url); } function findToolbarCommon() { return $("#root>div>div>div>ul").get(0); } async function insertButton(toolBar) { // 插入获取书签按钮 if($("#pb-get").length == 0 ){ toolBar.appendChild(toolBar.firstChild.cloneNode(true)); toolBar.lastChild.outerHTML = '
  • '; $("#pb-get").css("margin-top", "10px"); $("#pb-get").css("opacity", "0.8"); $("#pb-get").click(async function () { $("#pb-get").attr('disabled',true); await getAllBookmark(); console.log("bookmarks",bookmarks); console.log("WORKS",WORKS); //#TODO 很奇怪的是bookmarks.works比bookmarks total多,被删除的作品还会在bookmarks.works中只是不可访问了 DoLog(LogLevel.Info, `bookmarks total: ${bookmarks.total}个, 实际上bookmarks.works(包括失效作品): ${Object.keys(bookmarks.works).length}个`); // 保存书签信息 saveWORKS(WORKS,"works_info.json"); alert(`bookmarks total: ${bookmarks.total}个, 实际上bookmarks.works(包括失效作品): ${Object.keys(bookmarks.works).length}个`); $("#pb-get").attr('disabled',false); $("#pb-delete").css("display"," "); }); } // 插入删除按钮 if ($("#pb-delete").length == 0) { toolBar.appendChild(toolBar.firstChild.cloneNode(true)); toolBar.lastChild.outerHTML = '
  • '; $("#pb-delete").css("margin-top", "10px"); $("#pb-delete").css("opacity", "0.8"); // input是button的子元素。取消冒泡避免循环调用 $("#dir-input-delete").click((e)=>{ e.stopPropagation(); }); // 隐藏了input的样式由button触发input。 $("#pb-delete").click(function(event){ $("#dir-input-delete").click(); }); $("#dir-input-delete").change(async function(event){ let fs = event.target.files; // fIds是一个记录了要删除的图片id对象 let Ids = getFilesIds(fs); let originBookmarkTotal = bookmarks.total; await deleteBookmarkByIds(Ids); //加入延迟看看能不能解决这问题,可以,看来是访问太快服务器状态还没更新,不是同步问题 await new Promise(resolve => setTimeout(resolve,500)); //对比删除前后书签数 await getBookmark(0,10) DoLog(LogLevel.Info,"删除前书签数: "+originBookmarkTotal+"; 删除后书签数: "+bookmarks.total); alert("删除前书签数: "+originBookmarkTotal+"; 删除后书签数: "+bookmarks.total); }) } //获取书签svg // //删除图标svg //'' } function Load() { let toolBar = findToolbarCommon(); if (toolBar) { DoLog(LogLevel.Elements, toolBar); clearInterval(myloadInterval); } else { DoLog(LogLevel.Warning, "Get toolbar failed."); return; } getToken(); getUserId(); insertButton(toolBar); } myloadInterval = setInterval(Load, 1000);