Pixiv作品熱門程度排序與篩選器

在追蹤繪師作品、繪師作品、標籤作品頁面中以按讚數進行排序,並僅顯示高於閾值的作品。

Установить этот скрипт?
Рекомендуемый автором скрипт

Вам также может понравится 按鍵與滑鼠滾輪翻頁器.

Установить этот скрипт
// ==UserScript==
// @name         Pixiv作品熱門程度排序與篩選器
// @name:ja      Pixiv作品人気度ソート&フィルター
// @name:en      Pixiv Illustration Popularity Sorter and Filter
// @namespace    https://github.com/Max46656
// @description  在追蹤繪師作品、繪師作品、標籤作品頁面中以按讚數進行排序,並僅顯示高於閾值的作品。
// @description:ja  フォローアーティスト作品、アーティスト作品、タグ作品ページで、いいね數でソートし、閾値以上の作品のみを表示します。
// @description:en  Sort Illustration by likes and display only those above the threshold on followed artist illustrations, artist illustrations, and tag illustrations pages.
// @namespace    https://github.com/Max46656
// @version      1.7.6
// @author       Max
// @match        https://www.pixiv.net/bookmark_new_illust.php*
// @match        https://www.pixiv.net/users/*
// @match        https://www.pixiv.net/tags/*
// @match        https://www.pixiv.net/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=pixiv.net
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @license MPL2.0
// ==/UserScript==
/* TODO
*提示文字多語言翻譯

*/
class pageStrategy {
    getThumbnailClass() {}
    getArtsClass() {}
    getRenderArtWallClass() {}
    getButtonAtClass() {}
    getAllButtonClass() {}
    getArtsCountClass(){}
}

class userStrategy extends pageStrategy{
    getThumbnailClass() {
        return 'li.sc-9y4be5-2 img'
    }
    getArtsClass() {
        return 'div.cDZIoX li';
    }
    getRenderArtWallClass() {
        return 'div.cDZIoX';
    }
    getOutArtWallWayClass(){
        return '.sc-1nr368f-4.sc-1xj6el2-3.ggHNyV.CAStc';
    }
    getButtonAtClass() {
        return 'nav.kWAFb';
    }
    getAllButtonClass() {
        return ['jZgOUq','kmjYsK','jYyUpu','iUWhhQ'];
    }
    getArtsCountClass(){
        return 'div.sc-7zddlj-2 span';
    }
}

class tagsStrategy extends pageStrategy{
    getThumbnailClass() {
        return 'div.fxGVAF a.fGjAxR img'
    }
    getArtsClass() {
        return 'div.juyBTC div.ggHNyV li';
    }
    getRenderArtWallClass() {
        return 'ul.hdRpMN';
        // return 'div.ggHNyV:has(ul.hdRpMN)';
    }
    getOutArtWallWayClass(){
        return 'div.ggHNyV:has(table)';
    }
    getButtonAtClass() {
        return 'div.laRMNP div.hbGpVM';
    }
    getAllButtonClass() {
        return ['BSrHG','eGjXJv','iknzpl'];
    }
    getArtsCountClass(){
        return 'span.sc-1pt8s3a-10';
    }
}

class subStrategy extends pageStrategy{
    getThumbnailClass() {
        return 'div.fxGVAF a.fGjAxR img'
    }
    getArtsClass() {
        return 'ul.jtUPOE li';
    }
    getRenderArtWallClass() {
        return 'div.cwAKCq';
        // return 'div.ggHNyV:has(ul.hdRpMN)';
    }
    getOutArtWallWayClass(){
        return 'div.FIoEP';
    }
    getButtonAtClass() {
        return 'div.hbGpVM';
    }
    getAllButtonClass() {
        return ['hDldyK','iknzpl'];
    }
    getArtsCountClass(){
        return null;
    }
}

