Kdocs batch download 金山文档批量下载

Download a directory in kdocs. 金山文档批量下载。

// ==UserScript==
// @name         Kdocs batch download 金山文档批量下载
// @namespace    http://tampermonkey.net/
// @version      2024-03-20
// @description  Download a directory in kdocs. 金山文档批量下载。
// @author       yusanshi
// @match        https://www.kdocs.cn/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=kdocs.cn
// @grant        none
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js
// @license      MIT
// ==/UserScript==

let zip;

async function dfs(folderID, paths) {
  // Recursively download all files in `folderID` to `paths`.
  await new Promise((r) => setTimeout(() => r(), 200));
  const groupID = window.location.pathname.split('/')[2];
  const response = await fetch(
    `https://drive.kdocs.cn/api/v5/groups/${groupID}/files?parentid=${folderID}&count=10000000`,
    { credentials: 'include' }
  ).then((response) => response.json());
  if (response.next_offset !== -1) {
    throw new Error('Error file count');
  }
  const files = response.files.map((e) => ({
    name: e.fname,
    id: e.id,
    type: e.ftype,
  }));

  for (const file of files) {
    await new Promise((r) => setTimeout(() => r(), 200));
    if (['file', 'sharefile'].includes(file.type)) {
      let url;
      try {
        const response = await fetch(
          `https://www.kdocs.cn/api/v3/office/file/${file.id}/download`
        ).then((response) => {
          if (!response.ok) {
            throw new Error(response.status);
          }
          return response.json();
        });
        url = response.url;
      } catch (error) {
        console.error(
          `Failed to download ${file.name} (${error.message}), skipped`
        );
        continue;
      }

      const filepath = [...paths, file.name].join('/');
      console.log(`Download ${filepath} at ${url}`);
      const blob = await fetch(url).then((response) => response.blob());
      zip.file(filepath, blob);
    } else if (['folder', 'linkfolder'].includes(file.type)) {
      await dfs(file.id, [...paths, file.name]);
    } else {
      console.error(`Unknown file type ${file.type}`);
    }
  }
}

window.downloadDirectory = async function () {
  document.querySelector('#downloadDirectory').innerText =
    'Downloading (see console for details)';
  zip = new JSZip();
  const folderID =
    new URLSearchParams(window.location.search).get('folderid') ||
    window.location.pathname.split('/')[3];
  if (!folderID) {
    throw new Error('Folder ID not found');
  }
  await dfs(folderID, []);
  zip.generateAsync({ type: 'blob' }).then(function (content) {
    const url = URL.createObjectURL(content);
    window.location.assign(url);
  });
  document.querySelector('#downloadDirectory').innerText = 'Download finished';
};

(function () {
  'use strict';

  document.body.insertAdjacentHTML(
    'beforeend',
    `<div style="
      position: fixed;
      bottom: 30px;
      right: 30px;
      z-index: 10;
    ">
    <button type="button" style="padding:8px;" id="downloadDirectory" onclick="downloadDirectory()">Download directory</button>
  </div>`
  );
})();