動畫瘋評分顯示

簡單粗暴地為每個動畫封面添加評分顯示,並排序

目前为 2023-07-14 提交的版本。查看 最新版本

// ==UserScript==
// @name         動畫瘋評分顯示
// @namespace    Anong0u0
// @version      0.1.1
// @description  簡單粗暴地為每個動畫封面添加評分顯示,並排序
// @author       Anong0u0
// @match        https://ani.gamer.com.tw/animeList.php*
// @match        https://ani.gamer.com.tw/mygather.php*
// @match        https://ani.gamer.com.tw/*
// @icon         https://i.imgur.com/2aijUa9.png
// @grant        GM_xmlhttpRequest
// @connect      api.gamer.com.tw
// @connect      wall.gamer.com.tw
// @license      MIT
// ==/UserScript==

const delay = (ms = 0) => {return new Promise((r)=>{setTimeout(r, ms)})}

const waitElementLoad = (elementSelector, selectCount = 1, tryTimes = 1, interval = 0) =>
{
    return new Promise(async (resolve, reject)=>
    {
        let t = 1, result;
        while(true)
        {
            if(selectCount != 1) {if((result = document.querySelectorAll(elementSelector)).length >= selectCount) break;}
            else {if(result = document.querySelector(elementSelector)) break;}

            if(tryTimes>0 && ++t>tryTimes) return reject(new Error("Wait Timeout"));
            await delay(interval);
        }
        resolve(result);
    })
}

const requests = (method, url, data = null, headers = {}) =>
{
    return new Promise(resolve =>
    {
        GM_xmlhttpRequest({
          method: method,
          url: url,
          headers: headers,
          data: data,
          onload: resolve
        });
    })
}


(async ()=>
{
    Node.prototype.prependChild = function(element) {return this.insertBefore(element, this.firstChild)}
    const lists = await waitElementLoad("div.theme-list-block", 0, 10, 200)
    for (const list of lists)
    {
        const label = document.createElement("label")
        label.style="padding-left:1em;"
        label.innerHTML=`<input checked="true" type="checkbox" style="width:1.5em;height:1.5em;">依評分排序`
        list.parentElement.querySelector(".theme-title-block").firstElementChild.append(label)
        const elements = [...list.querySelectorAll("a.theme-list-main[href^='animeRef.php?sn=']")]
        const tip = document.createElement("span")
        tip.style="padding-left:1em;"
        let count = 0
        tip.innerText = `讀取評分中(${count}/${elements.length})`
        label.insertAdjacentElement("beforebegin", tip)
        for (const element of elements)
        {
            const refID = element.href.match(/(?<=sn=)\d+/g);
            const videoID = (await requests("head", `https://ani.gamer.com.tw/animeRef.php?sn=${refID}`)).finalUrl.match(/(?<=sn=)\d+/g);
            const reviewID = JSON.parse((await requests("get", `https://wall.gamer.com.tw/app/v2/comment_detail.php?service=4&serviceId=${videoID}`)).responseText).to.id;
            const avgRating = JSON.parse((await requests("get", `https://api.gamer.com.tw/acg/v1/reviews_list.php?sn=${reviewID}`)).responseText).data.rating.avg.toFixed(1);
            //console.log(refID);
            //console.log(videoID);
            //console.log(reviewID);
            //const name = element.querySelector(".theme-name")
            //console.log(avgRating, name.innerText);

            const ratingBlock = document.createElement("div")
            ratingBlock.className = "anime-label-block"
            ratingBlock.style = "top: 6px;bottom: unset;"
            ratingBlock.innerHTML = `<span class="label-edition color-paid" style="background-color: var(--anime-primary-color);">${avgRating}</span>`
            element.querySelector(".theme-img-block").append(ratingBlock)
            element.rating = avgRating
            element.videoID = videoID
            element.refID = refID
            tip.innerText = `讀取評分中(${++count}/${elements.length})`
        }
        tip.remove()
        label.onclick = ()=>
        {
            if(label.firstElementChild.checked) elements.sort((a, b)=> a.rating-b.rating || a.videoID-b.videoID).forEach((e)=>{list.prependChild(e)})
            else elements.sort((a, b)=> a.refID-b.refID).forEach((e)=>{list.prependChild(e)})
        }
        if(label.firstElementChild.checked) label.onclick()
        
    }
    // TODO: homepage timeline anime
    // TODO: homepage load more button
    // TODO: cache rating in ls = [rating, refID, videoID, imgID]
    // TODO: sort anime by rating (read all page)
})()