Greasy Fork is available in English.

ImageDownloaderLib

Image downloader for manga download scripts.

2022-12-03 일자. 최신 버전을 확인하세요.

이 스크립트는 직접 설치해서 쓰는 게 아닙니다. 다른 스크립트가 메타 명령 // @require https://update.greasyfork.org/scripts/451810/1124333/ImageDownloaderLib.js(으)로 포함하여 쓰는 라이브러리입니다.

질문, 리뷰하거나, 이 스크립트를 신고하세요.
function ImageDownloader({
  getImagePromises,
  isOKToDownload = () => true,
  title = `package_${Date.now()}`,
  zipOptions = {},
  imageSuffix = 'jpg',
  cssVerticalDistance = 'top: 80px',
  cssHorizontalDistance = 'left: 80px'
}) {
  // create download button element
  const dlBtn = document.createElement('button');
  dlBtn.textContent = 'Download';
  dlBtn.style = `
    position: fixed;
    ${cssVerticalDistance};
    ${cssHorizontalDistance};
    z-index: 9999999;

    width: 128px;
    height: 48px;

    display: flex;
    justify-content: center;
    align-items: center;

    font-size: 14px;
    font-family: 'Consolas', 'Monaco', 'Microsoft YaHei';
    color: #fff;

    background-color: #0984e3;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  `;

  // setup click event callback
  dlBtn.onclick = function () {
    if (!isOKToDownload()) return;

    dlBtn.disabled = true;
    dlBtn.textContent = "Processing";
    dlBtn.style.backgroundColor = '#aaa';
    dlBtn.style.cursor = 'not-allowed';
    download(getImagePromises, title, zipOptions);
  }

  // add button to body
  document.body.appendChild(dlBtn);

  // download
  async function download(getImagePromises, title, zipOptions) {
    // get promises of images
    const imagePromises = getImagePromises();

    // setup progress updater
    setupProgressUpdater(imagePromises);

    // wait until all promises are resolved
    const images = await Promise.all(imagePromises);

    // create zip
    const zip = new JSZip(); // JSZip library should be imported already
    const folder = zip.folder(title);
    images.forEach((image, index) => {
      const filename = `${String(index + 1).padStart(images.length >= 100 ? String(images.length).length : 2, '0')}.${imageSuffix}`;
      folder.file(filename, image, zipOptions);
    });

    // pop up download window
    const content = await zip.generateAsync({ type: "blob" });
    saveAs(content, `${title}.zip`); // FileSaver library should be imported already

    // change the text of download button
    dlBtn.innerText = "Completed";
  }

  function setupProgressUpdater(imagePromises) {
    const timer = setInterval(() => {
      const statePromises = imagePromises.map(getPromiseState);
      Promise
        .all(statePromises)
        .then(states => {
          const fulfillCount = states.filter(state => state === 'fulfilled').length;
          dlBtn.textContent = `Processing\n(${fulfillCount}/${states.length})`;

          if (fulfillCount === states.length) {
            dlBtn.textContent = "Zipping";
            clearInterval(timer);
          }
        });
    }, 200);
  }

  async function getPromiseState(targetPromise) {
    const uniqueFlag = {};
    return Promise.race([targetPromise, uniqueFlag]).then(
      (raceResult) => raceResult === uniqueFlag ? 'pending' : 'fulfilled',
      () => 'rejected'
    );
  }
}