class artScraper {
    constructor(targetPages,likesMinLimit) {
        this.domain = 'https://www.pixiv.net';
        this.allArts = [];
        this.allArtsWithoutLike = [];
        this.targetPages = GM_getValue("targetPages", 10) || targetPages;
        this.likesMinLimit=GM_getValue("likesMinLimit", 50) || likesMinLimit;
        this.strategy = this.setStrategy();
        this.currentArtCount=0;
        // console.log(strategy.getThumbnailClass(),strategy.getArtsClass(),strategy.getRenderArtWallClass(),strategy.getButtonAtClass(),strategy.getAllButtonClass())
    }
    setStrategy(){
        const url = self.location.href;
        if (url.includes('https://www.pixiv.net/bookmark_new_illust')) {
            return new subStrategy();
        } else if (url.match(/^https:\/\/www\.pixiv\.net\/(en\/users|users)\/.*\/.*$/)) {
            return new userStrategy();
        } else if (url.match(/^https:\/\/www\.pixiv\.net\/(en\/tags|tags)\/.*\/.*$/)) {
            return new tagsStrategy();
        } else {
            console.error('Unsupported page type');
        }
    }

    async eatAllArts() {
        const startTime = performance.now();

        await this.executeAndcountUpSec('readingPages', () => this.readingPages(this.strategy.getThumbnailClass(), this.strategy.getArtsClass()));
        await this.executeAndcountUpSec('sortArts', this.sortArts.bind(this));
        let renderArtWallAtClass = this.strategy.getRenderArtWallClass();
        await this.executeAndcountUpSec('renderArtWall', () => this.renderArtWall(renderArtWallAtClass));
        //this.changeElementClassName(document.querySelector(this.strategy.getOutArtWallWayClass()),'sortArtWall');
        let buttonAtClass = this.strategy.getButtonAtClass();
        //this.addRestoreButton(buttonAtClass, this.strategy.getAllButtonClass());
        this.addRerenderButton(renderArtWallAtClass, buttonAtClass, this.strategy.getAllButtonClass());

        const endTime = performance.now();
        console.log(`總耗時: ${(endTime - startTime) / 1000} 秒`);
    }

    async getElementBySelector(selector) {
        let elements = document.querySelectorAll(selector);
        while (elements.length === 0) {
            await this.delay(50);
            elements = document.querySelectorAll(selector);
            //console.log("selector",selector,"找不到,將重試")
        }
        return elements[0];
    }

      async getElementListBySelector(selector) {
        let elements = document.querySelectorAll(selector);
        while (elements.length === 0) {
            await this.delay(50);
            elements = document.querySelectorAll(selector);
            //console.log("selector",selector,"找不到,將重試")
        }
        return elements;
    }

    async readingPages(thumbnailClass, artsClass) {
        const startTime = performance.now();
        if(document.getElementById("RerenderButton")){
            this.toNextPage();
        }
        for (let i = 0; i <= this.targetPages; i++) {
            const iterationStartTime = performance.now();

            await this.getArtsInPage(thumbnailClass, artsClass);

            // 最後一頁的下一頁按鈕為隱藏
            let allPageNav = document.querySelectorAll('a:has(polyline[points="1,2 5,6 9,2"]');
            if (allPageNav[allPageNav.length-1].hasAttribute("hidden")) {
                console.log("已經至最後一頁");
                break;
            }
            let takeALook = Math.floor(Math.random() * 10) + 30;
            let waitTime = Math.floor(Math.random() * 3000) + 2000;

            if(i > 400 && i % takeALook * 10 == 0){
                console.log("請等待API冷卻時間");
                await this.delay(waitTime * 10);
            }else if(i > 40 && i % takeALook == 0){
                console.log("請等待API冷卻時間");
                await this.delay(waitTime);
            }

            if(this.allArtsWithoutLike.length>=800){
                while(this.allArtsWithoutLike.length != 0){
                    try{
                        await this.executeAndcountUpSec('appendLikeElementToAllArts',()=>this.appendLikeElementToAllArts());
                    }catch (e){
                        console.log("請等待API冷卻時間");
                        await this.delay(waitTime);
                    }
                }
            }


            if (i < this.targetPages - 1) {
                await this.toNextPage();
            }

            const iterationEndTime = performance.now();
        }

        while(this.allArtsWithoutLike.length != 0){
            try{
                await this.executeAndcountUpSec('appendLikeElementToAllArts',()=>this.appendLikeElementToAllArts());
            }catch (e){
                console.log("請等待API冷卻時間");
                await this.delay(Math.floor(Math.random() * 1000));
            }
        }
    }

