iwara下载助手

批量下载iwara视频

As of 24/02/2021. See the latest version.

// ==UserScript==
// @name         iwara下载助手
// @namespace    https://github.com/dawn-lc/user.js
// @version      1.0.1
// @description  批量下载iwara视频
// @author       dawn-lc
// @match        https://ecchi.iwara.tv/users/*
// @match        https://ecchi.iwara.tv/videos*
// @exclude      https://ecchi.iwara.tv/videos/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_download
// @grant        GM_openInTab
// ==/UserScript==
(function () {


    function fromData(Data) {
        let str = "";
        for (var key in Data) {
            str += key + '=' + Data[key] + '&';
        };
        return str.substr(0, str.length - 1);
    };
    function getQueryVariable(query, variable) {
        let vars = query.split("&");
        for (let i = 0; i < vars.length; i++) {
            let pair = vars[i].split("=");
            if (pair[0] == variable) { return pair[1]; };
        };
        return (false);
    };
    function parseDom(arg) {
        return new DOMParser().parseFromString(arg, 'text/html');
    };
    function getData(url, data, referrer) {
        return fetch(url + "?" + fromData(data), {
            headers: {
                "accept": "application/json, text/plain, */*",
                "content-type": "application/x-www-form-urlencoded",
            },
            referrer: referrer,
            credentials: 'include',
            method: 'GET',
            mode: 'cors',
            redirect: 'follow'
        });
    };
    function guid() {
        function S4() {
            return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
        };
        return (S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4());
    };







    const element = {
        createElement(detailedList) {
            if (detailedList instanceof Array) {
                return detailedList.map(item => this.createElement(item));
            } else {
                return this.generateElement(document.createElement(detailedList.nodeType), detailedList);
            };
        },
        generateElement(item, detailedList) {
            for (const i in detailedList) {
                if (i == 'nodeType') continue;
                if (i == 'childs' && detailedList.childs instanceof Array) {
                    detailedList.childs.forEach(child => {
                        if (child instanceof HTMLElement) item.appendChild(child);
                        else if (typeof (child) == 'string') item.insertAdjacentHTML('beforeend', child);
                        else item.appendChild(this.createElement(child));
                    });
                }
                else if (i == 'attribute') {
                    for (const key in detailedList.attribute) {
                        item.setAttribute(key, detailedList.attribute[key]);
                    };
                }
                else if (i == 'parent') {
                    detailedList.parent.appendChild(item);
                }
                else if (detailedList[i] instanceof Object && item[i]) {
                    Object.entries(detailedList[i]).forEach(([k, v]) => {
                        item[i][k] = v;
                    });
                }
                else {
                    item[i] = detailedList[i];
                }
            }
            return item;
        }
    };




    const DownloadTypes = {
        //aria2
        aria2: 0,
        //默认
        default: 1,
        //其他
        others: 2
    }
    const setting = {
        initialize: GM_getValue("initialize", false),
        DownloadType: GM_getValue("DownloadType", DownloadTypes.default),
        DownloadDir: GM_getValue("DownloadDir", ''),
        DownloadProxy: GM_getValue("DownloadProxy", ''),
        WebSocketAddress: GM_getValue("WebSocketAddress", 'ws://127.0.0.1:6800/'),
        WebSocketToken: GM_getValue("WebSocketToken", ''),
        WebSocketID: GM_getValue("WebSocketID", guid()),
        setDownloadType(value) {
            this.DownloadType = Number(value);
            GM_setValue("DownloadType", this.DownloadType);
        },
        setDownloadDir(value) {
            this.DownloadDir = value;
            GM_setValue("DownloadDir", this.DownloadDir);
        },
        setDownloadProxy(value) {
            this.DownloadProxy = value;
            GM_setValue("DownloadProxy", this.DownloadProxy);
        },
        setWebSocketAddress(value) {
            this.WebSocketAddress = value;
            GM_setValue("WebSocketAddress", this.WebSocketAddress);
        },
        setWebSocketToken(value) {
            this.WebSocketToken = value;
            GM_setValue("WebSocketToken", this.WebSocketToken);
        },
        setting() {
            if (!document.getElementById("PluginControlPanel")) element.createElement(main.PluginControlPanel);
            document.getElementById("PluginControlPanel").style.display = 'block';
            for (let index = 0; index < document.getElementsByTagName("INPUT").length; index++) {
                const element = document.getElementsByTagName("INPUT")[index];
                if (element.name == "DownloadType" && Number(element.value) == this.DownloadType) {
                    element.setAttribute("checked", null);
                    break;
                }
            }
        }
    };




    const main = {
        Aria2WebSocket: undefined,
        ConnectionWebSocket() {
            try {
                this.Aria2WebSocket = new WebSocket(setting.WebSocketAddress + "jsonrpc");
                this.Aria2WebSocket.onopen = wsopen;
                this.Aria2WebSocket.onmessage = wsmessage;
                this.Aria2WebSocket.onclose = wsclose;
            } catch (err) {
                setting.initialize = false;
                this.Aria2WebSocket.close();
            }
            function wsopen() {
                console.log("链接成功!");
            };
            function wsmessage(evt) {
                console.log(evt);
            };

            function wsclose() {
                console.log("断开链接!");
            };
        },
        ManualParseDownloadAddress(ID) {
            if (ID == undefined) {
                ID = prompt("请输入需要下载的视频ID", "");
                if (ID.split("_")[1] != undefined) {
                    ID = ID.split("_")[1];
                };
            };
            getData("https://ecchi.iwara.tv/videos/" + ID, null, window.location.href).then(
                function (responseData) {
                    if (responseData.status >= 200 && responseData.status < 300) {
                        responseData.text().then(function (response) {
                            let videoListRawData = parseDom(response);
                            if (videoListRawData.length == 0) {
                                console.log(responseData.url);
                                debugger
                            } else {
                                let Author = videoListRawData.getElementsByClassName("node-info")[0].getElementsByClassName("username")[0].innerText;
                                let Name = videoListRawData.getElementsByTagName("H1")[0].innerText;
                                let commentArea = videoListRawData.getElementsByClassName("node-info")[0].getElementsByClassName("field-type-text-with-summary field-label-hidden")[0].getElementsByClassName("field-item even");
                                let comment;
                                for (let index = 0; index < commentArea.length; index++) {
                                    const element = commentArea[index];
                                    comment += element.innerText.toUpperCase();
                                };
                                if (comment.indexOf("/S/") != -1 || comment.indexOf("MEGA") != -1) {
                                    window.open(element.getElementsByTagName("A")[0].href, '_blank');
                                } else {
                                    getData("https://ecchi.iwara.tv/api/video/" + ID, null, "https://ecchi.iwara.tv/videos/" + ID).then(
                                        function (responseData) {
                                            if (responseData.status >= 200 && responseData.status < 300) {
                                                responseData.json().then(function (response) {
                                                    let videoStreamInfo = response;
                                                    if (videoStreamInfo.length == 0) {
                                                        console.log(responseData.url);
                                                        debugger
                                                    } else {
                                                        let Url = decodeURIComponent("https:" + videoStreamInfo[0]["uri"]);
                                                        let FlieName = getQueryVariable(Url, "file").split("/")[3];
                                                        main.SendDownloadRequest(Name, Url, FlieName, Author, document.cookie);
                                                    };
                                                });
                                            };
                                        }
                                    );
                                };
                            };
                        });
                    };
                }
            );
        },
        DownloadSelected() {
            let select = document.createElement("div");
            for (let index = 0; index < document.getElementsByClassName("node-video").length; index++) {
                const element = document.getElementsByClassName("node-video")[index];
                if (!element.classList.contains("node-full")) {
                    if (element.getElementsByClassName("selectButton")[0].getAttribute("isselected") === "true") {
                        select.appendChild(element.cloneNode(true));
                    };
                };
            };
            main.ParseDownloadAddress(select);
        },
        DownloadAll() {
            if (document.getElementById("block-views-videos-block-2").getElementsByClassName("more-link").length == 0) {
                getData(window.location.href, null, window.location.href).then(
                    function (responseData) {
                        if (responseData.status >= 200 && responseData.status < 300) {
                            responseData.text().then(function (response) {
                                let videoListRawData = parseDom(response);
                                if (videoListRawData.length == 0) {
                                    console.log(responseData.url);
                                    debugger
                                } else {
                                    main.ParseDownloadAddress(videoListRawData.getElementById("block-views-videos-block-2"));
                                };
                            });
                        };
                    }
                );
            } else {
                let videoListUrl = window.location.href + "/videos";
                main.GetAllData(videoListUrl, null, window.location.href);
            };
        },
        GetAllData(videoListUrl, data, referrer) {
            getData(videoListUrl, data, referrer).then(
                function (responseData) {
                    if (responseData.status >= 200 && responseData.status < 300) {
                        responseData.text().then(function (response) {
                            let videoListRawData = parseDom(response);
                            if (videoListRawData.length == 0) {
                                console.log(responseData.url);
                                debugger
                            } else {
                                ParseDownloadAddress(videoListRawData);
                                if (videoListRawData.getElementsByClassName("pager-next").length != 0) {
                                    videoListUrl = videoListRawData.getElementsByClassName("pager-next")[0].children[0].href;
                                    this.GetAllData(videoListUrl, data, referrer);
                                } else {
                                    return;
                                };
                            };
                        });
                    };
                }
            );
        },
        ParseDownloadAddress(videosListDom) {
            let uploadedVideosList = videosListDom.getElementsByClassName("node-video");
            for (let index = 0; index < uploadedVideosList.length; index++) {
                const element = uploadedVideosList[index];
                let Author = element.getElementsByClassName("username")[0].innerText;
                let ID = element.getElementsByTagName("A")[0].href.split("/")[4];
                let Name = element.getElementsByTagName("H3")[0].innerText;
                getData(element.getElementsByTagName("A")[0].href, null, window.location.href).then(
                    function (responseData) {
                        if (responseData.status >= 200 && responseData.status < 300) {
                            responseData.text().then(function (response) {
                                let videoListRawData = parseDom(response);
                                if (videoListRawData.length == 0) {
                                    console.log(responseData.url);
                                    debugger
                                } else {
                                    let commentArea = videoListRawData.getElementsByClassName("node-info")[0].getElementsByClassName("field-type-text-with-summary field-label-hidden")[0].getElementsByClassName("field-item even");
                                    let comment;
                                    for (let index = 0; index < commentArea.length; index++) {
                                        const element = commentArea[index];
                                        comment += element.innerText.toUpperCase();
                                    };
                                    if (comment.indexOf("/S/") != -1 || comment.indexOf("MEGA") != -1) {
                                        window.open(element.getElementsByTagName("A")[0].href, '_blank');
                                    } else {
                                        getData("https://ecchi.iwara.tv/api/video/" + ID, null, element.getElementsByTagName("A")[0].href).then(
                                            function (responseData) {
                                                if (responseData.status >= 200 && responseData.status < 300) {
                                                    responseData.json().then(function (response) {
                                                        let videoStreamInfo = response;
                                                        if (videoStreamInfo.length == 0) {
                                                            console.log(responseData.url);
                                                            debugger
                                                        } else {
                                                            let Url = decodeURIComponent("https:" + videoStreamInfo[0]["uri"]);
                                                            let FlieName = getQueryVariable(Url, "file").split("/")[3];
                                                            main.SendDownloadRequest(Name, Url, FlieName, Author, document.cookie);
                                                        };
                                                    });
                                                };
                                            }
                                        );
                                    };
                                };
                            });
                        };
                    }
                );
            };
        },
        SendDownloadRequest(Name, Url, FlieName, Author, Cookie) {
            switch (setting.DownloadType) {
                case DownloadTypes.aria2:
                    this.Aria2WebSocket.send(JSON.stringify({
                        "jsonrpc": "2.0",
                        "method": "aria2.addUri",
                        "id": setting.WebSocketID,
                        "params": [
                            "token:" + setting.WebSocketToken,
                            [
                                Url
                            ],
                            {
                                "referer": "https://ecchi.iwara.tv/",
                                "header": [
                                    "Cookie:" + Cookie
                                ],
                                "out": FlieName,
                                "dir": setting.DownloadDir + Author,
                                "all-proxy": setting.DownloadProxy,
                                "split": 2
                            }
                        ]
                    }));
                    break;
                case DownloadTypes.default:
                    GM_download({
                        url: Url,
                        name: FlieName,
                        saveAs: false,
                        onerror: function (error) {
                            console.log(error);
                        },
                        onload: function () {
                            console.log("已下载");
                        }
                    });
                    break;
                case DownloadTypes.others:
                    GM_openInTab(Url, { active: true, insert: true, setParent: true });
                    break;
                default:
                    console.log("未知的下载模式!");
                    break;
            }
        },
        PluginUI: [{
            nodeType: 'style',
            innerHTML: `.selectButton{
                text-align:right;
            }
            .selectButton[isselected=false]:before 
            {
                position:absolute;
                content: "";
            }
            .selectButton[isselected=true]:before
            {
                position:absolute;
                content: "√";
            }
            .controlPanel {
                display: none; /* 默认隐藏 */
                position: fixed; /* 固定定位 */
                z-index: 9999; /* 设置在顶层 */
                left: 0;
                top: 0;
                width: 100%; 
                height: 100%;
                overflow: auto; 
                background-color: rgba(0,0,0,0.4); 
            }
            
            /* 弹窗内容 */
            .controlPanel-content {
                background-color: #fefefe;
                margin: 15% auto; 
                padding: 20px;
                border: 1px solid #888;
                width: 80%; 
            }
            
            /* 关闭按钮 */
            .controlPanelClose {
                color: #aaa;
                float: right;
                font-size: 28px;
                font-weight: bold;
            }
            
            .controlPanelClose:hover,
            .controlPanelClose:focus {
                color: black;
                text-decoration: none;
                cursor: pointer;
            }`,
            parent: document.head
        }, {
            nodeType: 'div',
            id: 'PluginUI',
            className: 'btn-group',
            childs: [{
                nodeType: 'button',
                type: 'button',
                id: 'PluginUIStartUp',
                title: '快速下载',
                className: 'btn btn-primary btn-sm dropdown-toggle',
                onclick: function () {
                    if (this.parentNode.classList.contains("open")) {
                        this.parentNode.classList.remove("open");
                    } else {
                        this.parentNode.classList.add("open");
                    };
                },
                childs: [{
                    nodeType: 'span',
                    className: 'glyphicon glyphicon-download-alt'
                }]
            },
            {
                nodeType: 'ul',
                className: 'dropdown-menu',
                attribute: {
                    role: 'menu'
                },
                childs: [{
                    nodeType: 'li',
                    style: 'cursor: pointer;',
                    id: 'DownloadSelected',
                    innerHTML: '<a><span class="glyphicon glyphicon-check"></span>下载所选</a>',
                    onclick: function () {
                        main.DownloadSelected();
                        document.getElementById("PluginUIStartUp").click();
                    }
                },
                {
                    nodeType: 'li',
                    style: 'display: none;cursor: pointer;',
                    id: 'DownloadAll',
                    innerHTML: '<a><span class="glyphicon glyphicon-save"></span>下载所有</a>',
                    onclick: function () {
                        main.DownloadAll();
                        document.getElementById("PluginUIStartUp").click();
                    }
                },
                {
                    nodeType: 'li',
                    style: 'cursor: pointer;',
                    id: 'manualDownload',
                    innerHTML: '<a><span class="glyphicon glyphicon-edit"></span>手动下载</a>',
                    onclick: function () {
                        main.ManualParseDownloadAddress();
                        document.getElementById("PluginUIStartUp").click();
                    }
                },
                {
                    nodeType: 'li',
                    style: 'cursor: pointer;',
                    id: 'pluginSet',
                    innerHTML: '<a><span class="glyphicon glyphicon-cog"></span>设置</a>',
                    onclick: function () {
                        setting.setting();
                        document.getElementById("PluginUIStartUp").click();
                    }
                }]
            }],
            parent: document.getElementById("user-links")
        }],
        PluginControlPanel: [{
            nodeType: 'div',
            id: 'PluginControlPanel',
            className: 'controlPanel',
            childs: [{
                nodeType: 'div',
                className: 'controlPanel-content',
                childs: [{
                    nodeType: 'span',
                    className: 'controlPanelClose',
                    innerHTML: '&times;',
                    onclick: function () {
                        this.parentNode.parentNode.style.display = "none";
                        setting.setDownloadType(setting.initialize);
                        setting.setDownloadDir(setting.DownloadDir);
                        setting.setDownloadProxy(setting.DownloadProxy);
                        setting.setWebSocketAddress(setting.WebSocketAddress);
                        setting.setWebSocketToken(setting.WebSocketToken);
                    }
                },
                {
                    nodeType: 'div',
                    id: 'controlPanelItem',
                    childs: [{
                        nodeType: 'div',
                        style: 'margin: 10px 0;',
                        childs: [
                            {
                                nodeType: 'label',
                                style: 'margin: 0px 10px 0px 0px;',
                                innerHTML: '下载方式:'
                            },
                            {
                                nodeType: 'input',
                                name: 'DownloadType',
                                type: 'radio',
                                value: DownloadTypes.aria2,
                                onchange: ({ target }) => setting.setDownloadType(target.value)
                            },
                            {
                                nodeType: 'label',
                                style: 'margin: 0px 20px 0px 0px;',
                                innerHTML: 'Aria2'
                            },
                            {
                                nodeType: 'input',
                                name: 'DownloadType',
                                type: 'radio',
                                value: DownloadTypes.default,
                                onchange: ({ target }) => setting.setDownloadType(target.value)
                            },
                            {
                                nodeType: 'label',
                                style: 'margin: 0px 20px 0px 0px;',
                                innerHTML: '浏览器默认'
                            },
                            {
                                nodeType: 'input',
                                name: 'DownloadType',
                                type: 'radio',
                                value: DownloadTypes.others,
                                onchange: ({ target }) => setting.setDownloadType(target.value)
                            },
                            {
                                nodeType: 'label',
                                style: 'margin: 0px 20px 0px 0px;',
                                innerHTML: '其他下载器'
                            }
                        ]
                    }, {
                        nodeType: 'div',
                        style: 'margin: 10px 0;',
                        childs: [
                            {
                                nodeType: 'label',
                                style: 'margin-right: 5px;',
                                innerHTML: '下载到:',
                                for: "DownloadDir"
                            },
                            {
                                nodeType: 'input',
                                id: 'DownloadDir',
                                type: 'text',
                                value: setting.DownloadDir,
                                onchange: ({ target }) => setting.setDownloadDir(target.value),
                                style: 'width:100%'
                            }
                        ]
                    }, {
                        nodeType: 'div',
                        style: 'margin: 10px 0;',
                        childs: [
                            {
                                nodeType: 'label',
                                style: 'margin-right: 5px;',
                                innerHTML: '代理服务器:',
                                for: "DownloadProxy"
                            },
                            {
                                nodeType: 'input',
                                id: 'DownloadProxy',
                                type: 'text',
                                value: setting.DownloadProxy,
                                onchange: ({ target }) => setting.setDownloadProxy(target.value),
                                style: 'width:100%'
                            }
                        ]
                    }, {
                        nodeType: 'div',
                        style: 'margin: 10px 0;',
                        childs: [
                            {
                                nodeType: 'label',
                                style: 'margin-right: 5px;',
                                innerHTML: 'Aria2 RPC WebSocket 地址:',
                                for: "WebSocketAddress"
                            },
                            {
                                nodeType: 'input',
                                id: 'WebSocketAddress',
                                type: 'text',
                                value: setting.WebSocketAddress,
                                onchange: ({ target }) => setting.setWebSocketAddress(target.value),
                                style: 'width:100%'
                            }
                        ]
                    }, {
                        nodeType: 'div',
                        style: 'margin: 10px 0;',
                        childs: [
                            {
                                nodeType: 'label',
                                style: 'margin-right: 5px;',
                                innerHTML: 'Aria2 RPC Token(密钥):',
                                for: "WebSocketToken"
                            },
                            {
                                nodeType: 'input',
                                id: 'WebSocketToken',
                                type: 'password',
                                value: setting.WebSocketToken,
                                onchange: ({ target }) => setting.setWebSocketToken(target.value),
                                style: 'width:100%'
                            }
                        ]
                    }]
                }]
            }],
            parent: document.body
        }]
    }

    element.createElement(main.PluginUI);

    main.ConnectionWebSocket();

    var clickTimer = null;
    for (let index = 0; index < document.getElementsByClassName("node-video").length; index++) {
        const element = document.getElementsByClassName("node-video")[index];
        if (!element.classList.contains("node-full")) {
            let selectButton = element.getElementsByClassName("field-items")[0];
            for (let index = 0; index < selectButton.getElementsByTagName("A").length; index++) {
                const a = selectButton.getElementsByTagName("A")[index];
                a.parentNode.appendChild(a.childNodes[0])
                a.style.display = "none";
            };
            selectButton.classList.add("selectButton");
            selectButton.setAttribute("isselected", false);
            selectButton.ondblclick = function () {
                if (clickTimer) {
                    window.clearTimeout(clickTimer);
                    clickTimer = null;
                };
                if (this.getAttribute("isselected") === "true") {
                    this.setAttribute("isselected", false);
                } else {
                    this.setAttribute("isselected", true);
                };
            };
            selectButton.onclick = function () {
                if (clickTimer) {
                    window.clearTimeout(clickTimer);
                    clickTimer = null;
                };
                clickTimer = window.setTimeout(function () {
                    element.getElementsByTagName("A")[1].click();
                }, 300);
            };
        };
    };

    if (window.location.href.split("/")[3] == "users") {
        document.getElementById("downloadAll").style.display = "inline";
    };



    window.onclick = function (event) {
        if (!event.path.includes(document.getElementById("PluginUI"))) {
            if (document.getElementById("PluginUI").classList.contains("open")) {
                document.getElementById("PluginUI").classList.remove("open");
            }
        }
    };


})();