Download Weibo Images & Videos (Only support new version weibo UI)

Download images and videos from new version weibo UI webpage.

  1. // ==UserScript==
  2. // @name Download Weibo Images & Videos (Only support new version weibo UI)
  3. // @name:zh-CN 下载微博图片和视频(仅支持新版界面)
  4. // @version 1.3.5
  5. // @description Download images and videos from new version weibo UI webpage.
  6. // @description:zh-CN 从新版微博界面下载图片和视频。
  7. // @author OWENDSWANG
  8. // @match https://weibo.com/*
  9. // @match https://www.weibo.com/*
  10. // @match https://s.weibo.com/weibo*
  11. // @match https://s.weibo.com/realtime*
  12. // @match https://s.weibo.com/video*
  13. // @exclude https://weibo.com/tv/*
  14. // @exclude https://www.weibo.com/tv/*
  15. // @exclude https://weibo.com/p/*
  16. // @exclude https://www.weibo.com/p/*
  17. // @icon https://weibo.com/favicon.ico
  18. // @license MIT
  19. // @homepage https://gf.qytechs.cn/scripts/430877
  20. // @supportURL https://github.com/owendswang/Download-Weibo-Images-Videos
  21. // @grant GM_xmlhttpRequest
  22. // @grant GM_notification
  23. // @grant GM_getValue
  24. // @grant GM_setValue
  25. // @grant GM_registerMenuCommand
  26. // @grant GM_cookie
  27. // @connect weibo.com
  28. // @connect www.weibo.com
  29. // @connect sinaimg.cn
  30. // @connect weibocdn.com
  31. // @connect localhost
  32. // @namespace http://tampermonkey.net/
  33. // @run-at document-end
  34. // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.9.1/jszip.min.js
  35. // ==/UserScript==
  36.  
  37. (function() {
  38. 'use strict';
  39.  
  40. const settingVersion = 1;
  41.  
  42. let text = [];
  43. let text_zh = [
  44. /*0*/ '添加下载按钮',
  45. /*1*/ '欢迎使用“下载微博图片”脚本',
  46. /*2*/ '请选择添加下载按钮的方式:',
  47. /*3*/ '点击“添加下载按钮”来添加下载按钮。',
  48. /*4*/ '当鼠标位于浏览器页面时添加下载按钮,但这种方式会占用很多CPU资源。',
  49. /*5*/ '确定',
  50. /*6*/ '下载设置',
  51. /*7*/ '下载文件名称',
  52. /*8*/ '{original} - 原文件名\n{username} - 原博主名称\n{userid} - 原博主ID\n{mblogid} - 原博mblogid\n{uid} - 原博uid\n{ext} - 文件后缀\n{index} - 图片序号\n{YYYY} {MM} {DD} {HH} {mm} {ss} - 原博发布时\n间的年份、月份、日期、小时、分钟、秒,可\n分开独立使用\n{content} - 博文内容(最多前25个字符)',
  53. /*9*/ '下载队列',
  54. /*10*/ '重试',
  55. /*11*/ '关闭',
  56. /*12*/ '取消',
  57. /*13*/ '打包下载',
  58. /*14*/ '打包文件名',
  59. /*15*/ '与“下载文件名称”规则相同,但{original}、{ext}、{index}除外',
  60. /*16*/ '单独设置转发微博下载文件名称',
  61. /*17*/ '转发微博下载文件名称',
  62. /*18*/ '除“下载文件名”规则外,额外标签如下:\n{re.mblogid} - 转博mblogid\n{re.username} - 转发博主名称\n{re.userid} - 转发博主ID\n{re.uid} - 转博uid\n{re.content} - 转发博文内容(最多前25个字符)\n{re.YYYY} {re.MM} {re.DD} {re.HH} {re.mm} {re.ss}\n - 原博发布时间的年份、月份、日期、小时、\n分钟、秒,可分开独立使用',
  63. /*19*/ '转发微博打包文件名',
  64. /*20*/ '与“转发微博下载文件名称”规则相同,但{original}、{ext}、{index}除外',
  65. /*21*/ '使用Aria2c远程下载',
  66. /*22*/ 'RPC接口地址',
  67. /*23*/ '使用此方式下载,无法使用打包功能,无法在页面右下角显示下载进度和结果。',
  68. /*24*/ '如果接口地址不是localhost,需手动将地址添加到XHR白名单。',
  69. /*25*/ '设置',
  70. /*26*/ '<b>注意</b>:',
  71. /*27*/ '启用“打包下载”时,需区分多文件名称,\n避免重复而导致打包后只有一个文件,文件命\n名时,必须包含{original}、{index}中至少一个\n标签。',
  72. /*28*/ '下载视频封面',
  73. /*29*/ '下载无水印图片',
  74. /*30*/ '图片质量会下降',
  75. /*31*/ '隐藏页面上的设置按钮',
  76. /*32*/ '可在浏览器扩展Tampermonkey下拉菜单中打开\n设置窗口。',
  77. ];
  78. let text_en = [
  79. /*0*/ 'Add Download Buttons',
  80. /*1*/ 'Welcome Using \'Download Weibo Images\' Script',
  81. /*2*/ 'Which way do you like to add download buttons to each weibo post?',
  82. /*3*/ 'Click \'Add Download Buttons\' button to add download buttons.',
  83. /*4*/ 'When mouse over browser page, add download buttons automatically. But it takes a lot of CPU usage.',
  84. /*5*/ 'OK',
  85. /*6*/ 'Download Setting',
  86. /*7*/ 'Download File Name',
  87. /*8*/ '{original} - Original file name\n{username} - Original user name\n{userid} - Original user ID\n{mblogid} - Original mblogid\n{uid} - Original uid\n{ext} - File extention\n{index} - Image index\n{YYYY} {MM} {DD} {HH} {mm} {ss} - "Year", \n"Month", "Date", "Hour", "Minute", "Second" \nof the created time of the original post\n{content} - Original post content (limited to \nfirst 25 characters)',
  88. /*9*/ 'Download Queue',
  89. /*10*/ 'Retry',
  90. /*11*/ 'Close',
  91. /*12*/ 'Cancel',
  92. /*13*/ 'Pack download files as a ZIP file',
  93. /*14*/ 'ZIP File Name',
  94. /*15*/ 'The same rules as "Download File Name" except {original}, {ext} and {index}',
  95. /*16*/ 'Different File Name for Retweets',
  96. /*17*/ 'Retweet Download File Name',
  97. /*18*/ 'Except the rules for "Download File Name", there are additional tags as below.\n{re.mblogid} - Retweet mblogid\n{re.username} - Retweet user name\n{re.userid} - Retweet user ID\n{re.uid} - Retweet uid\n{re.content} - Retweet post content (limited to first 25 characters)\n{re.YYYY} {re.MM} {re.DD} {re.HH} {re.mm} {re.ss} - "Year", "Month", "Date", "Hour", "Minute", "Second" of the created time of the retweet post',
  98. /*19*/ 'Retweet Zip File Name',
  99. /*20*/ 'The same rules as "Retweet Download File Name" except {original}, {ext} and {index}',
  100. /*21*/ 'Use Aria2c remote download API',
  101. /*22*/ 'RPC Url',
  102. /*23*/ 'In this mode, You would not be able to download in ZIP mode and to observe download progress and result.',
  103. /*24*/ 'If it\'s not \'localhost\', you would have to add it to \'XHR white list\'.',
  104. /*25*/ 'Settings',
  105. /*26*/ '<b>Attention</b>: ',
  106. /*27*/ 'When \'ZIP mode\' enabled, you have \nto include one of the tags {original} or {index} \nto avoid duplicated ones being overwritten.',
  107. /*28*/ 'Download video cover image',
  108. /*29*/ 'Download Images without watermarks',
  109. /*30*/ 'Image quality is lower than those \nwith watermarks',
  110. /*31*/ 'Hide \'Settings\' button on the web page',
  111. /*32*/ '\'Settings\' modal could be called \nout by click on the dropdown menu of \nTampermoneky extension.',
  112. ];
  113. if(navigator.language.substr(0, 2) == 'zh') {
  114. text = text_zh;
  115. } else {
  116. text = text_en;
  117. }
  118.  
  119. let downloadQueueCard = document.createElement('div');
  120. downloadQueueCard.style.position = 'fixed';
  121. downloadQueueCard.style.bottom = '0.5rem';
  122. downloadQueueCard.style.left = '0.5rem';
  123. downloadQueueCard.style.maxHeight = '50vh';
  124. downloadQueueCard.style.overflowY = 'auto';
  125. downloadQueueCard.style.overflowX = 'hidden';
  126. let downloadQueueTitle = document.createElement('div');
  127. downloadQueueTitle.textContent = text[9];
  128. downloadQueueTitle.style.fontSize = '0.8rem';
  129. downloadQueueTitle.style.color = 'gray';
  130. downloadQueueTitle.style.display = 'none';
  131. downloadQueueCard.appendChild(downloadQueueTitle);
  132. document.body.appendChild(downloadQueueCard);
  133. let progressBar = document.createElement('div');
  134. progressBar.style.height = '1.4rem';
  135. progressBar.style.width = '23rem';
  136. // progressBar.style.background = 'linear-gradient(to right, red 100%, transparent 100%)';
  137. progressBar.style.borderStyle = 'solid';
  138. progressBar.style.borderWidth = '0.1rem';
  139. progressBar.style.borderColor = 'grey';
  140. progressBar.style.borderRadius = '0.5rem';
  141. progressBar.style.boxSizing = 'content-box';
  142. progressBar.style.marginTop = '0.5rem';
  143. progressBar.style.marginRight = '1rem';
  144. progressBar.style.position = 'relative';
  145. let progressText = document.createElement('div');
  146. // progressText.textContent = 'test.test';
  147. progressText.style.mixBlendMode = 'screen';
  148. progressText.style.width = '100%';
  149. progressText.style.textAlign = 'center';
  150. progressText.style.color = 'orange';
  151. progressText.style.fontSize = '0.7rem';
  152. progressText.style.lineHeight = '1.4rem';
  153. progressText.style.overflow = 'hidden';
  154. progressBar.appendChild(progressText);
  155. let progressCloseBtn = document.createElement('button');
  156. progressCloseBtn.style.border = 'unset';
  157. progressCloseBtn.style.background = 'unset';
  158. progressCloseBtn.style.color = 'orange';
  159. progressCloseBtn.style.position = 'absolute';
  160. progressCloseBtn.style.right = '0';
  161. progressCloseBtn.style.top = '0.1rem';
  162. progressCloseBtn.style.fontSize = '1rem';
  163. progressCloseBtn.style.lineHeight = '1rem';
  164. progressCloseBtn.style.cursor = 'pointer';
  165. progressCloseBtn.textContent = '×';
  166. progressCloseBtn.title = text[12];
  167. progressCloseBtn.onmouseover = function(e){
  168. this.style.color = 'red';
  169. }
  170. progressCloseBtn.onmouseout = function(e){
  171. this.style.color = 'orange';
  172. }
  173. progressBar.appendChild(progressCloseBtn);
  174. // downloadQueueCard.appendChild(progressBar);
  175.  
  176. function saveAs(blob, name) {
  177. const link = document.createElement("a");
  178. link.style.display = "none";
  179. link.href = URL.createObjectURL(blob);
  180. link.download = name;
  181. link.target = '_blank';
  182. document.body.appendChild(link);
  183. link.click();
  184. const timeout = setTimeout(() => {
  185. URL.revokeObjectURL(link.href);
  186. link.parentNode.removeChild(link);
  187. }, 1000);
  188. }
  189.  
  190. function send2Aria2c(url, fileName, headerFlag) {
  191. // console.log(downloadUrl);
  192. return new Promise(function(resolve, reject) {
  193. GM_cookie.list({ url: '.weibo.com' }, function(cookies, error) {
  194. if (error) {
  195. console.error(error);
  196. } else {
  197. // console.log(cookies);
  198. let header = [ 'User-Agent: ' + window.navigator.userAgent ];
  199. if (headerFlag) {
  200. header.push('Referer: https://' + location.host);
  201. header.push('Origin: https://' + location.host);
  202. }
  203. if (cookies && cookies.length > 0) {
  204. header.push('Cookie: ' + cookies.map((cookie) => (cookie.name + '=' + cookie.value)).join('; '));
  205. }
  206. downloadQueueTitle.style.display = 'block';
  207. let progress = downloadQueueCard.appendChild(progressBar.cloneNode(true));
  208. progress.firstChild.textContent = fileName;
  209. GM_xmlhttpRequest({
  210. method: 'POST',
  211. url: GM_getValue('ariaRpcUrl','http://localhost:6800/jsonrpc'),
  212. data: JSON.stringify({
  213. jsonrpc: '2.0',
  214. id: 'weibo',
  215. method: 'aria2.addUri',
  216. params: [ [ url ], { header, out: fileName } ],
  217. }),
  218. headers: {"Content-Type": "application/json"},
  219. onload: function(response) {
  220. // console.log(response.responseText);
  221. progress.style.background = 'green';
  222. const timeout = setTimeout(() => {
  223. progress.remove();
  224. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  225. }, 1000);
  226. progress.lastChild.onclick = function(e) {
  227. clearTimeout(timeout);
  228. this.parentNode.remove();
  229. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  230. }
  231. resolve(response);
  232. },
  233. onabort: function(e) { resolve(null); },
  234. onerror: function(e) { downloadError(e, url, fileName, headerFlag, progress); resolve(null); },
  235. ontimeout: function(e) { downloadError(e, url, fileName, headerFlag, progress); resolve(null); },
  236. });
  237. }
  238. })
  239. // 下面这种原生的方法,因为安全原因,不被浏览器允许,属于跨域,且在https页面上请求http。
  240. /*let oReq = new XMLHttpRequest();
  241. oReq.open("POST", GM_getValue('ariaRpcUrl','http://localhost:6800/jsonrpc'));
  242. oReq.setRequestHeader('Content-Type', 'application/json')
  243. oReq.onload = (e) => {
  244. // console.log(response.responseText);
  245. progress.style.background = 'green';
  246. const timeout = setTimeout(() => {
  247. progress.remove();
  248. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  249. }, 1000);
  250. progress.lastChild.onclick = function(e) {
  251. clearTimeout(timeout);
  252. this.parentNode.remove();
  253. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  254. }
  255. resolve(oReq.response);
  256. };
  257. oReq.onerror = (e) => { downloadError(e, url, fileName, headerFlag, progress); resolve(null); };
  258. oReq.onabort = (e) => { resolve(null); };
  259. oReq.ontimeout = (e) => { downloadError(e, url, fileName, headerFlag, progress); resolve(null); };
  260. oReq.send(JSON.stringify({
  261. jsonrpc: '2.0',
  262. id: 'weibo',
  263. method: 'aria2.addUri',
  264. params: [ [ url ], { header, out: fileName } ],
  265. }));
  266. progress.lastChild.onclick = function(e) {
  267. oReq.abort();
  268. this.parentNode.remove();
  269. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  270. };*/
  271. });
  272. }
  273.  
  274. function httpRequest(url, method = 'GET', data = null) {
  275. return new Promise(function(resolve, reject) {
  276. let oReq = new XMLHttpRequest();
  277. oReq.open(method, url);
  278. oReq.responseType = 'json';
  279. oReq.onload = (e) => { resolve(oReq.response); };
  280. oReq.onerror = (e) => { resolve(null); };
  281. oReq.onabort = (e) => { resolve(null); };
  282. oReq.ontimeout = (e) => { resolve(null); };
  283. oReq.setRequestHeader('X-XSRF-TOKEN', getCookie('XSRF-TOKEN'));
  284. if(typeof(data) === 'string') {
  285. oReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  286. oReq.send(data);
  287. } else if(typeof(data) === 'object') {
  288. oReq.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
  289. oReq.send(JSON.stringify(data));
  290. } else {
  291. oReq.send();
  292. }
  293. });
  294. }
  295.  
  296. function gmHttpRequest(url, method = 'GET', data = null, ua = null) {
  297. return new Promise(function(resolve, reject) {
  298. // console.log(url);
  299. let headers = {
  300. 'Referer': 'https://' + location.host,
  301. 'Origin': 'https://' + location.host,
  302. 'X-XSRF-TOKEN': getCookie('XSRF-TOKEN'),
  303. };
  304. if(typeof(data) === 'string') {
  305. headers['Content-Type'] = 'application/x-www-form-urlencoded';
  306. } else if(typeof(data) === 'object') {
  307. headers['Content-Type'] = 'application/json;charset=UTF-8';
  308. }
  309. if(ua) {
  310. headers['User-Agent'] = ua;
  311. }
  312. // console.log(headers)
  313. GM_xmlhttpRequest({
  314. method,
  315. url,
  316. data,
  317. responseType: 'json',
  318. headers,
  319. onload: ({ status, response }) => {
  320. // console.log(response);
  321. resolve(response)
  322. },
  323. onabort: function(e) { resolve(null); },
  324. onerror: function(e) { resolve(null); },
  325. ontimeout: function(e) { resolve(null); },
  326. });
  327. });
  328. }
  329.  
  330. function downloadError(e, url, name, headerFlag, progress, zipMode = false, ariaMode = false) {
  331. // console.log(e, url);
  332. /*GM_notification({
  333. title: 'Download error',
  334. text: 'Error: ' + e.error + '\nUrl: ' + url,
  335. silent: true,
  336. timeout: 3,
  337. });*/
  338. progress.style.background = 'red';
  339. progress.firstChild.textContent = name + ' [' + (e.error || 'Unknown') + ']';
  340. progress.firstChild.style.color = 'yellow';
  341. progress.firstChild.style.mixBlendMode = 'unset';
  342. if (!zipMode) {
  343. let progressRetryBtn = document.createElement('button');
  344. progressRetryBtn.style.border = 'unset';
  345. progressRetryBtn.style.background = 'unset';
  346. progressRetryBtn.style.color = 'yellow';
  347. progressRetryBtn.style.position = 'absolute';
  348. progressRetryBtn.style.right = '1.2rem';
  349. progressRetryBtn.style.top = '0.05rem';
  350. progressRetryBtn.style.fontSize = '1rem';
  351. progressRetryBtn.style.lineHeight = '1rem';
  352. progressRetryBtn.style.cursor = 'pointer';
  353. progressRetryBtn.style.letterSpacing = '-0.2rem';
  354. progressRetryBtn.textContent = '⤤⤦';
  355. progressRetryBtn.title = text[10];
  356. progressRetryBtn.onmouseover = function(e){
  357. this.style.color = 'white';
  358. }
  359. progressRetryBtn.onmouseout = function(e){
  360. this.style.color = 'yellow';
  361. }
  362. progressRetryBtn.onclick = function(e) {
  363. this.parentNode.remove();
  364. if (ariaMode) {
  365. send2Aria2c(url, name, headerFlag);
  366. } else {
  367. downloadWrapper(url, name, headerFlag);
  368. }
  369. }
  370. progress.insertBefore(progressRetryBtn, progress.lastChild);
  371. }
  372. progress.lastChild.title = text[11];
  373. progress.lastChild.style.color = 'yellow';
  374. progress.lastChild.onmouseover = function(e){
  375. this.style.color = 'white';
  376. };
  377. progress.lastChild.onmouseout = function(e){
  378. this.style.color = 'yellow';
  379. };
  380. progress.lastChild.onclick = function(e) {
  381. this.parentNode.remove();
  382. if(progress.parent.childElementCount == 1) progress.parent.firstChild.style.display = 'none';
  383. };
  384. // setTimeout(() => { progress.remove(); if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none'; }, 1000);
  385. }
  386.  
  387. function getCookie(key = null) {
  388. let cookiesArr = document.cookie.split('; ');
  389. let cookiesObj = {};
  390. for (const cookie of cookiesArr) {
  391. let [ name, value ] = cookie.split('=');
  392. cookiesObj[name] = value;
  393. }
  394. if (key) {
  395. return cookiesObj[key];
  396. } else {
  397. return cookiesObj;
  398. }
  399. }
  400.  
  401. function downloadWrapper(url, name, headerFlag = false, zipMode = false) {
  402. // console.log(url);
  403. downloadQueueTitle.style.display = 'block';
  404. let progress = downloadQueueCard.appendChild(progressBar.cloneNode(true));
  405. progress.firstChild.textContent = name + ' [0%]';
  406. if (zipMode) {
  407. return new Promise(function(resolve, reject) {
  408. const download = GM_xmlhttpRequest({
  409. method: 'GET',
  410. url,
  411. responseType: 'blob',
  412. headers: headerFlag ? {
  413. 'Referer': 'https://' + location.host,
  414. 'Origin': 'https://' + location.host
  415. } : null,
  416. onprogress: (e) => {
  417. // e = { int done, finalUrl, bool lengthComputable, int loaded, int position, int readyState, response, str responseHeaders, responseText, responseXML, int status, statusText, int total, int totalSize }
  418. const percent = e.done / e.total * 100;
  419. progress.style.background = 'linear-gradient(to right, green ' + percent + '%, transparent ' + percent + '%)';
  420. progress.firstChild.textContent = name + ' [' + percent.toFixed(0) + '%]';
  421. },
  422. onload: ({ status, response }) => {
  423. const timeout = setTimeout(() => {
  424. progress.remove();
  425. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  426. }, 1000);
  427. progress.lastChild.onclick = function(e) {
  428. clearTimeout(timeout);
  429. this.parentNode.remove();
  430. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  431. };
  432. resolve(response);
  433. },
  434. onabort: function(e) { resolve(null); },
  435. onerror: function(e) { downloadError(e, url, name, headerFlag, progress); resolve(null); },
  436. ontimeout: function(e) { downloadError(e, url, name, headerFlag, progress); resolve(null); },
  437. });
  438. progress.lastChild.onclick = function(e) {
  439. download.abort();
  440. this.parentNode.remove();
  441. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  442. };
  443. // 下面这种原生的方法,可以正常下载非V+的资源,遇到V+的资源会报错。
  444. /*let oReq = new XMLHttpRequest();
  445. oReq.open("GET", url);
  446. oReq.responseType = 'blob';
  447. oReq.onprogress = (e) => {
  448. // console.log(e);
  449. const percent = e.loaded / e.total * 100;
  450. progress.style.background = 'linear-gradient(to right, green ' + percent + '%, transparent ' + percent + '%)';
  451. progress.firstChild.textContent = name + ' [' + percent.toFixed(0) + '%]';
  452. };
  453. oReq.onload = (e) => {
  454. const timeout = setTimeout(() => {
  455. progress.remove();
  456. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  457. }, 1000);
  458. progress.lastChild.onclick = function(e) {
  459. clearTimeout(timeout);
  460. this.parentNode.remove();
  461. oReq.abort();
  462. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  463. };
  464. resolve(oReq.response);
  465. };
  466. oReq.onerror = (e) => { downloadError(e, url, name, headerFlag, progress); resolve(null); };
  467. oReq.onabort = (e) => { resolve(null); };
  468. oReq.ontimeout = (e) => { downloadError(e, url, name, headerFlag, progress); resolve(null); };
  469. oReq.send();
  470. progress.lastChild.onclick = function(e) {
  471. this.parentNode.remove();
  472. oReq.abort();
  473. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  474. };*/
  475. });
  476. } else {
  477. /*const download = GM_download({
  478. url,
  479. name,
  480. headers: headerFlag ? {
  481. 'Referer': 'https://' + location.host,
  482. 'Origin': 'https://' + location.host
  483. } : null,
  484. onprogress: (e) => {
  485. // e = { int done, finalUrl, bool lengthComputable, int loaded, int position, int readyState, response, str responseHeaders, responseText, responseXML, int status, statusText, int total, int totalSize }
  486. const percent = e.done / e.total * 100;
  487. progress.style.background = 'linear-gradient(to right, green ' + percent + '%, transparent ' + percent + '%)';
  488. progress.firstChild.textContent = name + ' [' + percent.toFixed(0) + '%]';
  489. },
  490. onload: ({ status, response }) => {
  491. const timeout = setTimeout(() => {
  492. progress.remove();
  493. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  494. }, 1000);
  495. progress.lastChild.onclick = function(e) {
  496. clearTimeout(timeout);
  497. this.parentNode.remove();
  498. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  499. }
  500. },
  501. onerror: (e) => { downloadError(e, url, name, headerFlag, progress); },
  502. ontimeout: (e) => { downloadError(e, url, name, headerFlag, progress); },
  503. });
  504. progress.lastChild.onclick = function(e) {
  505. download.abort();
  506. this.parentNode.remove();
  507. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  508. };*/
  509. // 这个方法也可以,而且不是走GM_download,不会被油猴设置里的下载选项影响。
  510. const download = GM_xmlhttpRequest({
  511. method: 'GET',
  512. url,
  513. responseType: 'blob',
  514. headers: headerFlag ? {
  515. 'Referer': 'https://' + location.host,
  516. 'Origin': 'https://' + location.host
  517. } : null,
  518. onprogress: (e) => {
  519. // e = { int done, finalUrl, bool lengthComputable, int loaded, int position, int readyState, response, str responseHeaders, responseText, responseXML, int status, statusText, int total, int totalSize }
  520. const percent = e.done / e.total * 100;
  521. progress.style.background = 'linear-gradient(to right, green ' + percent + '%, transparent ' + percent + '%)';
  522. progress.firstChild.textContent = name + ' [' + percent.toFixed(0) + '%]';
  523. },
  524. onload: ({ status, response }) => {
  525. const timeout = setTimeout(() => {
  526. progress.remove();
  527. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  528. }, 1000);
  529. progress.lastChild.onclick = function(e) {
  530. clearTimeout(timeout);
  531. this.parentNode.remove();
  532. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  533. };
  534. saveAs(response, name);
  535. },
  536. });
  537. progress.lastChild.onclick = function(e) {
  538. download.abort();
  539. this.parentNode.remove();
  540. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  541. };
  542. // 下面这种原生的方法,可以正常下载非V+的资源,遇到V+的资源会报错。
  543. /*let oReq = new XMLHttpRequest();
  544. oReq.open("GET", url);
  545. oReq.responseType = 'blob';
  546. oReq.onprogress = (e) => {
  547. // console.log(e);
  548. const percent = e.loaded / e.total * 100;
  549. progress.style.background = 'linear-gradient(to right, green ' + percent + '%, transparent ' + percent + '%)';
  550. progress.firstChild.textContent = name + ' [' + percent.toFixed(0) + '%]';
  551. };
  552. oReq.onload = (e) => {
  553. const timeout = setTimeout(() => {
  554. progress.remove();
  555. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  556. }, 1000);
  557. progress.lastChild.onclick = function(e) {
  558. clearTimeout(timeout);
  559. this.parentNode.remove();
  560. oReq.abort();
  561. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  562. };
  563. saveAs(oReq.response, name);
  564. };
  565. oReq.onerror = (e) => { downloadError(e, url, name, headerFlag, progress); };
  566. oReq.ontimeout = (e) => { downloadError(e, url, name, headerFlag, progress); };
  567. oReq.send();
  568. progress.lastChild.onclick = function(e) {
  569. this.parentNode.remove();
  570. oReq.abort();
  571. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  572. };*/
  573. // 下面fetch的方法,感觉不是很好写,所以就不用下面的方法。
  574. /*(async function () {
  575. let controller = new AbortController();
  576. const response = await fetch(url, { signal: controller.signal });
  577. progress.lastChild.onclick = function(e) {
  578. controller.abort();
  579. this.parentNode.remove();
  580. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  581. }
  582. const contentLength = response.headers.get('content-length');
  583. const total = parseInt(contentLength, 10);
  584. let loaded = 0;
  585. const reader = response.body.getReader();
  586. const res = new Response(new ReadableStream({
  587. async start(controller) {
  588. while(true) {
  589. const { done, value } = await reader.read();
  590. if (value) loaded += value.length;
  591. const percent = loaded / total * 100;
  592. progress.style.background = 'linear-gradient(to right, green ' + percent + '%, transparent ' + percent + '%)';
  593. progress.firstChild.textContent = name + ' [' + percent.toFixed(0) + '%]';
  594. if (done) {
  595. break;
  596. controller.close();
  597. }
  598. }
  599. }
  600. }));
  601. const blob = await res.blob();
  602. const link = document.createElement("a");
  603. link.style.display = "none";
  604. link.href = URL.createObjectURL(blob);
  605. link.download = name;
  606. link.target = '_blank';
  607. document.body.appendChild(link);
  608. link.click();
  609. progress.style.background = 'green';
  610. const timeout = setTimeout(() => {
  611. progress.remove();
  612. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  613. URL.revokeObjectURL(link.href);
  614. link.parentNode.removeChild(link);
  615. }, 1000);
  616. progress.lastChild.onclick = function(e) {
  617. clearTimeout(timeout);
  618. this.parentNode.remove();
  619. URL.revokeObjectURL(link.href);
  620. link.parentNode.removeChild(link);
  621. if(downloadQueueCard.childElementCount == 1) downloadQueueTitle.style.display = 'none';
  622. }
  623. })();*/
  624. }
  625. }
  626.  
  627. function getName(nameSetting, originalName, ext, userName, userId, postId, postUid, index, postTime, content, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetContent) {
  628. let setName = nameSetting;
  629. setName = setName.replace('{ext}', ext);
  630. setName = setName.replace('{original}', originalName);
  631. setName = setName.replace('{username}', userName);
  632. setName = setName.replace('{userid}', userId);
  633. setName = setName.replace('{mblogid}', postId);
  634. setName = setName.replace('{uid}', postUid);
  635. setName = setName.replace('{index}', index);
  636. setName = setName.replace('{content}', content.substring(0, 25));
  637. let YYYY, MM, DD, HH, mm, ss;
  638. const postAt = new Date(postTime);
  639. if (postTime) {
  640. YYYY = postAt.getFullYear().toString();
  641. MM = (postAt.getMonth() + 1).toString().padStart(2, '0');
  642. DD = postAt.getDate().toString().padStart(2, '0');
  643. HH = postAt.getHours().toString().padStart(2, '0');
  644. mm = postAt.getMinutes().toString().padStart(2, '0');
  645. ss = postAt.getSeconds().toString().padStart(2, '0');
  646. }
  647. setName = setName.replace('{YYYY}', YYYY);
  648. setName = setName.replace('{MM}', MM);
  649. setName = setName.replace('{DD}', DD);
  650. setName = setName.replace('{HH}', HH);
  651. setName = setName.replace('{mm}', mm);
  652. setName = setName.replace('{ss}', ss);
  653. if (retweetPostId && GM_getValue('retweetMode', false)) {
  654. setName = setName.replace('{re.mblogid}', retweetPostId);
  655. setName = setName.replace('{re.username}', retweetUserName);
  656. setName = setName.replace('{re.userid}', retweetUserId);
  657. setName = setName.replace('{re.uid}', retweetPostUid);
  658. setName = setName.replace('{re.content}', retweetContent.substring(0, 25));
  659. let reYYYY, reMM, reDD, reHH, remm, ress;
  660. const retweetPostAt = new Date(retweetPostTime);
  661. if (retweetPostTime) {
  662. reYYYY = retweetPostAt.getFullYear().toString();
  663. reMM = (retweetPostAt.getMonth() + 1).toString().padStart(2, '0');
  664. reDD = retweetPostAt.getDate().toString().padStart(2, '0');
  665. reHH = retweetPostAt.getHours().toString().padStart(2, '0');
  666. remm = retweetPostAt.getMinutes().toString().padStart(2, '0');
  667. ress = retweetPostAt.getSeconds().toString().padStart(2, '0');
  668. }
  669. setName = setName.replace('{re.YYYY}', reYYYY);
  670. setName = setName.replace('{re.MM}', reMM);
  671. setName = setName.replace('{re.DD}', reDD);
  672. setName = setName.replace('{re.HH}', reHH);
  673. setName = setName.replace('{re.mm}', remm);
  674. setName = setName.replace('{re.ss}', ress);
  675. }
  676. return setName.replace(/[<|>|*|"|\/|\|:|?|\n]/g, '_');
  677. }
  678.  
  679. function handleDownloadList(downloadList, packName) {
  680. if (GM_getValue('ariaMode', false)) {
  681. for (const item of downloadList) {
  682. send2Aria2c(item.url, item.name, item.headerFlag);
  683. }
  684. } else if (GM_getValue('zipMode', false)) {
  685. let zip = new JSZip();
  686. // console.log('zip', zip);
  687. let promises = downloadList.map(async function(ele, idx) {
  688. return await downloadWrapper(ele.url, ele.name, ele.headerFlag, true).then(function(data) {
  689. // console.log(ele, idx, 'data', data);
  690. const currDate = new Date();
  691. const dateWithOffset = new Date(currDate.getTime() - currDate.getTimezoneOffset() * 60000);
  692. if (data) zip.file(downloadList[idx].name, data, { date: dateWithOffset });
  693. });
  694. });
  695. // console.log('promises', promises);
  696. Promise.all(promises).then(async function(responseList) {
  697. // console.log('responseList', responseList);
  698. // console.log('zip', zip);
  699. // console.log('generateAsync', zip.generateAsync());
  700. const content = await zip.generateAsync({ type: 'blob', streamFiles: true }/*, function({ percent, currentFile }) { console.log(percent); }*/);
  701. // console.log('content', content);
  702. if (zip.files && Object.keys(zip.files).length > 0) saveAs(content, packName);
  703. });
  704. } else {
  705. for (const item of downloadList) {
  706. downloadWrapper(item.url, item.name, item.headerFlag);
  707. }
  708. }
  709. }
  710.  
  711. async function handleVideo(mediaInfo, padLength, userName, userId, postId, postUid, index, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText) {
  712. const newList = [];
  713. let largeVidUrl = mediaInfo.playback_list ? mediaInfo.playback_list[0].play_info.url : ( mediaInfo.mp4_hd_url || mediaInfo.stream_url_hd || mediaInfo.stream_url );
  714. if(mediaInfo.hasOwnProperty('h5_url') && mediaInfo.h5_url) {
  715. const urlObj = new URL(mediaInfo.h5_url); // e.g. 'https://video.weibo.com/show?fid=1034:4924511439749139'
  716. const fid = urlObj.searchParams.get('fid');
  717. let url = 'https://' + location.host + '/tv/api/component?page=/tv/show/' + fid; // e.g. 'https://weibo.com/tv/api/component?page=/tv/show/1034:4924511439749139'
  718. // let url = 'https://h5.video.weibo.com/api/component?page=/show/' + fid; // e.g. 'https://h5.video.weibo.com/api/component?page=/show/1034:5070572795658319'
  719. let data = 'data={"Component_Play_Playinfo":{"oid":"' + fid + '"}}'; // e.g. 'data={"Component_Play_Playinfo":{"oid":"1034:4924511439749139"}}'
  720. // console.log(url, data);
  721. let tvRes = await gmHttpRequest(url, 'POST', data);
  722. // console.log(tvRes);
  723. if(tvRes && tvRes.data && tvRes.data.Component_Play_Playinfo && tvRes.data.Component_Play_Playinfo.urls && Object.keys(tvRes.data.Component_Play_Playinfo.urls).length > 0) {
  724. largeVidUrl = tvRes.data.Component_Play_Playinfo.urls[Object.keys(tvRes.data.Component_Play_Playinfo.urls)[0]];
  725. if(largeVidUrl.startsWith('//')) {
  726. largeVidUrl = 'http:' + largeVidUrl;
  727. }
  728. }
  729. }
  730. // console.log(largeVidUrl);
  731. let vidName = largeVidUrl.split('?')[0];
  732. vidName = vidName.split('/')[vidName.split('/').length - 1].split('?')[0];
  733. let originalName = vidName.split('.')[0];
  734. let ext = vidName.split('.')[1];
  735. const setName = getName((GM_getValue('retweetMode', false) && retweetPostId) ? GM_getValue('retweetFileName', '{original}.{ext}') : GM_getValue('dlFileName', '{original}.{ext}'), originalName, ext, userName, userId, postId, postUid, index.toString().padStart(padLength, '0'), postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText);
  736. newList.push({ url: largeVidUrl, name: setName, headerFlag: true });
  737. if(GM_getValue('dlVidCov', true) && mediaInfo.hasOwnProperty('big_pic_info')) {
  738. let picUrl = mediaInfo.big_pic_info.pic_big.url;
  739. let largePicUrl = picUrl.replace('/orj480/', GM_getValue('rmWtrMrk', false) ? '/oslarge/' : '/large/');
  740. let picName = largePicUrl.split('/')[largePicUrl.split('/').length - 1].split('?')[0];
  741. let originalName = picName.split('.')[0];
  742. let ext = picName.split('.')[1];
  743. const setName = getName((GM_getValue('retweetMode', false) && retweetPostId) ? GM_getValue('retweetFileName', '{original}.{ext}') : GM_getValue('dlFileName', '{original}.{ext}'), originalName, ext, userName, userId, postId, postUid, index.toString().padStart(padLength, '0'), postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText);
  744. newList.push({url: largePicUrl, name: setName, headerFlag: true });
  745. }
  746. return newList;
  747. }
  748.  
  749. function handlePic(pic, padLength, userName, userId, postId, postUid, index, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText) {
  750. let newList = [];
  751. let picId = pic.pic_id;
  752. let picUrl = pic.largest?.url || pic.pic_big?.url;
  753. let picSize = picUrl.split('/')[3];
  754. let largePicUrl = picUrl.replace('/' + picSize + '/', GM_getValue('rmWtrMrk', false) ? '/oslarge/' : '/large/');
  755. let downloadUrl = GM_getValue('rmWtrMrk', false) ? largePicUrl : ('https://weibo.com/ajax/common/download?pid=' + picId);
  756. let picName = largePicUrl.split('/')[largePicUrl.split('/').length - 1].split('?')[0];
  757. let originalName = picName.split('.')[0];
  758. let ext = picName.split('.')[1];
  759. const setName = getName((GM_getValue('retweetMode', false) && retweetPostId) ? GM_getValue('retweetFileName', '{original}.{ext}') : GM_getValue('dlFileName', '{original}.{ext}'), originalName, ext, userName, userId, postId, postUid, index.toString().padStart(padLength, '0'), postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText);
  760. newList.push({ url: GM_getValue('ariaMode', false) ? largePicUrl : downloadUrl, name: setName, headerFlag: true });
  761. if(pic.hasOwnProperty('video')) {
  762. let videoUrl = pic.video;
  763. let videoName = videoUrl.split('%2F')[videoUrl.split('%2F').length - 1].split('?')[0];
  764. videoName = videoName.split('/')[videoName.split('/').length - 1].split('?')[0];
  765. if (!videoName.includes('.')) videoName = videoUrl.split('/')[videoUrl.split('/').length - 1].split('?')[0];
  766. // console.log(videoUrl, videoName);
  767. let originalName = videoName.split('.')[0];
  768. let ext = videoName.split('.')[1];
  769. const setName = getName((GM_getValue('retweetMode', false) && retweetPostId) ? GM_getValue('retweetFileName', '{original}.{ext}') : GM_getValue('dlFileName', '{original}.{ext}'), originalName, ext, userName, userId, postId, postUid, index.toString().padStart(padLength, '0'), postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText);
  770. newList.push({ url: videoUrl, name: setName, headerFlag: true });
  771. }
  772. return newList;
  773. }
  774.  
  775. function addDlBtn(footer) {
  776. // console.log('add download button');
  777. let dlBtnDiv = document.createElement('div');
  778. dlBtnDiv.className = 'woo-box-item-flex toolbar_item_1ky_D toolbar_cursor_34j5V';
  779. let divInDiv = document.createElement('div');
  780. divInDiv.className = 'woo-box-flex woo-box-alignCenter woo-box-justifyCenter toolbar_like_20yPI toolbar_likebox_1rLfZ toolbar_wrap_np6Ug';
  781. let dlBtn = document.createElement('button');
  782. dlBtn.className = 'woo-like-main toolbar_btn_Cg9tz download-button';
  783. dlBtn.setAttribute('tabindex', '0');
  784. dlBtn.setAttribute('title', '下载');
  785. // dlBtn.innerHTML = '<span class="woo-like-iconWrap"><svg class="woo-like-icon"><use xlink:href="#woo_svg_download"></use></svg></span><span class="woo-like-count">下载</span>';
  786. dlBtn.innerHTML = '<span class="woo-like-iconWrap"><i class="woo-font woo-font--imgSave woo-like-icon"></i></span><span class="woo-like-count">下载</span>';
  787. dlBtn.addEventListener('click', async function(event) {
  788. event.preventDefault();
  789. const article = this.closest('article.woo-panel-main');
  790. if(article) {
  791. // let contentRow = article.getElementsByClassName('content_row_-r5Tk')[0];
  792. const header = article.getElementsByTagName('header')[0];
  793. const postLink = header.getElementsByClassName('head-info_time_6sFQg')[0];
  794. let postId = postLink.href.split('/')[postLink.href.split('/').length - 1];
  795. const resJson = await httpRequest('https://' + location.host + '/ajax/statuses/show?id=' + postId);
  796. // console.log(resJson);
  797. let status = resJson;
  798. let retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText;
  799. if(resJson.hasOwnProperty('retweeted_status')) {
  800. status = resJson.retweeted_status;
  801. retweetPostId = resJson.mblogid;
  802. retweetUserName = resJson.user.screen_name;
  803. retweetUserId = resJson.user.idstr;
  804. retweetPostUid = resJson.idstr;
  805. retweetPostTime = resJson.created_at;
  806. retweetText = resJson.text_raw;
  807. }
  808. postId = status.mblogid;
  809. const picInfos = status.pic_infos;
  810. const picIds = status.pic_ids;
  811. const mixMediaInfo = status.mix_media_info;
  812. const userName = status.user.screen_name;
  813. const userId = status.user.idstr;
  814. const postUid = status.idstr;
  815. const postTime = status.created_at;
  816. const text = status.text_raw;
  817. let downloadList = [];
  818. if(footer.parentElement.getElementsByTagName('video').length > 0) {
  819. // console.log('download video');
  820. if(resJson.page_info?.media_info) {
  821. downloadList = downloadList.concat(await handleVideo(resJson.page_info.media_info, 1, userName, userId, postId, postUid, 1, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  822. }
  823. /*if(resJson.page_info?.pic_info && GM_getValue('dlVidCov', true)) {
  824. downloadList = downloadList.concat(handlePic(resJson.page_info.pic_info, 1, userName, userId, postId, postUid, 1, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  825. }*/
  826. }
  827. if (picInfos) {
  828. // console.log('download images');
  829. let index = 0;
  830. let padLength = Object.entries(picInfos).length.toString().length;
  831. for (const [id, pic] of Object.entries(picInfos)) {
  832. index += 1;
  833. downloadList = downloadList.concat(handlePic(pic, padLength, userName, userId, postId, postUid, index, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  834. }
  835. }
  836. /*if (picIds) {
  837. // console.log('download images');
  838. let index = 0;
  839. let padLength = picIds.length.toString().length;
  840. for (const picId of Object.entries(picIds)) {
  841. index += 1;
  842. downloadList = downloadList.concat(handlePic(picId, padLength, userName, userId, postId, postUid, index, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  843. }
  844. }*/
  845. if (mixMediaInfo && mixMediaInfo.items) {
  846. // console.log('mix media');
  847. let index = 0;
  848. let padLength = Object.entries(mixMediaInfo.items).length.toString().length;
  849. for (const [id, media] of Object.entries(mixMediaInfo.items)) {
  850. index += 1;
  851. if(media.type === 'video') {
  852. downloadList = downloadList.concat(await handleVideo(media.data.media_info, padLength, userName, userId, postId, postUid, index, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  853. if (GM_getValue('dlVidCov', true)) {
  854. downloadList = downloadList.concat(handlePic(media.data.pic_info, padLength, userName, userId, postId, postUid, index, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  855. }
  856. } else if (media.type === 'pic') {
  857. downloadList = downloadList.concat(handlePic(media.data, padLength, userName, userId, postId, postUid, index, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  858. }
  859. }
  860. }
  861. const packName = getName((GM_getValue('retweetMode', false) && retweetPostId) ? GM_getValue('retweetPackFileName', '{mblogid}.zip') : GM_getValue('packFileName', '{mblogid}.zip'), '{original}', '{ext}', userName, userId, postId, postUid, '{index}', postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText);
  862. handleDownloadList(downloadList, packName);
  863. }
  864. });
  865. divInDiv.appendChild(dlBtn);
  866. dlBtnDiv.appendChild(divInDiv);
  867. footer.firstChild.firstChild.firstChild.appendChild(dlBtnDiv);
  868. // console.log('added download button');
  869. }
  870.  
  871. function addSingleDlBtn(img, idx = 0) {
  872. // console.log(img);
  873. const imgCtn = img.parentElement;
  874. const dlBtn = document.createElement('div');
  875. dlBtn.style.color = 'dimgray';
  876. dlBtn.style.position = 'absolute';
  877. dlBtn.style.bottom = '0';
  878. dlBtn.style.left = '0';
  879. dlBtn.style.backgroundColor = 'rgba(255, 255, 255, 0.4)';
  880. dlBtn.style.padding = '0.3rem';
  881. dlBtn.style.borderRadius = '0 8px';
  882. dlBtn.style.width = '1rem';
  883. dlBtn.style.height = '1rem';
  884. dlBtn.style.cursor = 'pointer';
  885. dlBtn.style.zIndex = '11';
  886. dlBtn.innerHTML = '<i class="woo-font woo-font--imgSave"></i>';
  887. dlBtn.addEventListener('mouseenter', (event) => { dlBtn.style.backgroundColor = 'rgba(255, 255, 255, 0.8)'; dlBtn.style.color = 'black'; });
  888. dlBtn.addEventListener('mouseleave', (event) => { dlBtn.style.backgroundColor = 'rgba(255, 255, 255, 0.4)'; dlBtn.style.color = 'dimgray'; });
  889. dlBtn.addEventListener('click', async function(event) {
  890. event.stopPropagation();
  891. const article = this.closest('article.woo-panel-main');
  892. if(article) {
  893. // let contentRow = article.getElementsByClassName('content_row_-r5Tk')[0];
  894. const header = article.getElementsByTagName('header')[0];
  895. const postLink = header.getElementsByClassName('head-info_time_6sFQg')[0];
  896. let postId = postLink.href.split('/')[postLink.href.split('/').length - 1];
  897. const resJson = await httpRequest('https://' + location.host + '/ajax/statuses/show?id=' + postId);
  898. // console.log(resJson);
  899. let status = resJson;
  900. let retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText;
  901. if(resJson.hasOwnProperty('retweeted_status')) {
  902. status = resJson.retweeted_status;
  903. retweetPostId = resJson.mblogid;
  904. retweetUserName = resJson.user.screen_name;
  905. retweetUserId = resJson.user.idstr;
  906. retweetPostUid = resJson.idstr;
  907. retweetPostTime = resJson.created_at;
  908. retweetText = resJson.text_raw;
  909. }
  910. postId = status.mblogid;
  911. const picInfos = status.pic_infos;
  912. const picIds = status.pic_ids;
  913. const mixMediaInfo = status.mix_media_info;
  914. const userName = status.user.screen_name;
  915. const userId = status.user.idstr;
  916. const postUid = status.idstr;
  917. const postTime = status.created_at;
  918. const text = status.text_raw;
  919. let downloadList = [];
  920. if (picInfos) {
  921. // console.log('download images');
  922. let padLength = Object.entries(picInfos).length.toString().length;
  923. // console.log(idx, picInfos);
  924. const pic = Object.entries(picInfos)[idx][1];
  925. downloadList = downloadList.concat(handlePic(pic, padLength, userName, userId, postId, postUid, idx + 1, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  926. }
  927. /*if (picIds) {
  928. // console.log('download images');
  929. let padLength = picIds.length.toString().length;
  930. // console.log(idx, picInfos);
  931. const picId = picIds[idx];
  932. downloadList = downloadList.concat(handlePic(picId, padLength, userName, userId, postId, postUid, idx + 1, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  933. }*/
  934. if (mixMediaInfo && mixMediaInfo.items) {
  935. // console.log('mix media');
  936. // console.log(mixMediaInfo.items);
  937. let padLength = Object.entries(mixMediaInfo.items).length.toString().length;
  938. const media = Object.entries(mixMediaInfo.items)[idx][1];
  939. if(media.type === 'video') {
  940. downloadList = downloadList.concat(await handleVideo(media.data.media_info, padLength, userName, userId, postId, postUid, idx + 1, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  941. if(GM_getValue('dlVidCov', true)) {
  942. downloadList = downloadList.concat(handlePic(media.data.pic_info, padLength, userName, userId, postId, postUid, idx + 1, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  943. }
  944. } else if (media.type === 'pic') {
  945. downloadList = downloadList.concat(handlePic(media.data, padLength, userName, userId, postId, postUid, idx + 1, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  946. }
  947. }
  948. const packName = getName((GM_getValue('retweetMode', false) && retweetPostId) ? GM_getValue('retweetPackFileName', '{mblogid}.zip') : GM_getValue('packFileName', '{mblogid}.zip'), '{original}', '{ext}', userName, userId, postId, postUid, '{index}', postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText);
  949. handleDownloadList(downloadList, packName);
  950. }
  951. });
  952. imgCtn.appendChild(dlBtn);
  953. }
  954.  
  955. function sAddDlBtn(footer) {
  956. // console.log('add download button on search');
  957. const lis = footer.getElementsByTagName('li');
  958. for (const li of lis) {
  959. li.style.width = '25%';
  960. }
  961. let dlBtnLi = document.createElement('li');
  962. dlBtnLi.style.width = '25%';
  963. let aInLi = document.createElement('a');
  964. aInLi.className = 'woo-box-flex woo-box-alignCenter woo-box-justifyCenter';
  965. aInLi.setAttribute('title', '下载');
  966. aInLi.setAttribute('href', 'javascript:void(0);');
  967. let dlBtn = document.createElement('button');
  968. dlBtn.className = 'woo-like-main toolbar_btn download-button';
  969. dlBtn.innerHTML = '<span class="woo-like-iconWrap"><svg class="woo-like-icon"><use xlink:href="#woo_svg_download"></use></svg></span><span class="woo-like-count">下载</span>';
  970. aInLi.addEventListener('click', function(event) { event.preventDefault(); });
  971. dlBtn.addEventListener('click', async function(event) {
  972. // console.log('download');
  973. event.preventDefault();
  974. const cardWrap = this.closest('div.card-wrap');
  975. // console.log(cardWrap);
  976. const mid = cardWrap.getAttribute('mid');
  977. // console.log(mid);
  978. if(mid) {
  979. // console.log('https://' + location.host + '/ajax/statuses/show?id=' + mid);
  980. const resJson = await gmHttpRequest('https://weibo.com/ajax/statuses/show?id=' + mid);
  981. // console.log(resJson);
  982. let status = resJson;
  983. let retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText;
  984. if(resJson.hasOwnProperty('retweeted_status')) {
  985. status = resJson.retweeted_status;
  986. retweetPostId = resJson.mblogid;
  987. retweetUserName = resJson.user.screen_name;
  988. retweetUserId = resJson.user.idstr;
  989. retweetPostUid = resJson.idstr;
  990. retweetPostTime = resJson.created_at;
  991. retweetText = resJson.text_raw;
  992. }
  993. const postId = status.mblogid;
  994. const picInfos = status.pic_infos;
  995. const picIds = status.pic_ids;
  996. const mixMediaInfo = status.mix_media_info;
  997. const userName = status.user.screen_name;
  998. const userId = status.user.idstr;
  999. const postUid = status.idstr;
  1000. const postTime = status.created_at;
  1001. const text = status.text_raw;
  1002. let downloadList = [];
  1003. if(footer.parentElement.getElementsByTagName('video').length > 0) {
  1004. // console.log('download video');
  1005. if(resJson.page_info?.media_info) {
  1006. downloadList = downloadList.concat(await handleVideo(resJson.page_info.media_info, 1, userName, userId, postId, postUid, 1, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  1007. }
  1008. if(resJson.page_info?.pic_info && GM_getValue('dlVidCov', true)) {
  1009. downloadList = downloadList.concat(handlePic(resJson.page_info.pic_info, 1, userName, userId, postId, postUid, 1, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  1010. }
  1011. }
  1012. if (picInfos) {
  1013. // console.log('download images');
  1014. let index = 0;
  1015. let padLength = Object.entries(picInfos).length.toString().length;
  1016. for (const [id, pic] of Object.entries(picInfos)) {
  1017. index += 1;
  1018. downloadList = downloadList.concat(handlePic(pic, padLength, userName, userId, postId, postUid, index, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  1019. }
  1020. }
  1021. /*if (picIds) {
  1022. // console.log('download images');
  1023. let index = 0;
  1024. let padLength = picIds.length.toString().length;
  1025. for (const picId of picIds) {
  1026. index += 1;
  1027. downloadList = downloadList.concat(handlePic(picId, padLength, userName, userId, postId, postUid, index, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  1028. }
  1029. }*/
  1030. if (mixMediaInfo && mixMediaInfo.items) {
  1031. // console.log('mix media');
  1032. let index = 0;
  1033. let padLength = Object.entries(mixMediaInfo.items).length.toString().length;
  1034. for (const [id, media] of Object.entries(mixMediaInfo.items)) {
  1035. index += 1;
  1036. if(media.type === 'video') {
  1037. downloadList = downloadList.concat(await handleVideo(media.data.media_info, padLength, userName, userId, postId, postUid, index, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  1038. if(GM_getValue('dlVidCov', true)) {
  1039. downloadList = downloadList.concat(handlePic(media.data.pic_info, padLength, userName, userId, postId, postUid, index, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  1040. }
  1041. } else if (media.type === 'pic') {
  1042. downloadList = downloadList.concat(handlePic(media.data, padLength, userName, userId, postId, postUid, index, postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText));
  1043. }
  1044. }
  1045. }
  1046. const packName = getName((GM_getValue('retweetMode', false) && retweetPostId) ? GM_getValue('retweetPackFileName', '{mblogid}.zip') : GM_getValue('packFileName', '{mblogid}.zip'), '{original}', '{ext}', userName, userId, postId, postUid, '{index}', postTime, text, retweetPostId, retweetUserName, retweetUserId, retweetPostUid, retweetPostTime, retweetText);
  1047. handleDownloadList(downloadList, packName);
  1048. }
  1049. });
  1050. aInLi.appendChild(dlBtn);
  1051. dlBtnLi.appendChild(dlBtn);
  1052. footer.firstChild.appendChild(dlBtnLi);
  1053. // console.log('added download button');
  1054. }
  1055. /*
  1056. function bodyMouseOver(event) {
  1057. if (location.host == 'weibo.com' || location.host == 'www.weibo.com') {
  1058. // let arts = document.getElementsByTagName('article');
  1059. const footers = document.getElementsByTagName('footer');
  1060. for (const footer of footers) {
  1061. if(footer.getElementsByClassName('download-button').length > 0) {
  1062. // console.log('already added download button');
  1063. } else {
  1064. // console.log(footer.parentElement);
  1065. if(footer.parentElement.tagName.toLowerCase() == 'article') {
  1066. const article = footer.parentElement;
  1067. const imgs = article.getElementsByTagName('img');
  1068. let added = false;
  1069. // console.log(imgs);
  1070. if(imgs.length > 0) {
  1071. let addFlag = false;
  1072. for (const img of imgs) {
  1073. if(['woo-picture-img', 'picture_focusImg_1z5In', 'picture-viewer_pic_37YQ3'].includes(img.className)) {
  1074. addFlag = true;
  1075. }
  1076. }
  1077. if(addFlag == true) {
  1078. addDlBtn(footer);
  1079. added = true;
  1080. }
  1081. }
  1082. let videos = article.getElementsByTagName('video');
  1083. if(videos.length > 0 && added == false) {
  1084. addDlBtn(footer);
  1085. }
  1086. }
  1087. }
  1088. }
  1089. }
  1090. if (location.host == 's.weibo.com') {
  1091. // let cards = document.querySelectorAll('#pl_feedlist_index .card-wrap');
  1092. const footers = document.querySelectorAll('#pl_feedlist_index .card-act');
  1093. for (const footer of footers) {
  1094. if(footer.getElementsByClassName('download-button').length > 0) {
  1095. // console.log('already added download button');
  1096. } else {
  1097. // console.log(footer.parentElement);
  1098. if(footer.parentElement.className == 'card' && footer.parentElement.parentElement.className == 'card-wrap') {
  1099. const card = footer.parentElement;
  1100. let added = false;
  1101. const media_prev = card.querySelector('div[node-type="feed_list_media_prev"]');
  1102. // console.log(media_prev);
  1103. if (media_prev) {
  1104. const imgs = media_prev.getElementsByTagName('img');
  1105. // console.log(imgs);
  1106. if(imgs.length > 0) {
  1107. sAddDlBtn(footer);
  1108. added = true;
  1109. }
  1110. const videos = card.getElementsByTagName('video');
  1111. if(videos.length > 0 && added == false) {
  1112. sAddDlBtn(footer);
  1113. }
  1114. }
  1115. }
  1116. }
  1117. }
  1118. }
  1119. }
  1120. */
  1121. function handleCard(card) {
  1122. // console.log(card);
  1123. const footer = card.querySelectorAll('footer')[1] || card.querySelector('footer');
  1124. const imgs = card.querySelectorAll('img.woo-picture-img,img.picture_focusImg_1z5In,img.picture-viewer_pic_37YQ3,video.picture-viewer_pic_37YQ3');
  1125. // console.log(imgs);
  1126. if (footer) {
  1127. if (footer.getElementsByClassName('download-button').length > 0) {
  1128. // console.log('already added download button');
  1129. } else {
  1130. // console.log(footer.parentElement);
  1131. let added = false;
  1132. if (imgs.length > 0) {
  1133. addDlBtn(footer);
  1134. added = true;
  1135. if (imgs.length > 1) {
  1136. for (const [ idx, img ] of Object.entries(imgs)) {
  1137. if (img.parentElement.getElementsByClassName('download-single-button').length === 0) {
  1138. if (img.className.includes('picture-viewer_pic_37YQ3')) {
  1139. const previews = card.querySelectorAll('div.picture-viewer_preview_2wOSq');
  1140. for (const [ index, preview ] of Object.entries(previews)) {
  1141. if (preview.className.includes('picture-viewer_cur_anUEY')) {
  1142. addSingleDlBtn(img, parseInt(index));
  1143. }
  1144. }
  1145. } else {
  1146. addSingleDlBtn(img, parseInt(idx));
  1147. }
  1148. }
  1149. }
  1150. }
  1151. }
  1152. let videos = card.getElementsByTagName('video');
  1153. if(videos.length > 0 && added == false) {
  1154. addDlBtn(footer);
  1155. }
  1156. }
  1157. }
  1158. }
  1159. /*
  1160. let startButton = document.createElement('button');
  1161. startButton.textContent = text[0];
  1162. startButton.id = 'startButton';
  1163. startButton.style.position = 'fixed';
  1164. startButton.style.top = '14rem';
  1165. startButton.style.left = '1rem';
  1166. startButton.style.zIndex = 400;
  1167. startButton.style.backgroundColor = 'black';
  1168. startButton.style.color = 'lightgray';
  1169. startButton.style.paddingLeft = '1rem';
  1170. startButton.style.paddingRight = '1rem';
  1171. startButton.style.paddingTop = '0.5rem';
  1172. startButton.style.paddingBottom = '0.5rem';
  1173. startButton.style.fontWeight = 'bold';
  1174. startButton.style.borderWidth = '0.15rem';
  1175. startButton.style.borderColor = 'lightgray';
  1176. startButton.style.borderRadius = '0.4rem';
  1177. startButton.style.borderStyle = 'solid';
  1178. startButton.addEventListener('mouseover', function(event) {
  1179. startButton.style.backgroundColor = 'lightgray';
  1180. startButton.style.color = 'black';
  1181. startButton.style.borderColor = 'black';
  1182. });
  1183. startButton.addEventListener('mouseout', function(event) {
  1184. startButton.style.backgroundColor = 'black';
  1185. startButton.style.color = 'lightgray';
  1186. startButton.style.borderColor = 'lightgray';
  1187. });
  1188. startButton.addEventListener('mousedown', function(event) {
  1189. startButton.style.backgroundColor = 'gray';
  1190. });
  1191. startButton.addEventListener('mouseup', function(event) {
  1192. startButton.style.backgroundColor = 'lightgray';
  1193. });
  1194. startButton.addEventListener('click', bodyMouseOver);
  1195.  
  1196. function addStartButton() {
  1197. document.body.appendChild(startButton);
  1198. document.body.removeEventListener('mouseover', bodyMouseOver)
  1199. }
  1200.  
  1201. function addEventListener() {
  1202. document.body.addEventListener('mouseover', bodyMouseOver);
  1203. if(document.getElementById('startButton')) {
  1204. document.body.removeChild(startButton);
  1205. }
  1206. }
  1207. */
  1208. // let addDlBtnMode = GM_getValue('addDlBtnMode', 0);
  1209.  
  1210. function showModal(event) {
  1211. // console.log(addDlBtnMode);
  1212. let bg = document.createElement('div');
  1213. bg.style.position = 'fixed';
  1214. bg.style.top = 0;
  1215. bg.style.left = 0;
  1216. bg.style.zIndex = 500;
  1217. bg.style.backgroundColor = 'black';
  1218. bg.style.opacity = 0.5;
  1219. let modal = document.createElement('div');
  1220. document.body.appendChild(bg);
  1221. modal.style.position = 'fixed';
  1222. modal.style.width = '25rem';
  1223. modal.style.height = 'auto';
  1224. modal.style.maxHeight = '80vh';
  1225. modal.style.zIndex = 600;
  1226. modal.style.backgroundColor = 'white';
  1227. modal.style.borderStyle = 'solid';
  1228. modal.style.borderWidth = '0.2rem';
  1229. modal.style.borderRadius = '0.5rem';
  1230. modal.style.borderColor = 'black';
  1231. modal.style.overflowX = 'hidden';
  1232. modal.style.overflowY = 'auto';
  1233. modal.style.fontSize = '1rem';
  1234. let titleBar = document.createElement('div');
  1235. titleBar.textContent = text[1];
  1236. titleBar.style.width = '100%';
  1237. titleBar.style.textAlign = 'center';
  1238. titleBar.style.backgroundColor = 'black';
  1239. titleBar.style.color = 'white';
  1240. titleBar.style.fontSize = '1rem';
  1241. titleBar.style.fontWeight = 'bold';
  1242. titleBar.style.paddingTop = '0.5rem';
  1243. titleBar.style.paddingBottom = '0.5rem';
  1244. titleBar.style.borderTopLeftRadius = '0.3rem';
  1245. titleBar.style.borderTopRightRadius = '0.3rem';
  1246. modal.appendChild(titleBar);
  1247. /*let question1 = document.createElement('p');
  1248. question1.textContent = text[2];
  1249. question1.style.paddingLeft = '2rem';
  1250. question1.style.paddingRight = '2rem';
  1251. question1.style.marginTop = '1rem';
  1252. question1.style.marginBottom = '1rem';
  1253. let chooseButton = document.createElement('input');
  1254. chooseButton.type = 'radio';
  1255. chooseButton.id = 'chooseButton';
  1256. chooseButton.name = 'chooseSetting';
  1257. chooseButton.value = 1;
  1258. chooseButton.style.margin = '0.5rem 0.5rem 0 0.5rem';
  1259. let labelForChooseButton = document.createElement('label');
  1260. labelForChooseButton.htmlFor = 'chooseButton';
  1261. labelForChooseButton.textContent = text[3];
  1262. let divForChooseButton = document.createElement('div');
  1263. divForChooseButton.appendChild(chooseButton);
  1264. divForChooseButton.appendChild(labelForChooseButton);
  1265. question1.appendChild(divForChooseButton);
  1266. let chooseEvent = document.createElement('input');
  1267. chooseEvent.type = 'radio';
  1268. chooseEvent.id = 'chooseEvent';
  1269. chooseEvent.name = 'chooseSetting';
  1270. chooseEvent.value = 2;
  1271. chooseEvent.style.margin = '0.5rem 0.5rem 0 0.5rem';
  1272. if (addDlBtnMode == 2) {
  1273. chooseEvent.checked = true;
  1274. } else {
  1275. chooseButton.checked = true;
  1276. }
  1277. let labelForChooseEvent = document.createElement('label');
  1278. labelForChooseEvent.htmlFor = 'chooseEvent';
  1279. labelForChooseEvent.textContent = text[4];
  1280. let divForChooseEvent = document.createElement('div');
  1281. divForChooseEvent.appendChild(chooseEvent);
  1282. divForChooseEvent.appendChild(labelForChooseEvent);
  1283. question1.appendChild(divForChooseEvent);
  1284. modal.appendChild(question1);*/
  1285. let question2 = document.createElement('p');
  1286. question2.style.paddingLeft = '2rem';
  1287. question2.style.paddingRight = '2rem';
  1288. question2.style.marginTop = '1rem';
  1289. question2.style.marginBottom = '1rem';
  1290. let labelFileName = document.createElement('label');
  1291. labelFileName.textContent = text[7];
  1292. labelFileName.setAttribute('for', 'dlFileName');
  1293. labelFileName.style.color = "black";
  1294. question2.appendChild(labelFileName);
  1295. let inputFileName = document.createElement('input');
  1296. inputFileName.type = 'text';
  1297. inputFileName.id = 'dlFileName';
  1298. inputFileName.name = 'dlFileName';
  1299. inputFileName.style.marginTop = '0.5rem';
  1300. inputFileName.style.width = 'calc(100% - 1rem)';
  1301. inputFileName.style.padding = '0.1rem 0.2rem 0.1rem 0.2rem';
  1302. inputFileName.style.borderStyle = 'solid';
  1303. inputFileName.style.borderColor = 'gray';
  1304. inputFileName.style.borderWidth = '0.14rem';
  1305. inputFileName.style.borderRadius = '0.2rem';
  1306. inputFileName.defaultValue = GM_getValue('dlFileName', '{original}.{ext}');
  1307. question2.appendChild(inputFileName);
  1308. let fileNameExplain1 = document.createElement('p');
  1309. fileNameExplain1.innerHTML = text[8];
  1310. fileNameExplain1.style.marginTop = '0.5rem';
  1311. fileNameExplain1.style.marginBottom = '0';
  1312. fileNameExplain1.style.whiteSpace = 'pre';
  1313. fileNameExplain1.style.color = 'gray';
  1314. question2.appendChild(fileNameExplain1);
  1315. let fileNameExplain2 = document.createElement('p');
  1316. fileNameExplain2.innerHTML = text[26] + text[27];
  1317. fileNameExplain2.style.marginTop = '0.5rem';
  1318. fileNameExplain2.style.whiteSpace = 'pre';
  1319. fileNameExplain2.style.color = 'gray';
  1320. question2.appendChild(fileNameExplain2);
  1321. modal.appendChild(question2);
  1322. let question3 = document.createElement('p');
  1323. question3.style.paddingLeft = '2rem';
  1324. question3.style.paddingRight = '2rem';
  1325. question3.style.marginTop = '1rem';
  1326. question3.style.marginBottom = '0';
  1327. let labelZipMode = document.createElement('label');
  1328. labelZipMode.setAttribute('for', 'zipMode');
  1329. labelZipMode.textContent = text[13];
  1330. labelZipMode.style.display = 'inline-block';
  1331. labelZipMode.style.paddingRight = '0.2rem';
  1332. labelZipMode.style.color = GM_getValue('ariaMode', false) ? "gray" : "black";
  1333. question3.appendChild(labelZipMode);
  1334. let inputZipMode = document.createElement('input');
  1335. inputZipMode.type = 'checkbox';
  1336. inputZipMode.id = 'zipMode';
  1337. inputZipMode.checked = GM_getValue('zipMode', false);
  1338. inputZipMode.disabled = GM_getValue('ariaMode', false);
  1339. question3.appendChild(inputZipMode);
  1340. let labelPackName = document.createElement('label');
  1341. labelPackName.textContent = text[14];
  1342. labelPackName.setAttribute('for', 'packFileName');
  1343. labelPackName.style.display = 'block';
  1344. labelPackName.style.marginTop = '0.5rem';
  1345. labelPackName.style.color = (GM_getValue('zipMode', false) && !GM_getValue('ariaMode', false)) ? "black" : "gray";
  1346. // labelPackName.style.display = GM_getValue('zipMode', false) ? 'block' : 'none';
  1347. question3.appendChild(labelPackName);
  1348. let inputPackName = document.createElement('input');
  1349. inputPackName.type = 'text';
  1350. inputPackName.id = 'packFileName';
  1351. inputPackName.name = 'packFileName';
  1352. inputPackName.style.marginTop = '0.5rem';
  1353. inputPackName.style.width = 'calc(100% - 1rem)';
  1354. inputPackName.style.padding = '0.1rem 0.2rem 0.1rem 0.2rem';
  1355. inputPackName.style.borderStyle = 'solid';
  1356. inputPackName.style.borderColor = (GM_getValue('zipMode', false) && !GM_getValue('ariaMode', false)) ? 'black' : 'gray';
  1357. inputPackName.style.borderWidth = '0.14rem';
  1358. inputPackName.style.borderRadius = '0.2rem';
  1359. inputPackName.defaultValue = GM_getValue('packFileName', '{mblogid}.zip');
  1360. // inputPackName.style.display = GM_getValue('zipMode', false) ? 'block' : 'none';
  1361. inputPackName.disabled = (GM_getValue('zipMode', false) && !GM_getValue('ariaMode', false)) ? false : true;
  1362. question3.appendChild(inputPackName);
  1363. let filePackExplain = document.createElement('p');
  1364. filePackExplain.textContent = text[15];
  1365. filePackExplain.style.marginTop = '0.5rem';
  1366. filePackExplain.style.marginBottom = '0';
  1367. filePackExplain.style.color = 'gray';
  1368. // filePackExplain.style.display = GM_getValue('zipMode', false) ? 'block' : 'none';
  1369. question3.appendChild(filePackExplain);
  1370. modal.appendChild(question3);
  1371. let question4 = document.createElement('p');
  1372. question4.style.paddingLeft = '2rem';
  1373. question4.style.paddingRight = '2rem';
  1374. question4.style.marginTop = '1rem';
  1375. question4.style.marginBottom = '0';
  1376. let labelRetweetMode = document.createElement('label');
  1377. labelRetweetMode.setAttribute('for', 'retweetMode');
  1378. labelRetweetMode.textContent = text[16];
  1379. labelRetweetMode.style.display = 'inline-block';
  1380. labelRetweetMode.style.paddingRight = '0.2rem';
  1381. labelRetweetMode.style.color = "black";
  1382. question4.appendChild(labelRetweetMode);
  1383. let inputRetweetMode = document.createElement('input');
  1384. inputRetweetMode.type = 'checkbox';
  1385. inputRetweetMode.id = 'retweetMode';
  1386. inputRetweetMode.checked = GM_getValue('retweetMode', false);
  1387. question4.appendChild(inputRetweetMode);
  1388. let labelRetweetFileName = document.createElement('label');
  1389. labelRetweetFileName.textContent = text[17];
  1390. labelRetweetFileName.setAttribute('for', 'retweetFileName');
  1391. labelRetweetFileName.style.display = 'block';
  1392. labelRetweetFileName.style.marginTop = '0.5rem';
  1393. labelRetweetFileName.style.color = GM_getValue('retweetMode', false) ? "black" : 'gray';
  1394. // labelPackName.style.display = GM_getValue('retweetMode', false) ? 'block' : 'none';
  1395. question4.appendChild(labelRetweetFileName);
  1396. let inputRetweetFileName = document.createElement('input');
  1397. inputRetweetFileName.type = 'text';
  1398. inputRetweetFileName.id = 'retweetFileName';
  1399. inputRetweetFileName.name = 'retweetFileName';
  1400. inputRetweetFileName.style.marginTop = '0.5rem';
  1401. inputRetweetFileName.style.width = 'calc(100% - 1rem)';
  1402. inputRetweetFileName.style.padding = '0.1rem 0.2rem 0.1rem 0.2rem';
  1403. inputRetweetFileName.style.borderStyle = 'solid';
  1404. inputRetweetFileName.style.borderColor = 'lightgray';
  1405. inputRetweetFileName.style.borderWidth = '0.14rem';
  1406. inputRetweetFileName.style.borderRadius = '0.2rem';
  1407. inputRetweetFileName.defaultValue = GM_getValue('retweetFileName', '{original}.{ext}');
  1408. // inputRetweetFileName.style.display = GM_getValue('retweetMode', false) ? 'block' : 'none';
  1409. inputRetweetFileName.disabled = GM_getValue('retweetMode', false) ? false : true;
  1410. question4.appendChild(inputRetweetFileName);
  1411. let retweetFileNameExplain = document.createElement('p');
  1412. retweetFileNameExplain.textContent = text[18];
  1413. retweetFileNameExplain.style.marginTop = '0.5rem';
  1414. retweetFileNameExplain.style.whiteSpace = 'pre';
  1415. retweetFileNameExplain.style.marginBottom = '0';
  1416. retweetFileNameExplain.style.color = 'gray';
  1417. // retweetFileNameExplain.style.display = GM_getValue('retweetMode', false) ? 'block' : 'none';
  1418. question4.appendChild(retweetFileNameExplain);
  1419. let labelRetweetPackName = document.createElement('label');
  1420. labelRetweetPackName.textContent = text[19];
  1421. labelRetweetPackName.setAttribute('for', 'retweetPackFileName');
  1422. labelRetweetPackName.style.display = 'block';
  1423. labelRetweetPackName.style.marginTop = '0.5rem';
  1424. labelRetweetPackName.style.color = (GM_getValue('zipMode', false) && GM_getValue('retweetMode', false) && !GM_getValue('ariaMode', false)) ? "black" : 'gray';
  1425. // labelRetweetPackName.style.display = GM_getValue('zipMode', false) ? 'block' : 'none';
  1426. question4.appendChild(labelRetweetPackName);
  1427. let inputRetweetPackName = document.createElement('input');
  1428. inputRetweetPackName.type = 'text';
  1429. inputRetweetPackName.id = 'retweetPackFileName';
  1430. inputRetweetPackName.name = 'retweetPackFileName';
  1431. inputRetweetPackName.style.marginTop = '0.5rem';
  1432. inputRetweetPackName.style.width = 'calc(100% - 1rem)';
  1433. inputRetweetPackName.style.padding = '0.1rem 0.2rem 0.1rem 0.2rem';
  1434. inputRetweetPackName.style.borderStyle = 'solid';
  1435. inputRetweetPackName.style.borderColor = 'lightgray';
  1436. inputRetweetPackName.style.borderWidth = '0.14rem';
  1437. inputRetweetPackName.style.borderRadius = '0.2rem';
  1438. inputRetweetPackName.defaultValue = GM_getValue('retweetPackFileName', '{mblogid}.zip');
  1439. // inputRetweetPackName.style.display = (GM_getValue('zipMode', false) && GM_getValue('retweetMode', false) && !GM_getValue('ariaMode', false)) ? 'block' : 'none';
  1440. inputRetweetPackName.disabled = (GM_getValue('zipMode', false) && GM_getValue('retweetMode', false) && !GM_getValue('ariaMode', false)) ? false : true;
  1441. question4.appendChild(inputRetweetPackName);
  1442. let retweetPackExplain = document.createElement('p');
  1443. retweetPackExplain.textContent = text[20];
  1444. retweetPackExplain.style.marginTop = '0.5rem';
  1445. retweetPackExplain.style.marginBottom = '0';
  1446. retweetPackExplain.style.color = 'gray';
  1447. // retweetPackExplain.style.display = (GM_getValue('zipMode', false) && GM_getValue('retweetMode', false) && !GM_getValue('ariaMode', false)) ? 'block' : 'none';
  1448. question4.appendChild(retweetPackExplain);
  1449. modal.appendChild(question4);
  1450. let question5 = document.createElement('p');
  1451. question5.style.paddingLeft = '2rem';
  1452. question5.style.paddingRight = '2rem';
  1453. question5.style.marginTop = '1rem';
  1454. question5.style.marginBottom = '0';
  1455. let labelAriaMode = document.createElement('label');
  1456. labelAriaMode.setAttribute('for', 'ariaMode');
  1457. labelAriaMode.textContent = text[21];
  1458. labelAriaMode.style.display = 'inline-block';
  1459. labelAriaMode.style.paddingRight = '0.2rem';
  1460. labelAriaMode.style.color = 'black';
  1461. question5.appendChild(labelAriaMode);
  1462. let inputAriaMode = document.createElement('input');
  1463. inputAriaMode.type = 'checkbox';
  1464. inputAriaMode.id = 'ariaMode';
  1465. inputAriaMode.checked = GM_getValue('ariaMode', false);
  1466. question5.appendChild(inputAriaMode);
  1467. let ariaModeExplain = document.createElement('p');
  1468. ariaModeExplain.textContent = text[23];
  1469. ariaModeExplain.style.marginTop = '0.5rem';
  1470. ariaModeExplain.style.marginBottom = '0';
  1471. ariaModeExplain.style.color = 'gray';
  1472. question5.appendChild(ariaModeExplain);
  1473. let labelAriaRpcUrl = document.createElement('label');
  1474. labelAriaRpcUrl.textContent = text[22];
  1475. labelAriaRpcUrl.setAttribute('for', 'ariaRpcUrl');
  1476. labelAriaRpcUrl.style.display = 'block';
  1477. labelAriaRpcUrl.style.marginTop = '0.5rem';
  1478. labelAriaRpcUrl.style.color = GM_getValue('ariaMode', false) ? "black" : "gray";
  1479. question5.appendChild(labelAriaRpcUrl);
  1480. let inputAriaRpcUrl = document.createElement('input');
  1481. inputAriaRpcUrl.type = 'text';
  1482. inputAriaRpcUrl.id = 'ariaRpcUrl';
  1483. inputAriaRpcUrl.name = 'ariaRpcUrl';
  1484. inputAriaRpcUrl.style.marginTop = '0.5rem';
  1485. inputAriaRpcUrl.style.width = 'calc(100% - 1rem)';
  1486. inputAriaRpcUrl.style.padding = '0.1rem 0.2rem 0.1rem 0.2rem';
  1487. inputAriaRpcUrl.style.borderStyle = 'solid';
  1488. inputAriaRpcUrl.style.borderColor = 'lightgray';
  1489. inputAriaRpcUrl.style.borderWidth = '0.14rem';
  1490. inputAriaRpcUrl.style.borderRadius = '0.2rem';
  1491. inputAriaRpcUrl.defaultValue = GM_getValue('ariaRpcUrl', 'http://localhost:6800/jsonrpc');
  1492. // inputAriaRpcUrl.style.display = GM_getValue('ariaMode', false) ? 'block' : 'none';
  1493. inputAriaRpcUrl.disabled = GM_getValue('ariaMode', false) ? false : true;
  1494. question5.appendChild(inputAriaRpcUrl);
  1495. let inputAriaExplain = document.createElement('p');
  1496. inputAriaExplain.textContent = text[24];
  1497. inputAriaExplain.style.marginTop = '0.5rem';
  1498. inputAriaExplain.style.marginBottom = '0';
  1499. inputAriaExplain.style.color = 'gray';
  1500. question5.appendChild(inputAriaExplain);
  1501. modal.appendChild(question5);
  1502. let question6 = document.createElement('p');
  1503. question6.style.paddingLeft = '2rem';
  1504. question6.style.paddingRight = '2rem';
  1505. question6.style.marginTop = '1rem';
  1506. question6.style.marginBottom = '0';
  1507. let labelDlVidCov = document.createElement('label');
  1508. labelDlVidCov.setAttribute('for', 'dlVidCov');
  1509. labelDlVidCov.textContent = text[28];
  1510. labelDlVidCov.style.display = 'inline-block';
  1511. labelDlVidCov.style.paddingRight = '0.2rem';
  1512. labelDlVidCov.style.color = "black";
  1513. question6.appendChild(labelDlVidCov);
  1514. let inputDlVidCov = document.createElement('input');
  1515. inputDlVidCov.type = 'checkbox';
  1516. inputDlVidCov.id = 'dlVidCov';
  1517. inputDlVidCov.checked = GM_getValue('dlVidCov', true);
  1518. question6.appendChild(inputDlVidCov);
  1519. modal.appendChild(question6);
  1520. let question7 = document.createElement('p');
  1521. question7.style.paddingLeft = '2rem';
  1522. question7.style.paddingRight = '2rem';
  1523. question7.style.marginTop = '1rem';
  1524. question7.style.marginBottom = '0';
  1525. let labelRmWtrMrk = document.createElement('label');
  1526. labelRmWtrMrk.setAttribute('for', 'rmWtrMrk');
  1527. labelRmWtrMrk.textContent = text[29];
  1528. labelRmWtrMrk.style.display = 'inline-block';
  1529. labelRmWtrMrk.style.paddingRight = '0.2rem';
  1530. labelRmWtrMrk.style.color = "black";
  1531. question7.appendChild(labelRmWtrMrk);
  1532. let inputRmWtrMrk = document.createElement('input');
  1533. inputRmWtrMrk.type = 'checkbox';
  1534. inputRmWtrMrk.id = 'rmWtrMrk';
  1535. inputRmWtrMrk.checked = GM_getValue('rmWtrMrk', false);
  1536. question7.appendChild(inputRmWtrMrk);
  1537. let rmWtrMrkExplain = document.createElement('p');
  1538. rmWtrMrkExplain.innerHTML = text[30];
  1539. rmWtrMrkExplain.style.marginTop = '0.5rem';
  1540. rmWtrMrkExplain.style.marginBottom = '0';
  1541. rmWtrMrkExplain.style.whiteSpace = 'pre';
  1542. rmWtrMrkExplain.style.color = 'gray';
  1543. question7.appendChild(rmWtrMrkExplain);
  1544. modal.appendChild(question7);
  1545. let question8 = document.createElement('p');
  1546. question8.style.paddingLeft = '2rem';
  1547. question8.style.paddingRight = '2rem';
  1548. question8.style.marginTop = '1rem';
  1549. question8.style.marginBottom = '0';
  1550. let labelHidSetBtn = document.createElement('label');
  1551. labelHidSetBtn.setAttribute('for', 'hidSetBtn');
  1552. labelHidSetBtn.textContent = text[31];
  1553. labelHidSetBtn.style.display = 'inline-block';
  1554. labelHidSetBtn.style.paddingRight = '0.2rem';
  1555. labelHidSetBtn.style.color = "black";
  1556. question8.appendChild(labelHidSetBtn);
  1557. let inputHidSetBtn = document.createElement('input');
  1558. inputHidSetBtn.type = 'checkbox';
  1559. inputHidSetBtn.id = 'hidSetBtn';
  1560. inputHidSetBtn.checked = GM_getValue('hidSetBtn', false);
  1561. question8.appendChild(inputHidSetBtn);
  1562. let hidSetBtnExplain = document.createElement('p');
  1563. hidSetBtnExplain.innerHTML = text[32];
  1564. hidSetBtnExplain.style.marginTop = '0.5rem';
  1565. hidSetBtnExplain.style.marginBottom = '0';
  1566. hidSetBtnExplain.style.whiteSpace = 'pre';
  1567. hidSetBtnExplain.style.color = 'gray';
  1568. question8.appendChild(hidSetBtnExplain);
  1569. modal.appendChild(question8);
  1570. inputRetweetMode.addEventListener('change', function(event) {
  1571. if (event.currentTarget.checked) {
  1572. // labelRetweetFileName.style.display = 'block';
  1573. // inputRetweetFileName.style.display = 'block';
  1574. // retweetFileNameExplain.style.display = 'block';
  1575. inputRetweetFileName.disabled = false;
  1576. labelRetweetFileName.style.color = "black";
  1577. inputRetweetFileName.style.borderColor = 'gray';
  1578. } else {
  1579. // labelRetweetFileName.style.display = 'none';
  1580. // inputRetweetFileName.style.display = 'none';
  1581. // retweetFileNameExplain.style.display = 'none';
  1582. inputRetweetFileName.disabled = true;
  1583. labelRetweetFileName.style.color = 'gray';
  1584. inputRetweetFileName.style.borderColor = 'lightgray';
  1585. }
  1586. if (event.currentTarget.checked && inputZipMode.checked && !inputAriaMode.checked) {
  1587. inputRetweetPackName.disabled = false;
  1588. labelRetweetPackName.style.color = "black";
  1589. inputRetweetPackName.style.borderColor = 'gray';
  1590. } else {
  1591. inputRetweetPackName.disabled = true;
  1592. labelRetweetPackName.style.color = 'gray';
  1593. inputRetweetPackName.style.borderColor = 'lightgray';
  1594. }
  1595. });
  1596. inputZipMode.addEventListener('change', function(event) {
  1597. if (event.currentTarget.checked) {
  1598. // labelPackName.style.display = 'block';
  1599. // inputPackName.style.display = 'block';
  1600. // filePackExplain.style.display = 'block';
  1601. inputPackName.disabled = false;
  1602. labelPackName.style.color = "black";
  1603. inputPackName.style.borderColor = 'gray';
  1604. inputAriaMode.disabled = true;
  1605. labelAriaMode.style.color = "gray";
  1606. // inputAriaMode.checked = false;
  1607. } else {
  1608. // labelPackName.style.display = 'none';
  1609. // inputPackName.style.display = 'none';
  1610. // filePackExplain.style.display = 'none';
  1611. inputPackName.disabled = true;
  1612. labelPackName.style.color = 'gray';
  1613. inputPackName.style.borderColor = 'lightgray';
  1614. inputAriaMode.disabled = false;
  1615. labelAriaMode.style.color = "black";
  1616. }
  1617. if (event.currentTarget.checked && inputRetweetMode.checked) {
  1618. inputRetweetPackName.disabled = false;
  1619. labelRetweetPackName.style.color = "black";
  1620. inputRetweetPackName.style.borderColor = 'gray';
  1621. } else {
  1622. inputRetweetPackName.disabled = true;
  1623. labelRetweetPackName.style.color = 'gray';
  1624. inputRetweetPackName.style.borderColor = 'lightgray';
  1625. }
  1626. });
  1627. inputAriaMode.addEventListener('change', function(event) {
  1628. if (event.currentTarget.checked) {
  1629. // labelAriaRpcUrl.style.display = 'block';
  1630. // inputAriaRpcUrl.style.display = 'block';
  1631. inputAriaRpcUrl.disabled = false;
  1632. labelAriaRpcUrl.style.color = "black";
  1633. inputAriaRpcUrl.style.borderColor = 'gray';
  1634. inputZipMode.disabled = true;
  1635. labelZipMode.style.color = 'gray';
  1636. // inputZipMode.checked = false;
  1637. } else {
  1638. // labelAriaRpcUrl.style.display = 'none';
  1639. // inputAriaRpcUrl.style.display = 'none';
  1640. inputAriaRpcUrl.disabled = true;
  1641. labelAriaRpcUrl.style.color = 'gray';
  1642. inputAriaRpcUrl.style.borderColor = 'lightgray';
  1643. inputZipMode.disabled = false;
  1644. labelZipMode.style.color = "black";
  1645. }
  1646. if (!event.currentTarget.checked && inputZipMode.checked) {
  1647. inputPackName.disabled = false;
  1648. labelPackName.style.color = "black";
  1649. inputPackName.style.borderColor = 'gray';
  1650. } else {
  1651. inputPackName.disabled = true;
  1652. labelPackName.style.color = 'gray';
  1653. inputPackName.style.borderColor = 'lightgray';
  1654. }
  1655. if (!event.currentTarget.checked && inputZipMode.checked && inputRetweetMode.checked) {
  1656. inputRetweetPackName.disabled = false;
  1657. labelRetweetPackName.style.color = "black";
  1658. inputRetweetPackName.style.borderColor = 'gray';
  1659. } else {
  1660. inputRetweetPackName.disabled = true;
  1661. labelRetweetPackName.style.color = 'gray';
  1662. inputRetweetPackName.style.borderColor = 'lightgray';
  1663. }
  1664. });
  1665. let okButton = document.createElement('button');
  1666. okButton.textContent = text[5];
  1667. okButton.style.paddingTop = '0.5rem';
  1668. okButton.style.paddingBottom = '0.5rem';
  1669. okButton.style.margin = '2rem';
  1670. okButton.style.backgroundColor = 'darkblue';
  1671. okButton.style.color = 'white';
  1672. okButton.style.fontSize = '1.5rem';
  1673. okButton.style.fontWeight = 'bold';
  1674. okButton.style.width = '21rem';
  1675. okButton.style.borderStyle = 'solid';
  1676. okButton.style.borderRadius = '0.5rem';
  1677. okButton.style.borderColor = 'black';
  1678. okButton.style.borderWidth = '0.2rem';
  1679. okButton.addEventListener('mouseover', function(event) {
  1680. okButton.style.backgroundColor = 'blue';
  1681. });
  1682. okButton.addEventListener('mouseout', function(event) {
  1683. okButton.style.backgroundColor = 'darkblue';
  1684. });
  1685. okButton.addEventListener('mousedown', function(event) {
  1686. okButton.style.backgroundColor = 'darkblue';
  1687. });
  1688. okButton.addEventListener('mouseover', function(event) {
  1689. okButton.style.backgroundColor = 'blue';
  1690. });
  1691. function resizeWindow(event) {
  1692. // console.log('resize');
  1693. bg.style.width = document.documentElement.clientWidth.toString() + 'px';
  1694. bg.style.height = document.documentElement.clientHeight.toString() + 'px';
  1695. modal.style.top = (( document.documentElement.clientHeight - modal.offsetHeight ) / 2).toString() + 'px';
  1696. modal.style.left = (( document.documentElement.clientWidth - modal.offsetWidth ) / 2).toString() + 'px';
  1697. }
  1698. okButton.addEventListener('click', function(event) {
  1699. /*if(document.getElementById('chooseButton').checked == true) {
  1700. GM_setValue('addDlBtnMode', 1);
  1701. addDlBtnMode = 1;
  1702. addStartButton();
  1703. } else {
  1704. GM_setValue('addDlBtnMode', 2);
  1705. addDlBtnMode = 2;
  1706. addEventListener();
  1707. }*/
  1708. if (document.getElementById('zipMode').checked && !document.getElementById('dlFileName').value.includes('{original}') && !document.getElementById('dlFileName').value.includes('{index}')) {
  1709. alert(text[27].replaceAll(/\n/g, ''));
  1710. document.getElementById('dlFileName').focus();
  1711. return;
  1712. }
  1713. GM_setValue('dlFileName', document.getElementById('dlFileName').value);
  1714. GM_setValue('retweetMode', document.getElementById('retweetMode').checked);
  1715. GM_setValue('retweetFileName', document.getElementById('retweetFileName').value);
  1716. GM_setValue('zipMode', document.getElementById('zipMode').checked);
  1717. GM_setValue('packFileName', document.getElementById('packFileName').value);
  1718. GM_setValue('retweetPackFileName', document.getElementById('retweetPackFileName').value);
  1719. GM_setValue('ariaMode', document.getElementById('ariaMode').checked);
  1720. GM_setValue('ariaRpcUrl', document.getElementById('ariaRpcUrl').value);
  1721. GM_setValue('dlVidCov', document.getElementById('dlVidCov').checked);
  1722. GM_setValue('rmWtrMrk', document.getElementById('rmWtrMrk').checked);
  1723. GM_setValue('hidSetBtn', document.getElementById('hidSetBtn').checked);
  1724. GM_setValue('isSet', settingVersion);
  1725. let setBtn = document.getElementById('wbDlSetBtn');
  1726. if (setBtn) {
  1727. setBtn.style.display = document.getElementById('hidSetBtn').checked ? 'none' : 'block';
  1728. }
  1729. document.body.removeChild(modal);
  1730. document.body.removeChild(bg);
  1731. window.removeEventListener('resize', resizeWindow);
  1732. });
  1733. modal.appendChild(okButton);
  1734. document.body.appendChild(modal);
  1735. /*bg.addEventListener('click', function(event) {
  1736. document.body.removeChild(modal);
  1737. document.body.removeChild(bg);
  1738. window.removeEventListener('resize', resizeWindow);
  1739. });*/
  1740. resizeWindow();
  1741. window.addEventListener('resize', resizeWindow);
  1742. }
  1743.  
  1744. let svg = document.getElementById('__SVG_SPRITE_NODE__');
  1745. let symbol = document.createElementNS('http://www.w3.org/2000/svg', 'symbol');
  1746. symbol.id = 'woo_svg_download';
  1747. symbol.setAttribute('viewBox', '0 0 100 100');
  1748. symbol.innerHTML = '<path d="m25,0l50,0l0,50l25,0l-50,50l-50,-50l25,0l0,-50" fill="currentColor"></path><path d="m30,5l40,0l0,50l20,0l-40,40l-40,-40l20,0l0,-50" fill="white"></path>';
  1749. svg.appendChild(symbol);
  1750. /*
  1751. if(addDlBtnMode == 0) {
  1752. showModal();
  1753. } else if (addDlBtnMode == 1) {
  1754. addStartButton();
  1755. } else if (addDlBtnMode == 2) {
  1756. addEventListener();
  1757. }
  1758. */
  1759. if(GM_getValue('isSet', null) !== settingVersion) {
  1760. showModal();
  1761. }
  1762. new MutationObserver((mutationList, observer) => {
  1763. // console.log(mutationList);
  1764. if (location.host == 'weibo.com' || location.host == 'www.weibo.com') {
  1765. const cards = document.body.querySelectorAll('article.woo-panel-main');
  1766. // console.log(cards);
  1767. for (const card of cards) {
  1768. handleCard(card);
  1769. }
  1770. for (const mutation of mutationList) {
  1771. // console.log(mutation.target);
  1772. if (mutation.type === 'childList' && mutation.target.tagName === 'DIV' && (mutation.target.className.includes('wbpro-feed-content') || mutation.target.className.includes('Feed_retweet_JqZJb'))) {
  1773. for (const node of mutation.addedNodes) {
  1774. // console.log(node);
  1775. const imgs = node.querySelectorAll('img.woo-picture-img,img.picture_focusImg_1z5In,img.picture-viewer_pic_37YQ3,video.picture-viewer_pic_37YQ3');
  1776. // console.log(imgs);
  1777. for (const [ idx, img ] of Object.entries(imgs)) {
  1778. if (img.parentElement.getElementsByClassName('download-single-button').length === 0) {
  1779. if (img.className.includes('picture-viewer_pic_37YQ3')) {
  1780. const previews = node.querySelectorAll('div.picture-viewer_preview_2wOSq');
  1781. for (const [ index, preview ] of Object.entries(previews)) {
  1782. if (preview.className.includes('picture-viewer_cur_anUEY')) {
  1783. addSingleDlBtn(img, parseInt(index));
  1784. }
  1785. }
  1786. } else {
  1787. addSingleDlBtn(img, parseInt(idx));
  1788. }
  1789. }
  1790. }
  1791. }
  1792. }
  1793. }
  1794. } else if (location.host == 's.weibo.com') {
  1795. // let cards = document.querySelectorAll('#pl_feedlist_index .card-wrap');
  1796. const footers = document.querySelectorAll('#pl_feedlist_index .card-act');
  1797. for (const footer of footers) {
  1798. if(footer.getElementsByClassName('download-button').length > 0) {
  1799. // console.log('already added download button');
  1800. } else {
  1801. // console.log(footer.parentElement);
  1802. if(footer.parentElement.className == 'card' && footer.parentElement.parentElement.className == 'card-wrap') {
  1803. const card = footer.parentElement;
  1804. let added = false;
  1805. const media_prev = card.querySelector('div[node-type="feed_list_media_prev"]');
  1806. // console.log(media_prev);
  1807. if (media_prev) {
  1808. const imgs = media_prev.getElementsByTagName('img');
  1809. // console.log(imgs);
  1810. if(imgs.length > 0) {
  1811. sAddDlBtn(footer);
  1812. added = true;
  1813. }
  1814. const videos = card.getElementsByTagName('video');
  1815. if(videos.length > 0 && added == false) {
  1816. sAddDlBtn(footer);
  1817. }
  1818. }
  1819. }
  1820. }
  1821. }
  1822. }
  1823. }).observe(document.body, { attributes: false, childList: true, subtree: true });
  1824.  
  1825. let settingButton = document.createElement('button');
  1826. settingButton.id = 'wbDlSetBtn';
  1827. settingButton.textContent = text[6];
  1828. settingButton.style.position = 'fixed';
  1829. settingButton.style.top = '4rem';
  1830. settingButton.style.left = '0rem';
  1831. settingButton.style.fontSize = '0.7rem';
  1832. settingButton.style.backgroundColor = 'gray';
  1833. settingButton.style.color = 'white';
  1834. settingButton.style.borderWidth = '0.2rem';
  1835. settingButton.style.borderStyle = 'solid';
  1836. settingButton.style.borderRadius = '0.5rem';
  1837. settingButton.style.borderColor = 'lightgrey';
  1838. settingButton.style.zIndex = 400;
  1839. settingButton.style.paddingLeft = '1rem';
  1840. settingButton.style.paddingRight = '1rem';
  1841. settingButton.style.paddingTop = '0.2rem';
  1842. settingButton.style.paddingBottom = '0.2rem';
  1843. settingButton.addEventListener('mouseover', function(event) {
  1844. settingButton.style.backgroundColor = 'darkgray';
  1845. settingButton.style.color = 'black';
  1846. });
  1847. settingButton.addEventListener('mouseout', function(event) {
  1848. settingButton.style.backgroundColor = 'gray';
  1849. settingButton.style.color = 'white';
  1850. });
  1851. settingButton.addEventListener('mousedown', function(event) {
  1852. settingButton.style.backgroundColor = 'gray';
  1853. settingButton.style.color = 'white';
  1854. });
  1855. settingButton.addEventListener('mouseup', function(event) {
  1856. settingButton.style.backgroundColor = 'darkgray';
  1857. settingButton.style.color = 'black';
  1858. });
  1859. settingButton.addEventListener('click', showModal);
  1860. document.body.appendChild(settingButton);
  1861. GM_registerMenuCommand(text[25], showModal, "0");
  1862. // console.log(GM_info.downloadMode);
  1863. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址