    async getArtsInPage(thumbnailClass, artsClass) {
        let pageStandard = await this.getElementListBySelector(artsClass);
        pageStandard = pageStandard.length - 1;
        let thumbnailCount = 0;

        while (thumbnailCount < pageStandard) {
            const thumbnails = await this.getElementListBySelector(thumbnailClass);
            thumbnailCount = thumbnails.length;
            if (thumbnailCount < pageStandard) {
                console.log(`缺少${pageStandard - thumbnailCount}張圖片,請關閉開發者工具且保持視窗在本分頁以確保所有圖片都載入`);
                window.scrollBy(0, window.innerHeight);
                await this.delay(100);
                //滑到頁面底部
                if(window.innerHeight + window.scrollY >= document.documentElement.scrollHeight){
                    window.scrollTo(0, 0);
                    pageStandard = await this.getElementListBySelector(artsClass);
                    pageStandard = pageStandard.length - 1;
                }
            }
        }

        const arts = await this.getElementListBySelector(artsClass);
        console.log(`找到${arts.length}張圖片,開始抓取圖片`);

        for (let art of arts) {
            this.allArtsWithoutLike.push(art);
        }
    }

    async appendLikeElementToAllArts() {
        this.allArtsWithoutLike = this.allArtsWithoutLike.filter(art => art !== undefined);
        const ids = this.allArtsWithoutLike.filter(art => art.getElementsByTagName('a')[0] !== undefined)
        .map(art => {
            const href = art.getElementsByTagName('a')[0].getAttribute('href');
            return href.match(/\/(\d+)/)[1];
        });

        const likeCounts = await Promise.all(ids.map(id => this.fetchLikeCount(id)));

        likeCounts.forEach((likeCount, index) => {
            const art = this.allArtsWithoutLike[index];
            if (!art.getElementsByClassName('likes').length) {// 檢查是否已經有處理過
                const referenceElement = art.getElementsByTagName('div')[0];
                if (referenceElement) {
                    const likeCountElement = document.createElement('span');
                    likeCountElement.textContent = `${likeCount}`;
                    likeCountElement.className = 'likes';
                    likeCountElement.style.cssText =
                    'text-align: center !important; padding-bottom: 20px !important; color: #0069b1 !important; font-size: 12px !important; font-weight: bold !important; text-decoration: none !important; background-color: #cef !important; background-image: url("data:image/svg+xml;charset=utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%2210%22 height=%2210%22 viewBox=%220 0 12 12%22><path fill=%22%230069B1%22 d=%22M9,1 C10.6568542,1 12,2.34314575 12,4 C12,6.70659075 10.1749287,9.18504759 6.52478604,11.4353705 L6.52478518,11.4353691 C6.20304221,11.6337245 5.79695454,11.6337245 5.4752116,11.4353691 C1.82507053,9.18504652 0,6.70659017 0,4 C1.1324993e-16,2.34314575 1.34314575,1 3,1 C4.12649824,1 5.33911281,1.85202454 6,2.91822994 C6.66088719,1.85202454 7.87350176,1 9,1 Z%22/></svg>") !important; background-position: center left 6px !important; background-repeat: no-repeat !important; padding: 3px 6px 3px 18px !important; border-radius: 3px !important;';
                    referenceElement.appendChild(likeCountElement);
                }
                this.allArts.push({ art, likeCount });
            }
        });

        this.allArtsWithoutLike = [];
    }

    async toNextPage() {
        let pageButtonsShape='a:has(polyline[points="1,2 5,6 9,2"])';
        const pageButtons = document.querySelectorAll(pageButtonsShape);
        let nextPageButton = pageButtons[pageButtons.length - 1];
        nextPageButton.click();
    }

    async toPervPage() {
        let pageButtonsClass='a.sc-d98f2c-0.sc-xhhh7v-2.cCkJiq.sc-xhhh7v-1-filterProps-Styled-Component.kKBslM';
        const pageButtons = document.querySelectorAll(pageButtonsClass);
        let nextPageButton = pageButtons[0];
        nextPageButton.click();
    }

