Greasy Fork is available in English.

GanmaDownloader

Manga downloader for ganma.jp

As of 18/12/2022. See the latest version.

// ==UserScript==
// @name         GanmaDownloader
// @namespace    https://github.com/Timesient/manga-download-scripts
// @version      0.3
// @license      GPL-3.0
// @author       Timesient
// @description  Manga downloader for ganma.jp
// @icon         https://ganma.jp/favicon.ico
// @homepageURL  https://greasyfork.org/zh-CN/scripts/451869-ganmadownloader
// @supportURL   https://github.com/Timesient/manga-download-scripts/issues
// @match        https://ganma.jp/*
// @require      https://unpkg.com/[email protected]/dist/axios.min.js
// @require      https://unpkg.com/[email protected]/dist/jszip.min.js
// @require      https://unpkg.com/[email protected]/dist/FileSaver.min.js
// @require      https://greasyfork.org/scripts/451810-imagedownloaderlib/code/ImageDownloaderLib.js?version=1129414
// @grant        GM_info
// ==/UserScript==

(async function(axios, JSZip, saveAs, ImageDownloader) {
  'use strict';

  // reload page when enter or leave chapter
  const re = /https:\/\/ganma\.jp\/(?<alias>.*)\/(?<episodeID>[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})\/\d+/;
  const oldHref = window.location.href;
  const timer = setInterval(() => {
    const newHref = window.location.href;
    if (re.exec(newHref) && re.exec(oldHref) && re.exec(newHref).groups.episodeID === re.exec(oldHref).groups.episodeID) return;
    if (re.test(newHref) || re.test(oldHref)) {
      clearInterval(timer);
      window.location.reload();
    }
  }, 200);

  // return if not reading chapter now
  if (!re.test(oldHref)) return;
  
  // get data of current episode
  const { alias, episodeID } = re.exec(oldHref).groups;
  const episodeData = await axios({
    method: 'GET',
    url: `https://ganma.jp/api/1.0/magazines/web/${alias}`,
    headers: { 'x-from': `https://ganma.jp/${alias}/${episodeID}/0` }
  }).then(res => res.data.root.items.filter(episodeData => episodeData.id === episodeID)[0]);

  // get title of current episode
  const title = `${episodeData.title} - ${episodeData.subtitle || ''}`;

  // get url of images
  const { token, baseUrl } = episodeData.page;
  const imageURLs = episodeData.page.files.map(file => `${baseUrl}${file}?${token}`);

  // append url of afterword image
  if (episodeData.afterwordImage && episodeData.afterwordImage.url) imageURLs.push(episodeData.afterwordImage.url);

  // setup ImageDownloader
  ImageDownloader.init({
    maxImageAmount: imageURLs.length,
    getImagePromises,
    title
  });

  // collect promises of image
  function getImagePromises(startNum, endNum) {
    return imageURLs
      .slice(startNum - 1, endNum)
      .map(url => axios
        .get(url, { responseType: 'arraybuffer' })
        .then(res => res.data)
        .then(ImageDownloader.fulfillHandler)
        .catch(ImageDownloader.rejectHandler)
      );
  }

})(axios, JSZip, saveAs, ImageDownloader);