Greasy Fork

Greasy Fork is available in English.

Pixiv Bookmark Batch-Delete

随手拼凑的代码,连样式也懒得整,用来删一下自己不喜欢的收藏,通过读取文件夹的图片id再按ID删除收藏;在收藏页可以操作:在右下角先点时钟按钮获取所有收藏的信息,再选择文件夹,读取文件夹的图片id删除收藏。

当前为 2021-01-30 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name                Pixiv Bookmark Batch-Delete
// @version             0.0.3
// @description   随手拼凑的代码,连样式也懒得整,用来删一下自己不喜欢的收藏,通过读取文件夹的图片id再按ID删除收藏;在收藏页可以操作:在右下角先点时钟按钮获取所有收藏的信息,再选择文件夹,读取文件夹的图片id删除收藏。
// @author              None
// @match               *://www.pixiv.net/users*
// @grant               none
// @compatible          Chrome
// @namespace http://greasyfork.icu/users/732531
// ==/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};
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;
  for(let i=0 ;i<works.length; ++i){
    bookmarks.works[works[i].id] = works[i].bookmarkData.id;
  }
}
async function getBookmark(offset, limit) {
  // !!注意修改语言参数
  return fetch(`https://www.pixiv.net/ajax/user/${userId}/illusts/bookmarks?tag=&offset=${offset}&limit=${limit}&rest=show&lang=zh`,
    {
      headers: {
        accept: "application/json",
        "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
        "sec-fetch-dest": "empty",
        "sec-fetch-mode": "cors",
        "sec-fetch-site": "same-origin",
        "x-user-id": userId,
      },
      referrer: `https://www.pixiv.net/users/${userId}/bookmarks/artworks`,
      referrerPolicy: "strict-origin-when-cross-origin",
      body: null,
      method: "GET",
      mode: "cors",
      credentials: "include",
    }
  )
    .then((response) => {
      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<total; i+=limit){
    p.push(getBookmark(i,limit));
  }
  return Promise.all(p);
}
// 暂不使用
// function saveBookmark(){
//   let a = document.querySelector("#dl");
//   let data={"a":"dsd", "b":"ds"};
//   let s = JSON.stringify(data);
//   let bb = new Blob([s]);
//   let myurl =  URL.createObjectURL(bb);
//   let name =  "test.json";
//   a.href = myurl;
//   a.download = name;
//   a.outerHTML = `<a id="dl" herf=${url} download=${name}></a>`;
// }
// 返回一个对象
function getDeleteFileIds(files){
  let ids = {};
  for(let i=0;i < files.length; ++i){
    let m = files[i].name.match(/^[0-9]+/);
    if(m){
      ids[m[0]] = true;
    }
  }
  return ids;
}

function deleteBookmarkBybookmarkId(bookmarkId){
  return fetch("https://www.pixiv.net/rpc/index.php", {
    "headers": {
      "accept": "application/json",
      "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
      "content-type": "application/x-www-form-urlencoded; charset=utf-8",
      "sec-fetch-dest": "empty",
      "sec-fetch-mode": "cors",
      "sec-fetch-site": "same-origin",
      "x-csrf-token": g_csrfToken
    },
    "referrer": `https://www.pixiv.net/users/${userId}/bookmarks/artworks`,
    "referrerPolicy": "strict-origin-when-cross-origin",
    "body": `mode=delete_illust_bookmark&bookmark_id=${bookmarkId}`,
    "method": "POST",
    "mode": "cors",
    "credentials": "include"
  }).then((response) =>{
    if (response.ok) {
      return response.json();
    } else {
      return Promise.reject({
        status: response.status,
        statusText: response.statusText,
      });
    }
  });
}
async function deleteBookmarkByIds(fIds){
  let p = [];
  let bid = [];
  let ids = Object.keys(fIds);
  let works = bookmarks.works;
  for (let i=0; i < ids.length; ++i){
    let bid = works[ids[i]]
    if(bid){
      p.push(deleteBookmarkBybookmarkId(bid))
    }
  }
  DoLog(LogLevel.Info,`有--${ids.length}--个文件提交删除,在书签中找到${p.length}个`);
  return Promise.all([p]);
}

function findToolbarCommon() {
  return $("#root>div>div>ul").get(0);
}
async function insertButton(toolBar) {
  // 插入获取书签按钮
  if($("#pb-get").length == 0 ){
    toolBar.appendChild(toolBar.firstChild.cloneNode(true));
    toolBar.lastChild.outerHTML =
      '<button id="pb-get" style="background-color: rgb(0, 0, 0);margin-top: 5px;opacity: 0.8;cursor: pointer;border: none;padding: 12px;border-radius: 24px;width: 48px;height: 48px;"><svg t="1611904366076" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2903" width="200" height="200"><path d="M512 74.666667C270.933333 74.666667 74.666667 270.933333 74.666667 512S270.933333 949.333333 512 949.333333 949.333333 753.066667 949.333333 512 753.066667 74.666667 512 74.666667z m0 810.666666c-204.8 0-373.333333-168.533333-373.333333-373.333333S307.2 138.666667 512 138.666667 885.333333 307.2 885.333333 512 716.8 885.333333 512 885.333333z" p-id="2904" fill="#ffffff"></path><path d="M695.466667 567.466667l-151.466667-70.4V277.333333c0-17.066667-14.933333-32-32-32s-32 14.933333-32 32v238.933334c0 12.8 6.4 23.466667 19.2 29.866666l170.666667 81.066667c4.266667 2.133333 8.533333 2.133333 12.8 2.133333 12.8 0 23.466667-6.4 29.866666-19.2 6.4-14.933333 0-34.133333-17.066666-42.666666z" p-id="2905" fill="#ffffff"></path></svg></button>';
    $("#pb-get").css("margin-top", "10px");
    $("#pb-get").css("opacity", "0.8");
    $("#pb-get").click(async function () {
      $("#pb-get").attr('disabled',true);
      await getAllBookmark();
      // DoLog(LogLevel.Info, `total: ${bookmarks.total}, actully got: ${Object.keys(bookmarks.works).length}`);
      alert(`total: ${bookmarks.total}, actully got: ${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 =
      '<button id="pb-delete" style="background-color: rgb(0, 0, 0);margin-top: 5px;opacity: 0.8;cursor: pointer;border: none;padding: 12px;border-radius: 24px;width: 48px;height: 48px;"><input type="file" id="dir-input" style="" webkitdirectory directory /><svg t="1611836610996" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3167" width="200" height="200"><path d="M874.666667 202.666667H360.533333c-21.333333 0-40.533333 8.533333-55.466666 23.466666l-217.6 234.666667c-25.6 27.733333-25.6 72.533333 0 100.266667l217.6 234.666666c14.933333 14.933333 34.133333 23.466667 55.466666 23.466667H874.666667c40.533333 0 74.666667-34.133333 74.666666-74.666667V277.333333c0-40.533333-34.133333-74.666667-74.666666-74.666666z m10.666666 544c0 6.4-4.266667 10.666667-10.666666 10.666666H360.533333c-2.133333 0-6.4-2.133333-8.533333-4.266666l-217.6-234.666667c-4.266667-4.266667-4.266667-10.666667 0-14.933333l217.6-234.666667c2.133333-2.133333 4.266667-4.266667 8.533333-4.266667H874.666667c6.4 0 10.666667 4.266667 10.666666 10.666667V746.666667z" p-id="3168" fill="#ffffff"></path><path d="M684.8 403.2c-12.8-12.8-32-12.8-44.8 0l-64 64-61.866667-61.866667c-12.8-12.8-32-12.8-44.8 0-12.8 12.8-12.8 32 0 44.8l61.866667 61.866667-61.866667 61.866667c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 14.933333 8.533333 23.466667 8.533333s17.066667-2.133333 23.466667-8.533333l61.866666-61.866667L640 618.666667c6.4 6.4 14.933333 8.533333 23.466667 8.533333s17.066667-2.133333 23.466666-8.533333c12.8-12.8 12.8-32 0-44.8L620.8 512l61.866667-61.866667c12.8-12.8 12.8-34.133333 2.133333-46.933333z" p-id="3169" fill="#ffffff"></path></svg></button>';
    $("#pb-delete").css("margin-top", "10px");
    $("#pb-delete").css("opacity", "0.8");
    $("#dir-input").change(async function(event){
      let fs = event.target.files;
      // fIds是一个记录了要删除的图片id对象
      let fIds = getDeleteFileIds(fs);
      let originBookmarkTotal = bookmarks.total;
      await deleteBookmarkByIds(fIds);
      //对比删除前后书签数
      await getBookmark(0,10);
      alert(`删除前书签数: ${originBookmarkTotal}; 删除后书签数: ${bookmarks.total}`);
    })
    // $("#pb-delete").click(function () {
    //   $("#dir-input").click();
    // });
  }

  //获取书签svg
  // <svg t="1611904366076" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2903" width="200" height="200"><path d="M512 74.666667C270.933333 74.666667 74.666667 270.933333 74.666667 512S270.933333 949.333333 512 949.333333 949.333333 753.066667 949.333333 512 753.066667 74.666667 512 74.666667z m0 810.666666c-204.8 0-373.333333-168.533333-373.333333-373.333333S307.2 138.666667 512 138.666667 885.333333 307.2 885.333333 512 716.8 885.333333 512 885.333333z" p-id="2904" fill="#ffffff"></path><path d="M695.466667 567.466667l-151.466667-70.4V277.333333c0-17.066667-14.933333-32-32-32s-32 14.933333-32 32v238.933334c0 12.8 6.4 23.466667 19.2 29.866666l170.666667 81.066667c4.266667 2.133333 8.533333 2.133333 12.8 2.133333 12.8 0 23.466667-6.4 29.866666-19.2 6.4-14.933333 0-34.133333-17.066666-42.666666z" p-id="2905" fill="#ffffff"></path></svg>
  //删除图标svg
  //'<svg t="1611836610996" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3167" width="200" height="200"><path d="M874.666667 202.666667H360.533333c-21.333333 0-40.533333 8.533333-55.466666 23.466666l-217.6 234.666667c-25.6 27.733333-25.6 72.533333 0 100.266667l217.6 234.666666c14.933333 14.933333 34.133333 23.466667 55.466666 23.466667H874.666667c40.533333 0 74.666667-34.133333 74.666666-74.666667V277.333333c0-40.533333-34.133333-74.666667-74.666666-74.666666z m10.666666 544c0 6.4-4.266667 10.666667-10.666666 10.666666H360.533333c-2.133333 0-6.4-2.133333-8.533333-4.266666l-217.6-234.666667c-4.266667-4.266667-4.266667-10.666667 0-14.933333l217.6-234.666667c2.133333-2.133333 4.266667-4.266667 8.533333-4.266667H874.666667c6.4 0 10.666667 4.266667 10.666666 10.666667V746.666667z" p-id="3168" fill="#ffffff"></path><path d="M684.8 403.2c-12.8-12.8-32-12.8-44.8 0l-64 64-61.866667-61.866667c-12.8-12.8-32-12.8-44.8 0-12.8 12.8-12.8 32 0 44.8l61.866667 61.866667-61.866667 61.866667c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 14.933333 8.533333 23.466667 8.533333s17.066667-2.133333 23.466667-8.533333l61.866666-61.866667L640 618.666667c6.4 6.4 14.933333 8.533333 23.466667 8.533333s17.066667-2.133333 23.466666-8.533333c12.8-12.8 12.8-32 0-44.8L620.8 512l61.866667-61.866667c12.8-12.8 12.8-34.133333 2.133333-46.933333z" p-id="3169" fill="#ffffff"></path></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);