    async fetchLikeCount(id) {
        const response = await fetch(`https://www.pixiv.net/ajax/illust/${id}`, { credentials: 'omit' });
        const json = await response.json();
        return json.body.likeCount;
    }

    async sortArts() {
        // 使用 Map 來儲存每個 img src 對應的 art 元素
        const artMap = new Map();

        // 依據 likeCount 進行排序,並且過濾掉重複的 art
        this.allArts
            .sort((a, b) => b.likeCount - a.likeCount) // 依據 likeCount 排序
            .forEach(({ art }) => {
            if (art) {
                const imgSrc = art.getElementsByTagName("img")[0];
                if (imgSrc && !artMap.has(imgSrc)) {
                    // 如果 img src 不在 artMap 中,則將 art 存入 artMap
                    artMap.set(imgSrc, art);
                }
            }
        });

        // 更新 allArts 為排除重複的 art 元素列表
        this.allArts = Array.from(artMap.values());
    }


    async renderArtWall(renderArtWallAtClass) {
        const parentElement = await this.getElementBySelector(renderArtWallAtClass);
        this.clearElement(parentElement);

        const table = document.createElement('table');
        table.style.cssText = 'width: 1223px; overflow-y: auto; margin: 0 auto;';
        table.classList.add('TableArtWall');

        const fragment = document.createDocumentFragment();
        fragment.appendChild(table);

        let tr = document.createElement('tr');
        table.appendChild(tr);
        let row = GM_getValue("rowsOfArtsWall", 7);

        let artCount = 0; // 計算繪畫數量

        for (let art of this.allArts) {
            if (art.getElementsByClassName('likes')[0].textContent >= this.likesMinLimit) {
                const td = document.createElement('td');

                Array.from(art.attributes).forEach(attr => {
                    td.setAttribute(attr.name, attr.value);
                });

                td.innerHTML = art.innerHTML;
                tr.appendChild(td);
                artCount++; // 增加繪畫數量

                if (tr.children.length % row === 0) {
                    tr = document.createElement('tr');
                    table.appendChild(tr);
                }
            }
        }
        parentElement.appendChild(fragment);
        this.currentArtCount = artCount;
    }

    // 縮圖換原圖,以其他腳本獨立解決
    /*async changeThumbToOriginal() {
        for (const element of this.allArts) {
            const img = element.getElementsByTagName('img')[0];
            if (img) {
                const originalSrc = img.src.replace(/\/c\/\d+x\d+_\d+/, '')
                .replace('/img-master/', '/img-original/')
                .replace('/custom-thumb/', '/img-original/')
                .replace(/_square1200/, '')
                .replace(/_custom1200/, '');

                //console.log(originalSrc);
                const newSrc = await this.testImageSrc(originalSrc);
                img.src = newSrc;
                //console.log(img.src);
            }
        }
    }
    async testImageSrc(src) {
        return new Promise(resolve => {
            const img = new Image();
            img.onload = function() {
                resolve(src);
            };
            img.onerror = function() {
                resolve(src.replace('.jpg', '.png'));
            };
            img.src = src;
        });
    }*/

