* 发送链接到 IDM 下载器 * @author hmjz100 * @author Night-stars-1 * @description IDM 下载必备 * @param {String} link - 下载链接 * @param {String} filename - 文件名 * @param {Array} [headers] - 自定义请求头参数(可选) * @returns {Promise<"success"|"fail">} 发送态结果
(link, filename, filesize, headers = {})
| 797 | * @returns {Promise<"success"|"fail">} 发送态结果 |
| 798 | */ |
| 799 | async sendLinkToIDM(link, filename, filesize, headers = {}) { |
| 800 | let rpc = base.getValue("setting_idm_rpc").find(i => i.default); |
| 801 | if (!this.sendLinkToIDM.lock) this.sendLinkToIDM.lock = Promise.resolve(); |
| 802 | return this.sendLinkToIDM.lock = this.sendLinkToIDM.lock.then(async () => { |
| 803 | headers = this.standHeaders(headers); |
| 804 | |
| 805 | if (!this.sendLinkToIDM.seq) this.sendLinkToIDM.seq = 1; |
| 806 | let seq = this.sendLinkToIDM.seq; |
| 807 | let time = Date.now(); |
| 808 | let url = `http://127.0.0.1:1001/client/${rpc.id}?seq=${seq}`; |
| 809 | let ext = base.getExtension(filename); |
| 810 | |
| 811 | let headersText = Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join("\n") + "\n"; // 坑1:IDM 对 Header 的解码比较死板,最后不加换行不肯解析 |
| 812 | |
| 813 | function format(key, val) { |
| 814 | if (val === undefined || val === null) return ""; |
| 815 | var strVal = String(val); |
| 816 | var len = new Blob([strVal]).size; // 坑2:使用 blob.size,而不是 length |
| 817 | return `${key}=${len}:${strVal}`; |
| 818 | }; |
| 819 | |
| 820 | let fields = [ |
| 821 | format(4, ext), // 4: 文件类型 |
| 822 | format(6, link), // 6: 链接 |
| 823 | format(7, location.origin), // 7: 来源页面(“该文件来自网页”) |
| 824 | format(11, headersText), // 11: 请求头 |
| 825 | format(100, filename), // 100: 文件名 |
| 826 | format(122, 4), // 122: 代理 |
| 827 | ]; |
| 828 | |
| 829 | // 坑3:神秘的请求格式 |
| 830 | // MSG# {请求指示} #13#1# {10241/20xx}(是/否 使用扩展提供的文件信息) : {?}(可能是距离扩展启动的时间?) :0: {当前时间戳} :0:1: {2/1}(是/否 优先弹窗,再获取文件信息) : {文件大小} :0,{表单}(格式如上); |
| 831 | let data = `MSG#${seq}#13#1#10241:${seq + 1000}:0:${time}:0:1:2:${filesize}:0,${fields.join(",")};`; |
| 832 | |
| 833 | let request = base.post(url, data, {}, "text").catch(() => false); |
| 834 | let timeout = new Promise((_, reject) => { |
| 835 | setTimeout(() => { |
| 836 | if (request.abort) request.abort(); |
| 837 | reject(new Error("timeout")); |
| 838 | }, 15 * 1000); |
| 839 | }) |
| 840 | |
| 841 | let res = await Promise.race([request, timeout]).catch(() => false); |
| 842 | |
| 843 | if (res && res.endsWith(`${seq}:3;`)) { |
| 844 | this.sendLinkToIDM.seq++; |
| 845 | return "success"; |
| 846 | }; |
| 847 | return "fail"; |
| 848 | }); |
| 849 | }, |
| 850 | |
| 851 | /** |
| 852 | * 发送链接到 Aria2 下载器 |