Greasy Fork

Greasy Fork is available in English.

Pixiv 小说下载/小说系列打包下载

下载小说和小说系列

目前为 2024-07-29 提交的版本,查看 最新版本

// ==UserScript==
// @name        Pixiv 小说下载/小说系列打包下载
// @namespace   https://pixiv.net/
// @version     0.1
// @author      huyaoi
// @description 下载小说和小说系列
// @match       https://www.pixiv.net/novel/show.php?id=*
// @match       https://www.pixiv.net/novel/series/*
// @icon        data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant       GM_registerMenuCommand
// @license     MIT
// @require     https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js
// ==/UserScript==

(function() {
    'use strict';
    const apiEndpoint = "https://www.pixiv.net/ajax";

    async function fetchJson(url) {
        return await fetch(url).then(result => result.json());
    }

    function GetQueryValue(queryName) {
        let query = decodeURI(window.location.search.substring(1));
        let vars = query.split("&");
        for (let i = 0; i < vars.length; i++) {
            let pair = vars[i].split("=");
            if (pair[0] === queryName) { return pair[1]; }
        }
        return null;
    }

    function CreateHeader(data){
        //替换掉","和"/"
        let tags = data.tags.tags.map(tag => tag.tag).join(", ");
        tags = tags.replaceAll(",",", ");
        tags = tags.replaceAll("/",", ");
        return `id: ${data.id}
user: ${data.userName} [${data.userId}]
title: ${data.title}
lang: ${data.language}
tags: ${tags}
count: ${data.characterCount}
description: ${data.description}
create: ${data.createDate}
update: ${data.uploadDate}
content:
${data.content}
`
    }

    function DownloadFile(content,filename){
        const blob = new Blob([content]);
        const t = document.createElement('a');
        const href = URL.createObjectURL(blob);
        t.setAttribute('href', href);
        t.setAttribute('download', filename);
        t.click();
        window.URL.revokeObjectURL(href);
    }

    async function DownloadNovel(){
        if(GetQueryValue("id") == null){
            alert("当前页面不是小说页面!");
            return;
        }
        let novelID = GetQueryValue("id");

        const data = await GetNovel(novelID);
        if(data != null){
            let Content = CreateHeader(data);
            DownloadFile(Content,`${data.title}_${data.id}.txt`);
        }
    }

    async function GetNovel(novelID){
        let url = apiEndpoint + `/novel/${novelID}`;

        return await fetchJson(url).then(data => {
            return data.body;
        })
        .catch(err => {
            console.log("获取失败");
            console.log(err);
            return null;
        });
    }

    async function GetSeriesContent(id,last){
        let contentUrl = apiEndpoint + `/novel/series_content/${id}?limit=30&last_order=${last}&order_by=asc`;
        return await fetchJson(contentUrl).then(data => {
            return data.body;
        })
        .catch(err => {
            console.log("获取失败");
            console.log(err);
            return null;
        });
    }

    async function DownloadSeries(){
        let tmp = window.location.href.split('/series/');
        if(tmp.length != 2){
            alert("当前页面不是小说系列页面!");
            return;
        }
        let seriesID = tmp[1];

        let novelInfoUrl = apiEndpoint + `/novel/series/${seriesID}`;
        let displaySeriesContentCount = 0;
        let title = "";

        await fetchJson(novelInfoUrl).then(data => {
            displaySeriesContentCount = data.body.displaySeriesContentCount || 0;
            title = data.body.title;
        })
        .catch(err => {
            console.log("获取失败");
            console.log(err);
            return;
        });

        console.log(displaySeriesContentCount);
        if(displaySeriesContentCount == 0){
            return;
        }

        let zip = new JSZip();
        let index = 0;

        let maxPage = Math.ceil(displaySeriesContentCount/30);
        for(let o = 0;o < maxPage;o++){
            let data = await GetSeriesContent(seriesID,o * 30);
            if(data!=null){
                data.page.seriesContents.forEach(async(item) => {
                    let novel = await GetNovel(item.id);
                    if(novel != null){
                        let Content = CreateHeader(novel);
                        await zip.file(`${seriesID}_${index}_${novel.id}_${novel.title}.txt`, Content);
                        index++;
                        if(index >= displaySeriesContentCount){
                            console.log("Start");
                            DownloadFile(zip.generate({type:"blob"}), `${seriesID}_${title}.zip`);
                        }
                    }
                });
            }
        }
    }

    GM_registerMenuCommand("下载小说", () => DownloadNovel());
    GM_registerMenuCommand("下载小说系列", () => DownloadSeries());
})();