    //     搜尋樣式
    /*     async addStartButton() {
        let startButtonParentClass = '.sc-s8zj3z-5.eyagzq';
        let startButtonClass = 'lkjHVk';
        const parentElement = document.querySelector(startButtonParentClass);
        if (!parentElement) {
            await this.delay(50);
            await this.addStartButton();
            return;
        }

        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.alignItems = 'center';
        buttonContainer.className = 'startButton';
        buttonContainer.innerHTML= '<div class="hxckiU"><form class="ahao-search"><div class="hjxNtZ"><div class="bbSVxZ"></div><div class="dlaIss"><div class="lclerM"><svg viewBox="0 0 16 16" size="16" class="fiLugu"><path d="M8.25739 9.1716C7.46696 9.69512 6.51908 10 5.5 10C2.73858 10 0.5 7.76142 0.5 5C0.5 2.23858 2.73858 0 5.5 0C8.26142 0 10.5 2.23858 10.5 5C10.5 6.01908 10.1951 6.96696 9.67161 7.75739L11.7071 9.79288C12.0976 10.1834 12.0976 10.8166 11.7071 11.2071C11.3166 11.5976 10.6834 11.5976 10.2929 11.2071L8.25739 9.1716ZM8.5 5C8.5 6.65685 7.15685 8 5.5 8C3.84315 8 2.5 6.65685 2.5 5C2.5 3.34315 3.84315 2 5.5 2C7.15685 2 8.5 3.34315 8.5 5Z" transform="translate(3 3)" fill-rule="evenodd" clip-rule="evenodd"></path></svg></div></div></div></form><div class="kFcBON"></div></div>';

        const inputField = document.createElement('input');
        inputField.type = 'text';
        inputField.value = this.targetPages;
        inputField.className = 'gSIBXG';
        inputField.addEventListener('input', (event) => {
            this.targetPages = event.target.value;
        });

        const start = document.createElement('button');
        start.textContent = 'Sort';
        start.style.marginRight = '-10px';
        start.className = startButtonClass;
        start.addEventListener('click', async () => {
            await this.eatAllArts();
        });
        const startButton = document.createElement('button');
        startButton.textContent = 'Page Go';
        startButton.className = startButtonClass;
        startButton.addEventListener('click', async () => {
            await this.eatAllArts();
        });

        buttonContainer.appendChild(start);
        buttonContainer.appendChild(inputField);
        buttonContainer.appendChild(startButton);

        parentElement.appendChild(buttonContainer);
    } */

    // 拉桿樣式
    async addStartButton(ParentClass,buttonClass) {
        if(document.getElementById("StartButton")){
            return;
        }

        const buttonContainer = document.createElement('nav');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.alignItems = 'center';
        buttonContainer.id = 'myScriptButtonContainer';

        const startButton = document.createElement('button');
        this.addLikeRangeInput(buttonContainer,startButton);
        await this.addPageRangeInput(buttonContainer,startButton);

        startButton.textContent = `likes: ${this.likesMinLimit} for ${this.targetPages}Page Go!`;
        buttonClass.forEach(cls => startButton.classList.add(cls));
        startButton.id = "StartButton";
        startButton.addEventListener('click', async () => {
            GM_setValue("targetPages", this.targetPages);
            GM_setValue("likesMinLimit", this.likesMinLimit);
            await this.eatAllArts();
            startButton.textContent = `likes: ${this.likesMinLimit} for ${this.targetPages}Page Go!`;
        });

        const parentElement = await this.getElementBySelector(ParentClass);
        buttonContainer.appendChild(startButton);
        parentElement.appendChild(buttonContainer);
    }

    async addRerenderButton(renderArtWallAtClass, ParentClass, buttonClass) {
        if(document.getElementById("RerenderButton")){
            document.getElementById("RerenderButton").textContent = `likes: ${this.likesMinLimit} Rerender Go! now:${this.currentArtCount}(${Math.round(this.currentArtCount/this.allArts.length *100)}%)`;
            return;
        }
        document.querySelector("nav#myScriptButtonContainer input[id=LikeRangeInput]").style.display="none";
        document.getElementById("LikeIcon").style.display="none";
        await this.delay(0);
        document.getElementById("StartButton").textContent = 'bug? you can try again.';

        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.alignItems = 'center';

        const rerenderButton = document.createElement('button');
        rerenderButton.textContent = `likes: ${this.likesMinLimit} Rerender Go! now:${this.currentArtCount}(${Math.round(this.currentArtCount/this.allArts.length *100)}%)`; // 顯示目前繪畫數量
        buttonClass.forEach(cls => rerenderButton.classList.add(cls));
        rerenderButton.id = "RerenderButton";
        rerenderButton.addEventListener('click', async () => {
            GM_setValue("likesMinLimit", this.likesMinLimit);
            await this.renderArtWall(renderArtWallAtClass);
            rerenderButton.textContent = `likes: ${this.likesMinLimit} Rerender Go! now:${this.currentArtCount}(${Math.round(this.currentArtCount/this.allArts.length *100)}%)`; // 更新繪畫數量
        });

        this.addLikeRangeInput(buttonContainer, rerenderButton);

        const parentElement = await this.getElementBySelector(ParentClass);
        buttonContainer.appendChild(rerenderButton);
        parentElement.appendChild(buttonContainer);
    }

    addLikeRangeInput(container,Button) {
        const likesMinLimitsRange = [0, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 7500, 10000];

        const likeIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        likeIcon.id = "LikeIcon";
        likeIcon.setAttributeNS(null, "viewBox", "0 0 32 32");
        likeIcon.setAttributeNS(null, "height", "16");
        likeIcon.setAttributeNS(null, "width", "16");
        likeIcon.classList.add("dxYRhf", "fiLugu");

        const path1 = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path1.setAttributeNS(null, "d", "M21,5.5 C24.8659932,5.5 28,8.63400675 28,12.5 C28,18.2694439 24.2975093,23.1517313 17.2206059,27.1100183 C16.4622493,27.5342993 15.5379984,27.5343235 14.779626,27.110148 C7.70250208,23.1517462 4,18.2694529 4,12.5 C4,8.63400691 7.13400681,5.5 11,5.5 C12.829814,5.5 14.6210123,6.4144028 16,7.8282366 C17.3789877,6.4144028 19.170186,5.5 21,5.5 Z");

        const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path2.setAttributeNS(null, "d", "M16,11.3317089 C15.0857201,9.28334665 13.0491506,7.5 11,7.5 C8.23857625,7.5 6,9.73857647 6,12.5 C6,17.4386065 9.2519779,21.7268174 15.7559337,25.3646328 C15.9076021,25.4494645 16.092439,25.4494644 16.2441073,25.3646326 C22.7480325,21.7268037 26,17.4385986 26,12.5 C26,9.73857625 23.7614237,7.5 21,7.5 C18.9508494,7.5 16.9142799,9.28334665 16,11.3317089 Z");
        path2.setAttributeNS(null, "class", "sc-j89e3c-0 dUurgf");

        likeIcon.appendChild(path1);
        likeIcon.appendChild(path2);

        const likeRangeInput = document.createElement('input');
        likeRangeInput.type = 'range';
        likeRangeInput.min = '0';
        likeRangeInput.max = (likesMinLimitsRange.length - 1).toString();
        likeRangeInput.value = likesMinLimitsRange.indexOf(this.likesMinLimit);
        likeRangeInput.style.marginRight = '10px';
        likeRangeInput.style.backgroundColor = 'red';
        likeRangeInput.id="LikeRangeInput";
        // reRender
        if(document.querySelector('.TableArtWall')){
            likeRangeInput.addEventListener('input', (event) => {
                this.likesMinLimit = likesMinLimitsRange[event.target.value];
                Button.textContent = `likes: ${this.likesMinLimit} Rerender Go! now:${this.currentArtCount}(${Math.round(this.currentArtCount/this.allArts.length *100)}%)`;
            });
        }else{
            likeRangeInput.addEventListener('input', (event) => {
                this.likesMinLimit = likesMinLimitsRange[event.target.value];
                Button.textContent = `likes: ${this.likesMinLimit} for ${this.targetPages}Page Go!`;
            });
        }
        container.appendChild(likeIcon);
        container.appendChild(likeRangeInput);
    }

    async addPageRangeInput(container, startButton) {
        const pageIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        pageIcon.setAttributeNS(null, "viewBox", "0 0 16 16");
        pageIcon.setAttributeNS(null, "height", "16");
        pageIcon.setAttributeNS(null, "width", "16");
        pageIcon.classList.add("pageInput", "fiLugu");

        const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path.setAttributeNS(null, "d", "M8.25739 9.1716C7.46696 9.69512 6.51908 10 5.5 10C2.73858 10 0.5 7.76142 0.5 5C0.5 2.23858 2.73858 0 5.5 0C8.26142 0 10.5 2.23858 10.5 5C10.5 6.01908 10.1951 6.96696 9.67161 7.75739L11.7071 9.79288C12.0976 10.1834 12.0976 10.8166 11.7071 11.2071C11.3166 11.5976 10.6834 11.5976 10.2929 11.2071L8.25739 9.1716ZM8.5 5C8.5 6.65685 7.15685 8 5.5 8C3.84315 8 2.5 6.65685 2.5 5C2.5 3.34315 3.84315 2 5.5 2C7.15685 2 8.5 3.34315 8.5 5Z");
        path.setAttributeNS(null, "transform", "translate(3 3)");
        path.setAttributeNS(null, "fill-rule", "evenodd");
        path.setAttributeNS(null, "clip-rule", "evenodd");

        pageIcon.appendChild(path);

        const pageRangeInput = document.createElement('input');
        pageRangeInput.type = 'range';
        pageRangeInput.min = '1';

        let max = await this.getMaxPage();
        const stepSize = Math.floor(max / 24);

        //console.log(this.getMaxPage());
        pageRangeInput.max = max || 34;

        if (this.targetPages > max) {
            pageRangeInput.value = max;
            this.targetPages=max;
        } else {
            pageRangeInput.value = this.targetPages;
        }

        pageRangeInput.step = stepSize;
        pageRangeInput.style.marginRight = '10px';
        pageRangeInput.classList.add('pageInput');
        pageRangeInput.addEventListener('input', (event) => {
            this.targetPages = event.target.value;
            startButton.textContent = `likes: ${this.likesMinLimit} for ${this.targetPages}Page Go!`;
        });
        container.appendChild(pageIcon);
        container.appendChild(pageRangeInput);
        if(max>50){
            const pageInputBox = document.createElement('input');
            pageInputBox.type = 'number';
            pageInputBox.min = '1';
            pageInputBox.max = max || 34;
            pageInputBox.classList.add("gSIBXG");
            pageInputBox.value = this.targetPages;
            pageInputBox.style.width = '50px';
            pageInputBox.style.marginRight = '10px';
            pageInputBox.addEventListener('input', (event) => {
                const value = parseInt(event.target.value);
                if (value >= 1 && value <= max) {
                    this.targetPages = value;
                    startButton.textContent = `likes: ${this.likesMinLimit} for ${this.targetPages}Page Go!`;
                }
            });
            container.appendChild(pageInputBox);
        }
    }

    async getMaxPage() {
        if (this.strategy.getArtsCountClass() === null) {
            return 34;
        }
        const artsCountElement = await this.getElementBySelector(this.strategy.getArtsCountClass());
        //console.log(artsCountElement);
        if (artsCountElement) {
            // 刪除數字中的逗號
            const artsCountText = artsCountElement.textContent.replace(/,/g, '');
            const artsCount = parseInt(artsCountText);
            const arts = await this.getElementListBySelector(this.strategy.getArtsClass());
            const artsPerPage = arts.length;
            const maxPage = Math.ceil(artsCount / artsPerPage);
            //console.log("artsPerPage", artsPerPage, 'artsCount', artsCount);
            return maxPage;
        } else {
            return 34;
        }
    }

    async addRestoreButton(ParentClass,buttonClass) {
        //const startButton = document.querySelector('nav.startButton');
        //this.clearElement(startButton);

        const restoreButton = document.createElement('button');
        restoreButton.textContent = 'Back to Start';
        restoreButton.style.marginRight = '10px';
        restoreButton.className = buttonClass;

        restoreButton.addEventListener('click', async () => {
            const url = new URL(window.location.href);
            const params = url.searchParams;
            const currentPage = parseInt(params.get('p')) || 1;
            const newPage = currentPage - (this.targetPages - 1);
            params.set('p', newPage > 0 ? newPage : 1);
            url.search = params.toString();
            window.location.href = url.toString();
        });

        const parentElement = await this.getElementBySelector(ParentClass);
        parentElement.appendChild(restoreButton);
    }

    clearElement(element) {
        element.innerHTML='';
    }

    delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    changeElementClassName(element, newClassName) {
        if (element && typeof newClassName === 'string') {
            element.className = newClassName;
        }
    }

    async executeAndcountUpSec(label, fn) {
        const startTime = performance.now();
        await fn();
        const endTime = performance.now();
        console.log(`${label} 花費時間: ${(endTime - startTime) / 1000} 秒`);
    }

}

class customMenu{
    constructor() {
        this.registerMenuCommand(this);
        this.rowsOfArtsWall=this.getRowsOfArtsWall();
    }

    rowsOfArtsWallMenu(){
        const rows=parseInt(prompt(`${this.getFeatureMessageLocalization("rowsOfArtsWallPrompt")} ${this.getRowsOfArtsWall()}`));
        // console.log(Number.isInteger(rows));
        if(rows && Number.isInteger(rows) && rows>0){
            this.setRowsOfArtsWall(rows);
        }else {
            alert(this.getFeatureMessageLocalization("rowsOfArtsWallMenuError"));
        }
    }

    getRowsOfArtsWall() {
        return GM_getValue("rowsOfArtsWall", 7);
    }

    setRowsOfArtsWall(intger) {
        GM_setValue("rowsOfArtsWall",intger);
    }

    getFeatureMessageLocalization(word) {
        let display = {
            "zh-TW": {
                "rowsOfArtsWall": "行數設定",
                "rowsOfArtsWallPrompt": "一行顯示幾個繪畫?(請根據瀏覽器放大程度決定) 目前為:",
                "rowsOfArtsWallMenuError": "請輸入一個數字,且不能小於1",
            },
            "en": {
                "rowsOfArtsWall": "row setting",
                "rowsOfArtsWallPrompt": "How many paintings should be displayed in one row?(Please decide based on browser magnification level) Currently:",
                "rowsOfArtsWallMenuError": "Please enter a number, and it cannot be less than 1",
            },
            "ja": {
                "rowsOfArtsWall": "行設定",
                "rowsOfArtsWallPrompt": "1 行に何枚の絵畫を表示する必要がありますか?(ブラウザの倍率レベルに基づいて決定してください) 現在:",
                "rowsOfArtsWallMenuError": "數値を入力してください。1 未満にすることはできません",
            }
        };
        return display[navigator.language][word];
    }

    registerMenuCommand(instance) {
        //console.log("註冊選單");
        GM_registerMenuCommand(instance.getFeatureMessageLocalization("rowsOfArtsWall"), () => instance.rowsOfArtsWallMenu());
    }
}

class readingStand {
    static expandAllArtworks() {
        const artistHomePattern = /^https:\/\/www\.pixiv\.net\/(en\/users|users)\/[0-9]*$/;
        const tagHomePattern = /^.*:\/\/www\.pixiv\.net\/(en\/tags|tags)\/.*$/;
        const tagPagePattern = /^.*:\/\/www\.pixiv\.net\/(en\/tags|tags)\/.*\/artworks*/;
        if (artistHomePattern.test(self.location.href) || !tagPagePattern.test(self.location.href) && tagHomePattern.test(self.location.href)) {
            self.location.href = self.location.href + "/artworks?p=1";
        }
    }
}

//網頁名稱不論載入或AJAX更換頁面都會在過程會觸發1次,hashchange與popstate在此無法正確處理
const title = document.querySelector('title');
//新增對網頁網址的檢查,以確保即便標題被其他程式修改,腳本仍能意識到是否在相同頁面
let pageUrl = window.location.href;

const observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        //console.log('頁面名稱更改為 "%s"', document.title);
        readingStand.expandAllArtworks();
        if (window.location.href === pageUrl) {
            return;
        }
        pageUrl = window.location.href;
        let johnTheHornyOne = new artScraper(10, 50);
        johnTheHornyOne.addStartButton(johnTheHornyOne.strategy.getButtonAtClass(), johnTheHornyOne.strategy.getAllButtonClass());
    });
});
let config = {childList: true,};
observer.observe(title, config);
//初始化
let johnTheHornyOne = new artScraper(10, 50);
johnTheHornyOne.addStartButton(johnTheHornyOne.strategy.getButtonAtClass(), johnTheHornyOne.strategy.getAllButtonClass());

const johnTheRestaurantWaiter = new customMenu();