批量下载微博原图、视频、livephoto

一键打包下载微博中一贴的原图、视频、livephoto,收藏时本地自动备份

Installer ce script?
Script suggéré par l'auteur

Vous pourriez également aimer 批量下载贴吧原图.

Installer ce script
  1. // ==UserScript==
  2. // @name 批量下载微博原图、视频、livephoto
  3. // @name:zh 批量下载微博原图、视频、livephoto
  4. // @name:en Batch Download Src Image From Weibo Card
  5. // @namespace https://github.com/Jeffrey-deng/userscript
  6. // @version 2.3.5
  7. // @description 一键打包下载微博中一贴的原图、视频、livephoto,收藏时本地自动备份
  8. // @description:zh 一键打包下载微博中一贴的原图、视频、livephoto,收藏时本地自动备份
  9. // @description:en Batch download weibo's source image
  10. // @author Jeffrey.Deng
  11. // @supportURL https://imcoder.site/a/detail/HuXBzyC
  12. // @homepageURL https://imcoder.site
  13. // @weibo http://weibo.com/3983281402
  14. // @match https://weibo.com/*
  15. // @match https://www.weibo.com/*
  16. // @match https://d.weibo.com/*
  17. // @match http://*.sinaimg.cn/*
  18. // @match https://*.sinaimg.cn/*
  19. // @match http://*.sinaimg.com/*
  20. // @match https://*.sinaimg.com/*
  21. // @connect sinaimg.cn
  22. // @connect weibocdn.com
  23. // @connect weibo.com
  24. // @connect miaopai.com
  25. // @connect tbcache.com
  26. // @connect youku.com
  27. // @connect *
  28. // @require https://cdn.bootcss.com/jquery/1.11.1/jquery.min.js
  29. // @require https://cdn.bootcss.com/toastr.js/2.1.3/toastr.min.js
  30. // @require https://cdn.bootcss.com/jszip/3.1.5/jszip.min.js
  31. // @resource toastr_css https://cdn.bootcss.com/toastr.js/2.1.3/toastr.min.css
  32. // @grant GM.xmlHttpRequest
  33. // @grant GM_xmlHttpRequest
  34. // @grant GM_download
  35. // @grant GM_notification
  36. // @grant GM_setClipboard
  37. // @grant GM_getValue
  38. // @grant GM_setValue
  39. // @grant GM_addStyle
  40. // @grant GM_getResourceText
  41. // @grant GM_registerMenuCommand
  42. // @grant GM_unregisterMenuCommand
  43. // ==/UserScript==
  44.  
  45. // @更新日志
  46. // v.2.3 2023.4.8 1.支持图片与视频混合微博、支持多视频微博
  47. // 2.新版微博支持在【收藏页面】的【被删除的微博】上用备份数据还原显示
  48. // v.2.2 2023.4.2 1.简单适配新版微博批量下载、评论收藏、收藏面板;新版微博的html元素是往下滚动是复用的,所以脚本总是无法达到老版微博的体验,请理解
  49. // v.2.1 2022.11.10 1.尝试解决一些下载图片报错问题,但不一定能解决,我没有网络能测试,我这边下载是一切正常的,我的浏览器是Chrome最新版/mac
  50. // v.2.0.6 2022.7.1 1.增加微博收藏查看面板,只适配了旧版微博样式
  51. // v.2.0 2021.5.30 1.初步适配新版微博,修复18图bug,修复批量下载bug
  52. // v.1.9.4 2020.8.15 1.去掉下载超时时间,修复当照片大小超大时,陷于循环的情况
  53. // v.1.9.3 2020.7.11 1.修复jQuery下载失败问题
  54. // V 1.9.2 2020.06.29 1.照片按原页面显示顺序排序
  55. // V 1.9.0 2020.06.10 1.一键下载用户一页的微博
  56. // 2.批量备份一页收藏
  57. // V 1.8.7 2020.05.26 1.增加收藏时自动备份(不需要点击确认框)开关
  58. // 2.修复在超过9张图(over9pic)中含有gif时,gif的文件名后缀错误的问题
  59. // 3.修复当转发的微博被删除时可能出现备份读取不到的问题
  60. // V 1.8.6 2020.05.23 1.修复www.weibo.com域名下不起作用的问题
  61. // V 1.8.5 2020.05.15 1.修复视频下载失败,原因是不能弹出白名单确认框,解决办法是允许所有域名,弹出确认框后请点击总是允许所有域名,请放心点允许,代码绝对无后门
  62. // V 1.8.3 2020.05.12 1.优化收藏备份
  63. // V 1.8.2 2020.05.07 1.修复只有一张图片时,无法下载livephoto的问题
  64. // V 1.8 2020.04.30 1.收藏时自动备份收藏到本地缓存(只备份图片链接),这样博主删除微博仍能找到内容
  65. // V 1.6 2020.04.29 1.打印链接直接用面板显示,感谢@indefined提供的代码
  66. // V 1.5 2020.03.26 1.支持只打印链接,仅在控制台打印链接(按F12打开控制台console),【建议先按F12打开控制台console,在点按钮】
  67. // V 1.4 2020.03.26 1.支持只下载链接,按钮【打包下载】:下载文件和链接,【下载链接】:仅下载链接
  68. // V 1.3 2020.01.26 1.修复bug
  69. // V 1.0 2019.12.26 1.支持打包下载用户一次动态的所有原图
  70. // 2.支持下载18图
  71. // 3.支持下载livephoto
  72. // 4.支持下载视频
  73. // 5.支持下载微博故事
  74. // 6.右键图片新标签直接打开原图
  75.  
  76. (function(factory) {
  77. factory(document, jQuery);
  78. // console.time('『微博原图下载』/ready_init_use_time');
  79. // $().ready(function(){
  80. // console.timeEnd('『微博原图下载』/ready_init_use_time');
  81. // factory(document, jQuery);
  82. // });
  83. })(function (document, $) {
  84.  
  85. var common_utils = (function (document, $) {
  86. function parseURL(url) {
  87. var a = document.createElement('a');
  88. a.href = url;
  89. return {
  90. source: url,
  91. protocol: a.protocol.replace(':', ''),
  92. host: a.hostname,
  93. port: a.port,
  94. query: a.search,
  95. params: (function () {
  96. var ret = {},
  97. seg = a.search.replace(/^\?/, '').split('&'),
  98. len = seg.length, i = 0, s;
  99. for (; i < len; i++) {
  100. if (!seg[i]) {
  101. continue;
  102. }
  103. s = seg[i].split('=');
  104. ret[s[0]] = s[1];
  105. }
  106. return ret;
  107. })(),
  108. file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1],
  109. hash: a.hash.replace('#', ''),
  110. path: a.pathname.replace(/^([^\/])/, '/$1'),
  111. relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [, ''])[1],
  112. segments: a.pathname.replace(/^\//, '').split('/')
  113. };
  114. }
  115.  
  116. function formatDate (date, fmt) {
  117. if (typeof date == 'number') {
  118. date = new Date(date);
  119. }
  120. var o = {
  121. "M+": date.getMonth() + 1, //月份
  122. "d+": date.getDate(), //日
  123. "h+": date.getHours(), //小时
  124. "m+": date.getMinutes(), //分
  125. "s+": date.getSeconds(), //秒
  126. "q+": Math.floor((date.getMonth() + 3) / 3), //季度
  127. "S": date.getMilliseconds() //毫秒
  128. };
  129. if (/(y+)/.test(fmt))
  130. fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
  131. for (var k in o)
  132. if (new RegExp('(' + k + ')').test(fmt))
  133. fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
  134. return fmt;
  135. };
  136.  
  137. function ajaxDownload(url, callback, args, tryTimes) {
  138. var headers = {
  139. 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36',
  140. //'cache-control': 'max-age=0',
  141. 'referer': 'https://weibo.com/'
  142. };
  143. if (typeof url === 'object') {
  144. headers = url.headers;
  145. url = url.url;
  146. }
  147. tryTimes = tryTimes || 0;
  148. var GM_download = GM.xmlHttpRequest || GM_xmlHttpRequest,
  149. clearUrl = url.replace(/[&\?]?download_timestamp=\d+/, ''),
  150. retryUrl = clearUrl + (clearUrl.indexOf('?') === -1 ? '?' : '&') + 'download_timestamp=' + new Date().getTime(),
  151. nocache = tryTimes === 0 ? false : true;
  152. GM_download({
  153. method: 'GET',
  154. responseType: 'blob',
  155. url: url,
  156. headers: headers,
  157. //timeout: 2000,
  158. nocache: nocache,
  159. onreadystatechange: function (responseDetails) {
  160. if (responseDetails.readyState === 4) {
  161. if (responseDetails.response != null && (responseDetails.status === 200)) {
  162. var blob = responseDetails.response, size = blob && blob.size;
  163. if (size && (size / 1024 > 0)) {
  164. callback(blob, args);
  165. } else if (tryTimes++ == 3) {
  166. callback(blob, args);
  167. } else {
  168. setTimeout(function() {
  169. ajaxDownload(retryUrl, callback, args, tryTimes);
  170. }, 500);
  171. }
  172. } else {
  173. if (tryTimes++ == 3) {
  174. callback(null, args);
  175. } else {
  176. setTimeout(function() {
  177. ajaxDownload(retryUrl, callback, args, tryTimes);
  178. }, 500);
  179. }
  180. }
  181. }
  182. },
  183. onerror: function (responseDetails) {
  184. if (tryTimes++ == 3) {
  185. callback(null, args);
  186. } else {
  187. setTimeout(function() {
  188. ajaxDownload(retryUrl, callback, args, tryTimes);
  189. }, 500);
  190. }
  191. console.log(responseDetails.status);
  192. }
  193. });
  194. // try {
  195. // var xhr = new XMLHttpRequest();
  196. // xhr.open('GET', url, true);
  197. // xhr.responseType = "blob";
  198. // xhr.onreadystatechange = function(evt) {
  199. // if (xhr.readyState === 4) {
  200. // if (xhr.status === 200 || xhr.status === 0) {
  201. // callback(xhr.response, args);
  202. // } else {
  203. // callback(null, args);
  204. // }
  205. // }
  206. // };
  207. // xhr.send();
  208. // } catch (e) {
  209. // callback(null, args);
  210. // }
  211. }
  212.  
  213. function fileNameFromHeader(disposition, url) {
  214. var result = null;
  215. if (disposition && /filename=.*/ig.test(disposition)) {
  216. result = disposition.match(/filename=.*/ig);
  217. return decodeURI(result[0].split("=")[1]);
  218. }
  219. return url.substring(url.lastIndexOf('/') + 1);
  220. }
  221.  
  222. function downloadBlobFile(content, fileName) {
  223. if ('msSaveOrOpenBlob' in navigator) {
  224. navigator.msSaveOrOpenBlob(content, fileName);
  225. } else {
  226. var aLink = document.createElement('a');
  227. aLink.className = 'download-temp-node';
  228. aLink.download = fileName;
  229. aLink.style = "display:none;";
  230. var blob = new Blob([content], {type: content.type});
  231. aLink.href = window.URL.createObjectURL(blob);
  232. document.body.appendChild(aLink);
  233. if (document.all) {
  234. aLink.click(); //IE
  235. } else {
  236. var evt = document.createEvent("MouseEvents");
  237. evt.initEvent("click", true, true);
  238. aLink.dispatchEvent(evt); // 其它浏览器
  239. }
  240. window.URL.revokeObjectURL(aLink.href);
  241. document.body.removeChild(aLink);
  242. }
  243. }
  244.  
  245. function downloadUrlFile(url, fileName) {
  246. var aLink = document.createElement('a');
  247. if (fileName) {
  248. aLink.download = fileName;
  249. } else {
  250. aLink.download = url.substring(url.lastIndexOf('/') + 1);
  251. }
  252. aLink.className = 'download-temp-node';
  253. aLink.target = "_blank";
  254. aLink.style = "display:none;";
  255. aLink.href = url;
  256. document.body.appendChild(aLink);
  257. if (document.all) {
  258. aLink.click(); //IE
  259. } else {
  260. var evt = document.createEvent("MouseEvents");
  261. evt.initEvent("click", true, true);
  262. aLink.dispatchEvent(evt); // 其它浏览器
  263. }
  264. document.body.removeChild(aLink);
  265. }
  266.  
  267. function paddingZero(num, length) {
  268. return (Array(length).join("0") + num).substr(-length);
  269. }
  270.  
  271. /* Class: TaskQueue
  272. * Constructor: handler
  273. * takes a function which will be the task handler to be called,
  274. * handler should return Deferred object(not Promise), if not it will run immediately;
  275. * methods: append
  276. * appends a task to the Queue. Queue will only call a task when the previous task has finished
  277. */
  278. var TaskQueue = function (handler) {
  279. var tasks = [];
  280. // empty resolved deferred object
  281. var deferred = $.when();
  282.  
  283. // handle the next object
  284. function handleNextTask() {
  285. // if the current deferred task has resolved and there are more tasks
  286. if (deferred.state() == "resolved" && tasks.length > 0) {
  287. // grab a task
  288. var task = tasks.shift();
  289. // set the deferred to be deferred returned from the handler
  290. deferred = handler(task);
  291. // if its not a deferred object then set it to be an empty deferred object
  292. if (!(deferred && deferred.promise)) {
  293. deferred = $.when();
  294. }
  295. // if we have tasks left then handle the next one when the current one
  296. // is done.
  297. if (tasks.length >= 0) {
  298. deferred.fail(function () {
  299. tasks = [];
  300. });
  301. deferred.done(handleNextTask);
  302. }
  303. }
  304. }
  305.  
  306. // appends a task.
  307. this.append = function (task) {
  308. // add to the array
  309. tasks.push(task);
  310. // handle the next task
  311. handleNextTask();
  312. };
  313. };
  314. /**
  315. * 判断元素是否在出现在可见区域内
  316. *
  317. * @param element
  318. * @param {Number=} ON_SCREEN_HEIGHT 用来设置元素出现在屏幕中 N px的条件,也就是这里的ON_SCREEN_HEIGHT。只要保证元素的上下左右四个边界都在屏幕内显示超过npx,我们就可以认为元素出现在页面中了。
  319. * @param {Number=} ON_SCREEN_WIDTH
  320. * @returns {boolean}
  321. */
  322. var isOnScreen = function (element, ON_SCREEN_HEIGHT, ON_SCREEN_WIDTH) {
  323. var ON_SCREEN_HEIGHT = ON_SCREEN_HEIGHT || 20;
  324. var ON_SCREEN_WIDTH = ON_SCREEN_WIDTH || 20;
  325. var rect = element.getBoundingClientRect();
  326. var windowHeight = window.innerHeight || document.documentElement.clientHeight;
  327. var windowWidth = window.innerWidth || document.documentElement.clientWidth;
  328. var elementHeight = element.offsetHeight;
  329. var elementWidth = element.offsetWidth;
  330. var onScreenHeight = ON_SCREEN_HEIGHT > elementHeight ? elementHeight : ON_SCREEN_HEIGHT;
  331. var onScreenWidth = ON_SCREEN_WIDTH > elementWidth ? elementWidth : ON_SCREEN_WIDTH;
  332. // 元素在屏幕上方
  333. var elementBottomToWindowTop = rect.top + elementHeight;
  334. var bottomBoundingOnScreen = elementBottomToWindowTop >= onScreenHeight;
  335. // 元素在屏幕下方
  336. var elementTopToWindowBottom = windowHeight - (rect.bottom - elementHeight);
  337. var topBoundingOnScreen = elementTopToWindowBottom >= onScreenHeight;
  338. // 元素在屏幕左侧
  339. var elementRightToWindowLeft = rect.left + elementWidth;
  340. var rightBoundingOnScreen = elementRightToWindowLeft >= onScreenWidth;
  341. // 元素在屏幕右侧
  342. var elementLeftToWindowRight = windowWidth - (rect.right - elementWidth);
  343. var leftBoundingOnScreen = elementLeftToWindowRight >= onScreenWidth;
  344. return bottomBoundingOnScreen && topBoundingOnScreen && rightBoundingOnScreen && leftBoundingOnScreen;
  345. };
  346.  
  347. var context = {
  348. "formatDate": formatDate,
  349. "ajaxDownload": ajaxDownload,
  350. "fileNameFromHeader": fileNameFromHeader,
  351. "downloadBlobFile": downloadBlobFile,
  352. "downloadUrlFile": downloadUrlFile,
  353. "parseURL": parseURL,
  354. "paddingZero": paddingZero,
  355. "TaskQueue": TaskQueue,
  356. "isOnScreen": isOnScreen
  357. };
  358. return context;
  359. })(document, jQuery);
  360.  
  361. var options = {
  362. "type": 2,
  363. "isNeedConfirmDownload": true,
  364. "isNeedHasFiles": true,
  365. "useQueueDownloadThreshold": 0,
  366. "suffix": null,
  367. "callback": {
  368. "parseLocationInfo_callback": function (location_info, options) {
  369. return common_utils.parseURL(document.location.href);
  370. },
  371. "parseFiles_callback": function (location_info, options) {
  372. // file.url file.folder_sort_index
  373. // not folder_sort_index -> use fileName
  374. var files = [];
  375. return files;
  376. },
  377. "makeNames_callback": function (arr, location_info, options) {
  378. var names = {};
  379. var time = new Date().getTime();
  380. names.zipName = "pack_" + time;
  381. names.folderName = names.zipName;
  382. names.infoName = null;
  383. names.infoValue = null;
  384. names.prefix = time;
  385. names.suffix = options.suffix;
  386. return names;
  387. },
  388. "beforeFilesDownload_callback": function (files, names, location_info, options, zip, main_folder) {
  389. },
  390. "beforeFileDownload_callback": function (file, location_info, options, zipFileLength, zip, main_folder, folder) {
  391. },
  392. "eachFileOnload_callback": function (blob, file, location_info, options, zipFileLength, zip, main_folder, folder) {
  393. },
  394. "allFilesOnload_callback": function (files, names, location_info, options, zip, main_folder) {
  395. },
  396. "beforeZipFileDownload_callback": function (zip_blob, files, names, location_info, options, zip, main_folder) {
  397. common_utils.downloadBlobFile(zip_blob, names.zipName + ".zip");
  398. }
  399. }
  400. };
  401.  
  402. var ajaxDownloadAndZipFiles = function (files, names, location_info, options) {
  403. // GM_notification("开始下载~", names.zipName);
  404. var notify_start = toastr.success("正在打包~", names.zipName, {
  405. "progressBar": false,
  406. "hideDuration": 0,
  407. "showDuration": 0,
  408. "timeOut": 0,
  409. "closeButton": false
  410. });
  411. if (!options.isNeedHasFiles || (files && files.length > 0)) {
  412. var fixTimezoneOffset = function () {
  413. const currDate = new Date();
  414. const dateWithOffset = new Date(currDate.getTime() - currDate.getTimezoneOffset() * 60000);
  415. // replace the default date with dateWithOffset
  416. JSZip.defaults.date = dateWithOffset;
  417. }
  418. var zip = new JSZip();
  419. fixTimezoneOffset();
  420. var main_folder = zip.folder(names.folderName);
  421. var zipFileLength = 0;
  422. var maxLength = files.length;
  423. var paddingZeroLength = (files.length + "").length;
  424. if (names.infoName) {
  425. fixTimezoneOffset();
  426. main_folder.file(names.infoName, names.infoValue);
  427. }
  428. options.callback.beforeFilesDownload_callback(files, names, location_info, options, zip, main_folder);
  429. var downloadFile = function (file, resolveCallback) {
  430. var triggerCompleted = file === true;
  431. return $.Deferred(function(dfd) {
  432. if (triggerCompleted) {
  433. dfd.resolve();
  434. return;
  435. }
  436. fixTimezoneOffset();
  437. var folder = file.location ? main_folder.folder(file.location) : main_folder;
  438. var isSave = options.callback.beforeFileDownload_callback(file, location_info, options, zipFileLength, zip, main_folder, folder);
  439. if (isSave !== false) {
  440. common_utils.ajaxDownload(file.url, function (blob, file) {
  441. var isSave = options.callback.eachFileOnload_callback(blob, file, location_info, options, zipFileLength, zip, main_folder, folder);
  442. if (isSave !== false) {
  443. fixTimezoneOffset();
  444. if (file.fileName) {
  445. folder.file(file.fileName, blob);
  446. } else {
  447. var suffix = names.suffix || file.url.substring(file.url.lastIndexOf('.') + 1);
  448. file.fileName = names.prefix + "_" + common_utils.paddingZero(file.folder_sort_index, paddingZeroLength) + "." + suffix;
  449. folder.file(file.fileName, blob);
  450. }
  451. }
  452. dfd.resolveWith(file, [blob, folder, isSave]);
  453. }, file);
  454. } else {
  455. dfd.resolveWith(file, [null, folder, false]);
  456. }
  457. }).done(function(blob, folder, isSave){
  458. if (!triggerCompleted) {
  459. zipFileLength++;
  460. notify_start.find(".toast-message").text("正在打包~ 第 " + zipFileLength + " 张" + (isSave ? "" : "跳过"));
  461. }
  462. resolveCallback && resolveCallback(); // resolve延迟对象
  463. if (triggerCompleted || zipFileLength >= maxLength) {
  464. var isDownloadZip = options.callback.allFilesOnload_callback(files, names, location_info, options, zip, main_folder);
  465. if (isDownloadZip !== false) {
  466. zip.generateAsync({type: "blob"}).then(function (content) {
  467. options.callback.beforeZipFileDownload_callback(content, files, names, location_info, options, zip, main_folder);
  468. });
  469. // GM_notification({text: "打包下载完成!", title: names.zipName, highlight : true});
  470. toastr.success("下载完成!", names.zipName, {"progressBar": false, timeOut: 0});
  471. }
  472. notify_start.css("display", "none").remove();
  473. }
  474. });
  475. };
  476. if (maxLength < options.useQueueDownloadThreshold) {
  477. // 并发数在useQueueDownloadThreshold内,直接下载
  478. for (var i = 0; i < maxLength; i++) {
  479. downloadFile(files[i]);
  480. }
  481. } else {
  482. // 并发数在useQueueDownloadThreshold之上,采用队列下载
  483. var queue = new common_utils.TaskQueue(function (file) {
  484. if (file) {
  485. return downloadFile(file);
  486. }
  487. });
  488. for (var j = 0; j < maxLength; j++) {
  489. queue.append(files[j]);
  490. }
  491. }
  492. if (maxLength == 0) {
  493. downloadFile(true);
  494. }
  495. } else {
  496. notify_start.css("display", "none").remove();
  497. toastr.error("未解析到图片!", "错误", {"progressBar": false});
  498. }
  499. };
  500.  
  501. /** 批量下载 **/
  502. function batchDownload(config) {
  503. try {
  504. options = $.extend(true, {}, options, config);
  505. var location_info = options.callback.parseLocationInfo_callback(options);
  506. var files = options.callback.parseFiles_callback(location_info, options);
  507. if (!(files && files.promise)) {
  508. files = $.when(files);
  509. }
  510. files.done(function (files) {
  511. var hasFiles = files && files.length > 0;
  512. if (!options.isNeedHasFiles || hasFiles) {
  513. if (!hasFiles) {
  514. files = [];
  515. }
  516. if (!options.isNeedConfirmDownload || confirm("是否下载 " + files.length + " 张图片")) {
  517. var names = options.callback.makeNames_callback(files, location_info, options);
  518. options.location_info = location_info;
  519. options.files = files;
  520. options.names = names;
  521. if (options.type == 1) {
  522. urlDownload(files, names, location_info, options);
  523. } else {
  524. ajaxDownloadAndZipFiles(files, names, location_info, options);
  525. }
  526. }
  527. } else {
  528. toastr.error("未找到图片~", "");
  529. }
  530. }).fail(function(message) {
  531. toastr.error(message, "错误");
  532. });
  533. } catch (e) {
  534. // GM_notification("批量下载照片 出现错误!", "");
  535. console.warn("批量下载照片 出现错误!, exception: ", e);
  536. toastr.error("批量下载照片 出现错误!", "");
  537. }
  538.  
  539. }
  540.  
  541. /** 下载 **/
  542. function urlDownload(photos, names, location_info, options) {
  543. GM_notification("开始下载~", names.zipName);
  544. var index = 0;
  545. var interval = setInterval(function () {
  546. if (index < photos.length) {
  547. var url = photos[index].url;
  548. var fileName = null;
  549. if (!names.suffix) {
  550. fileName = names.prefix + "_" + (index + 1) + url.substring(url.lastIndexOf('.'));
  551. } else {
  552. fileName = names.prefix + "_" + (index + 1) + "." + names.suffix;
  553. }
  554. common_utils.downloadUrlFile(url, fileName);
  555. } else {
  556. clearInterval(interval);
  557. return;
  558. }
  559. index++;
  560. }, 100);
  561. }
  562.  
  563. // 右键新标签打开图片直接打开原图
  564. function initRightClickOpenSource() {
  565. var url = document.location.toString();
  566. var m;
  567. if ((m = url.match(/^(https?:\/\/(?:(?:ww|wx|ws|tvax|tva)\d+|wxt|wt)\.sinaimg\.(?:cn|com)\/)([\w\.]+)(\/.+)(?:\?.+)?$/i))) {
  568. if (m[2] != "large") {
  569. document.location = m[1] + "large" + m[3];
  570. }
  571. }
  572. }
  573.  
  574. /*** start main ***/
  575.  
  576. //右键新标签打开图片直接打开原图
  577. initRightClickOpenSource();
  578.  
  579. // css
  580. GM_addStyle(GM_getResourceText('toastr_css'));
  581. GM_addStyle('.download-link-pop {'+
  582. ' position: absolute;'+
  583. ' right: 4px;'+
  584. ' top: 40px;'+
  585. ' width: 70%;'+
  586. ' z-index: 100;'+
  587. '}'+
  588. '.download-link-pop > * {'+
  589. ' padding: 15px;'+
  590. '}'+
  591. '.download-link-pop .link-list ul li {'+
  592. ' border-top: 1px solid #eee;'+
  593. ' padding-top: 4px;'+
  594. ' padding-bottom: 4px;'+
  595. '}'+
  596. '.download-link-pop .link-list ul li:first-child {'+
  597. ' border-top: unset;'+
  598. ' padding-top: 0px;'+
  599. '}'+
  600. '.download-link-pop .link-list ul li:last-child {'+
  601. ' padding-bottom: 0px;'+
  602. '}'+
  603. '.download-link-pop .link-list li a{'+
  604. ' word-break: break-all;'+
  605. '}'+
  606. '.download-link-pop .sidebar {'+
  607. ' position: absolute;'+
  608. ' bottom: -1px;'+
  609. ' right: -65px;'+
  610. ' width: 65px;'+
  611. ' padding: 0px 0px 1px 0px;'+
  612. ' background: #fff;'+
  613. ' border: 1px solid #ccc;'+
  614. ' border-left-width: 0px;'+
  615. ' border-bottom-right-radius: 3px;'+
  616. ' border-top-right-radius: 3px;'+
  617. ' text-align: center;'+
  618. ' visibility:hidden;'+
  619. '}'+
  620. '.download-link-pop .sidebar-btn {'+
  621. ' font-weight: bold;'+
  622. ' font-size: 12px;'+
  623. ' padding: 3px 4px;'+
  624. ' line-height: 22px;'+
  625. ' border-top: 1px solid #ccc;'+
  626. ' cursor: pointer;'+
  627. '}'+
  628. '.download-link-pop .sidebar-btn:first-child {'+
  629. ' border-top-width: 0px;'+
  630. '}'+
  631. '.download-link-pop:hover .sidebar {'+
  632. ' visibility:visible;'+
  633. '}'+
  634. '.download-link-pop .preview {'+
  635. ' /*visibility:hidden;*/'+
  636. ' display: none;'+
  637. '}'+
  638. '.download-link-pop .preview img {'+
  639. ' width: 100%;'+
  640. '}'+
  641. '#app .download-link-pop {'+
  642. ' background: #f9f9f9;'+
  643. ' border-radius: 3px;'+
  644. ' border: 1px solid #ccc;'+
  645. ' box-shadow: 0 4px 20px 1px rgb(0 0 0 / 20%);'+
  646. ' font-size: 0.8rem!important;'+
  647. ' width: 80%;'+
  648. '}'+
  649. '#app .download-link-pop > * {'+
  650. ' padding: 0px;'+
  651. '}'+
  652. '#app .download-link-pop .link-list ul {'+
  653. ' padding: 10px 0px 10px 35px;'+
  654. ' margin: 5px;'+
  655. '}'+
  656. '#app .download-link-pop .preview {'+
  657. ' padding: 0px 15px 15px 15px;'+
  658. '}'
  659. );
  660.  
  661. const Constants = {
  662. KEY_FAV_BACKUP_GROUP: 'weibo_fav_backup_group',
  663. KEY_SETTING_AUTO_BACKUP_FAV: 'weibo_setting_auto_backup_fav'
  664. };
  665.  
  666.  
  667. /**
  668. * 接口
  669. */
  670. class Interface {
  671.  
  672. /**
  673. * 构造函数
  674. * @param {字符串} name 接口名
  675. * @param {字符串数组} methods 该接口所包含的所有方法
  676. */
  677. constructor(name, methods) {
  678.  
  679. //判断接口的参数个数(第一个为接口对象,第二个为参数数组)
  680. if (arguments.length != 2) {
  681. throw new Error('创建的接口对象参数必须为两个,第二个为方法数组!');
  682. }
  683.  
  684. // 判断第二个参数是否为数组
  685. if(!Array.isArray(methods)){
  686. throw new Error('参数2必须为字符串数组!');
  687. }
  688.  
  689. //接口对象引用名
  690. this.name = name;
  691.  
  692. //自己的属性
  693. this.methods = []; //定义一个内置的空数组对象 等待接受methods里的元素(方法名称)
  694.  
  695. //判断数组是否中的元素是否为string的字符串
  696. for (var i = 0; i < methods.length; i++) {
  697.  
  698. //判断方法数组里面是否为string(字符串)的属性
  699. if (typeof methods[i] != 'string') {
  700. throw new Error('方法名必须是string类型的!');
  701. }
  702.  
  703. //把他放在接口对象中的methods中(把接口方法名放在Interface对象的数组中)
  704. this.methods.push(methods[i]);
  705. }
  706. }
  707.  
  708. /**
  709. * 实现
  710. * @param {对象} obj 待实现接口的对象
  711. * @param {接口} I 接口对象
  712. * @param {对象} proxy 接口的实现
  713. * @return {对象} 扩展后的当前对象
  714. */
  715. static impl(obj, I, proxy) {
  716.  
  717. if (I.constructor != Interface) {
  718. throw new Error("参数2不是一个接口!");
  719. }
  720.  
  721. // 校验实现是否实现了接口的每一个方法
  722. for (var i = 0; i < I.methods.length; i++) {
  723.  
  724. // 方法名
  725. var methodName = I.methods[i];
  726.  
  727. //判断obj中是否实现了接口的方法和methodName是方法(而不是属性)
  728. if (!proxy[methodName] || typeof proxy[methodName] != 'function') {
  729. throw new Error('有接口的方法没实现');
  730. }
  731.  
  732. // 将代理中的方法渡给obj
  733. obj[methodName] = proxy[methodName];
  734. }
  735. }
  736. }
  737. /*jshint esversion: 6 */
  738.  
  739.  
  740. /**
  741. * 微博解析器接口
  742. */
  743. const WeiboResolver = new Interface("WeiboResolver",[
  744. "getResolverName",
  745. "addDownloadBtnToWeiboCard", //
  746. "showPhotoLinksPopPanel", //
  747. "findWeiboCardMid", //
  748. "isWeiboDelete", //
  749. "downloadWeiboCardPhotos",
  750. "addFavWeiboBackup",
  751. "getFavWeiboBackup",
  752. "removeFavWeiboBackup",
  753. "generateBatchDownloadOptionsFromBackup",
  754. "batchDownloadPageWeiboCard",
  755. "showFavWeiboRestoreBtn",
  756. "findFeedCardItemList",
  757. "findCardArrowDown",
  758. "findElemClosestCard",
  759. "findCardPhotoDownloadBtn",
  760. "findDeletedCard",
  761. "findCardFavBtn",
  762. "findCardShowBackupBtn",
  763. "findCardRebuildBackupBtn",
  764. "findCardRestoreBackupBtn",
  765. ]);
  766. /*jshint esversion: 6 */
  767.  
  768.  
  769. const OldWeiboResolver = {};
  770.  
  771. Interface.impl(OldWeiboResolver, WeiboResolver, {
  772. "getResolverName": function() {
  773. return "OldWeiboResolver"
  774. },
  775. "addDownloadBtnToWeiboCard": function ($wb_card) {
  776. var $card_btn_list = $wb_card.find(".WB_feed_detail .WB_screen .layer_menu_list ul:nth-child(1)");
  777. if ($card_btn_list.find(".WB_card_photos_download").length == 0) {
  778. $card_btn_list.append('<li class="WB_card_photos_download" title="下载文件和链接"><a>打包下载</a></li>');
  779. $card_btn_list.append('<li class="WB_card_photos_download WB_card_photos_download_only_download_url" title="仅下载链接,不下载文件"><a>下载链接</a></li>');
  780. $card_btn_list.append('<li class="WB_card_photos_download WB_card_photos_download_only_print_url" title="仅打印出链接"><a>打印链接</a></li>');
  781. $card_btn_list.append('<li class="WB_card_photos_show_fav_weibo_backup" title="查看当前微博是否有备份"><a>显示备份</a></li>');
  782. $card_btn_list.append('<li class="WB_card_photos_rebuild_fav_weibo_backup" title="重新备份当前微博"><a>重新备份</a></li>');
  783. }
  784. },
  785. "showPhotoLinksPopPanel": function ($wb_card, options, removeOnClickBody) {
  786. const resolver = this;
  787. options.names || (options.names = {});
  788. options.callback || (options.callback = {});
  789. const $pop = $('<div class="W_layer W_layer_pop download-link-pop"><div class="content link-list">' +
  790. '<div class="sidebar"><span class="sidebar-btn download-all-link" title="全部打包下载">打包下载</span><span class="sidebar-btn copy-all-link" title="复制所有链接">全部复制</span></div>' +
  791. '<ul></ul></div><div class="content preview"><img></div></div>'),
  792. $link_ul = $pop.find('.link-list ul'),
  793. $preview_img = $pop.find('.preview img'),
  794. photos = options.files, card = options.names.card || options.names.forwardCard || {};
  795. removeOnClickBody = removeOnClickBody !== false;
  796. $wb_card.find('.WB_feed_detail').append($pop);
  797. $.each(photos, function(i, photo) {
  798. if (photo.location == 'videos' && photo.download_disable) {
  799. $link_ul.append(`<li><a href="${photo.url}" target="_blank" data-location="${photo.location}" class="clearfix" title="视频文件链接暂时无法备份,请通过视频链接去尝试获取">${photo.fileName}</a></li>`);
  800. } else {
  801. $link_ul.append(`<li><a href="${photo.url}" target="_blank" download="${photo.fileName}" data-location="${photo.location}" class="clearfix" title="${photo.location == 'photos' ? '点击下载,右键链接另存为' : '右键链接另存为'}">${photo.url}</a></li>`);
  802. }
  803. });
  804. $link_ul.on({
  805. 'mouseenter': function() {
  806. let $self = $(this);
  807. if ($self.attr('data-location') == 'photos') {
  808. $preview_img.attr('src', $self.attr('href').replace('/large/', '/mw690/')).parent().show(); //css('visibility', 'visible');
  809. $wb_card.hiding = false;
  810. }
  811. },
  812. 'mouseleave': function() {
  813. if ($wb_card.hiding === true) {
  814. return;
  815. }
  816. setTimeout(function () {
  817. if ($wb_card.hiding === true) {
  818. $preview_img.attr('src', '').parent().hide(); //.css('visibility', 'hidden');
  819. }
  820. $wb_card.hiding === false;
  821. },500);
  822. $wb_card.hiding = true;
  823. },
  824. 'click': function(e) {
  825. let $self = $(this), url = $self.attr('href'), fileName = $self.attr('download');
  826. if ($self.attr('data-location') == 'photos') {
  827. let notify_download_media = toastr.success('正在下载图片~', '', {
  828. "progressBar": false,
  829. "hideDuration": 0,
  830. "showDuration": 0,
  831. "timeOut": 0,
  832. "closeButton": false,
  833. });
  834. // GM_download({'url': url, 'name': fileName, 'saveAs': true});
  835. common_utils.ajaxDownload(url, function (blob) {
  836. if (blob) {
  837. if (blob.type === 'image/gif' && fileName.indexOf('.gif') === -1) {
  838. fileName = fileName.replace(/\.[^.]+$/, '.gif');
  839. }
  840. common_utils.downloadBlobFile(blob, fileName);
  841. } else {
  842. toastr.error('请手动打开链接下载', '下载失败');
  843. }
  844. notify_download_media.css("display", "none").remove();
  845. });
  846. // e.stopImmediatePropagation();
  847. return false;
  848. }
  849. }
  850. }, 'li a');
  851. $pop.on('click', '.copy-all-link', function() {
  852. GM_setClipboard($link_ul[0].innerText);
  853. toastr.success('复制全部链接成功');
  854. }).on('click', '.download-all-link', function() {
  855. options.isNeedConfirmDownload = true;
  856. options.only_download_url = false;
  857. options.only_print_url = false;
  858. // if (options.files && !options.callback.parseFiles_callback) {
  859. // options.callback.parseFiles_callback = function () {
  860. // return options.files;
  861. // }
  862. // }
  863. resolver.downloadWeiboCardPhotos($wb_card, options);
  864. });
  865. if (removeOnClickBody) {
  866. function remove(ev) {
  867. if(!ev.target.classList.contains('download-temp-node') && !$pop[0].contains(ev.target)){
  868. $pop.remove();
  869. $('body').off("click", remove);
  870. }
  871. }
  872. $('body').on("click", remove);
  873. }
  874. console.log('\n--★-- print -- ' + (options.names.folderName || card.name || (card.text ? card.text.substr(0, 15) : card.mid) || '照片链接') + ' ----★--');
  875. console.table(JSON.parse(JSON.stringify(photos)), ['location', 'url']);
  876. console.log('当url被省略可以复制下面的链接,也可从上面 >Array(' + photos.length + ') 查看');
  877. $.each(photos, function (i, photo) {
  878. console.log(photo.url);
  879. });
  880. return $pop;
  881. }, //
  882. "findWeiboCardMid": function ($wb_card, findForwardIfHas) {
  883. const resolver = this;
  884. let mid, isForward = $wb_card.attr("isforward") == '1' ? true : false,
  885. isWeiboDelete = $wb_card.children('.WB_empty').length != 0;
  886. if (isForward && findForwardIfHas) {
  887. mid = $wb_card.attr('omid') || $wb_card.find('.WB_feed_detail .WB_detail .WB_feed_expand .WB_expand').find('.WB_handle').attr('mid');
  888. } else {
  889. mid = $wb_card.attr('mid');
  890. if (!mid && isWeiboDelete) {
  891. mid = $wb_card.children('.WB_empty').attr('mid');
  892. }
  893. }
  894. return mid;
  895. }, //
  896. "isWeiboDelete": function ($wb_card, findForwardIfHas) {
  897. const resolver = this;
  898. var isForward = $wb_card.attr("isforward") == '1';
  899. if (isForward && findForwardIfHas) {
  900. return $wb_card.find('.WB_expand > .WB_empty').length > 0;
  901. } else {
  902. return $wb_card.find('> .WB_empty').length > 0;
  903. }
  904. }, //
  905. "downloadWeiboCardPhotos": function (wb_card_node, options) {
  906. const resolver = this;
  907. var $wb_card = (wb_card_node instanceof $) ? wb_card_node : $(wb_card_node);
  908. var config = {
  909. "$wb_card": $wb_card,
  910. "type": 2,
  911. "isNeedConfirmDownload": true, // 下载前是否需要弹出确认框
  912. "isNeedHasFiles": false,
  913. "useQueueDownloadThreshold": 0,
  914. "only_download_url": false, // 是否仅下载链接,true: 只下链接,false:下载文件和链接
  915. "only_print_url": false, // 是否仅打印出链接
  916. "sortByOrigin": true, // 照片按原页面显示顺序排序
  917. "suffix": null,
  918. "callback": {
  919. "parseFiles_callback": function (location_info, options) {
  920. var $wb_detail = $wb_card.find(".WB_feed_detail .WB_detail");
  921. var photo_parse_index = 0;
  922. var video_parse_index = 0;
  923. var photo_arr = [];
  924. // 视频
  925. var $wb_video = $wb_detail.find(".WB_media_wrap .media_box .WB_video");
  926. if ($wb_video.length != 0) {
  927. var feedVideo = {};
  928. var feedVideoCoverImg = {};
  929. var video_data_str = $wb_video.attr("action-data");
  930. var isFeedVideo = video_data_str.match(/&?type=feedvideo\b/) ? true : false;
  931. if (isFeedVideo) {
  932. feedVideo.url = decodeURIComponent(video_data_str.match(/&video_src=([^&]+)/)[1]);
  933. feedVideo.url.indexOf("//") == 0 && (feedVideo.url = "https:" + feedVideo.url);
  934. feedVideo.fileName = feedVideo.url.match(/\/([^/?]+?(\.mp4)?)\?/)[1] + (RegExp.$2 ? "" : ".mp4");;
  935. feedVideo.folder_sort_index = ++video_parse_index;
  936. feedVideo.location = "videos";
  937. feedVideoCoverImg.url = decodeURIComponent(video_data_str.match(/&cover_img=([^&]+)/)[1]);
  938. feedVideoCoverImg.fileName = feedVideoCoverImg.url.match(/\/([^/]+)$/)[1];
  939. if (feedVideoCoverImg.url.indexOf("miaopai.com") != -1 || feedVideoCoverImg.url.indexOf("youku.com") != -1 ) {
  940. feedVideoCoverImg.url = feedVideoCoverImg.url;
  941. feedVideoCoverImg.url.indexOf("//") == 0 && (feedVideoCoverImg.url = "https:" + feedVideoCoverImg.url);
  942. } else {
  943. feedVideoCoverImg.url = "https://wx3.sinaimg.cn/large/" + feedVideoCoverImg.fileName;
  944. }
  945. feedVideoCoverImg.folder_sort_index = ++photo_parse_index;
  946. feedVideoCoverImg.location = "photos";
  947. photo_arr.push(feedVideo);
  948. photo_arr.push(feedVideoCoverImg);
  949. }
  950. var video_sources_str = $wb_video.attr("video-sources");
  951. if (video_sources_str) {
  952. // 取清晰度最高的
  953. var video_source_list = video_sources_str.split("&").filter(function (line) {
  954. return /^\d+=.+/.test(line);
  955. }).sort(function (a, b) {
  956. return parseInt(a.match(/^(\d+)=/)[1]) < parseInt(b.match(/^(\d+)=/)[1]) ? 1 : -1;
  957. }).map(function (url) {
  958. return decodeURIComponent(url.replace(/^\d+=/, ""));
  959. });
  960. if (video_source_list.length > 0) {
  961. feedVideo.url = video_source_list[0].replace('label=mp4_720p', 'label=dash_720p');
  962. feedVideo.fileName = feedVideo.url.match(/\/([^/?]+?(\.mp4)?)\?/)[1] + (RegExp.$2 ? "" : ".mp4");
  963. }
  964. }
  965. }
  966. // 微博故事
  967. var $wb_story = $wb_detail.find(".WB_media_wrap .media_box .li_story");
  968. if ($wb_story.length != 0) {
  969. var weiboStoryVideo = {};
  970. var weibo_story_data_str = $wb_story.attr("action-data");
  971. if (/&gif_ourl=([^&]+)/.test(weibo_story_data_str)) {
  972. weiboStoryVideo.url = decodeURIComponent(RegExp.$1);
  973. } else if (/&gif_url=([^&]+)/.test(weibo_story_data_str)) {
  974. weiboStoryVideo.url = decodeURIComponent(RegExp.$1);
  975. }
  976. if (weiboStoryVideo.url) {
  977. weiboStoryVideo.fileName = weiboStoryVideo.url.match(/\/([^/?]+?(\.mp4)?)\?/)[1] + (RegExp.$2 ? "" : ".mp4");
  978. weiboStoryVideo.folder_sort_index = ++video_parse_index;
  979. weiboStoryVideo.location = "videos";
  980. photo_arr.push(weiboStoryVideo);
  981. }
  982. }
  983. // 照片
  984. var $dataNode = $wb_detail.find(".WB_media_wrap .media_box ul");
  985. var pic_data_str = ($dataNode.children('li').length == 1 ? $dataNode.children('li') : $dataNode).attr("action-data");
  986. var pic_ids_str_m = pic_data_str && pic_data_str.match(/&pic_ids=([^&]+)/);
  987. if (pic_ids_str_m) {
  988. // livephoto
  989. var pic_video_ids = null;
  990. var pic_video_ids_str_m = pic_data_str.match(/&pic_video=([^&]+)/);
  991. if (pic_video_ids_str_m) {
  992. pic_video_ids = pic_video_ids_str_m[1].split(",").map(function (pair) {
  993. return pair.split(":")[1];
  994. });
  995. }
  996. var pic_thumb_str = pic_data_str.match(/&thumb_picSrc=([^&]+)/) && RegExp.$1;
  997. var parsePhotosFromIds = function (pic_ids, pic_video_ids) {
  998. $.each(pic_ids, function (i, photo_id) {
  999. var photo = {};
  1000. photo.photo_id = photo_id;
  1001. if (pic_thumb_str && pic_thumb_str.indexOf(photo_id + ".gif") != -1) { // 这里只能判断前九张图是否是gif
  1002. photo.url = "https://wx3.sinaimg.cn/large/" + photo_id + ".gif";
  1003. } else {
  1004. photo.url = "https://wx3.sinaimg.cn/large/" + photo_id + ".jpg";
  1005. }
  1006. photo.folder_sort_index = ++photo_parse_index;
  1007. photo.location = "photos";
  1008. photo_arr.push(photo);
  1009. });
  1010. pic_video_ids && $.each(pic_video_ids, function (i, photo_video_id) {
  1011. var photo = {};
  1012. photo.video_id = photo_video_id;
  1013. photo.url = "https://video.weibo.com/media/play?livephoto=//us.sinaimg.cn/" + photo_video_id + ".mov&KID=unistore,videomovSrc";
  1014. photo.fileName = photo_video_id + ".mov";
  1015. photo.folder_sort_index = ++video_parse_index;
  1016. photo.location = "videos";
  1017. photo_arr.push(photo);
  1018. });
  1019. };
  1020. if (/over9pic=1&/.test(pic_data_str) && !/isloadedover9pids=1/.test(pic_data_str)) {
  1021. var deferred = $.Deferred();
  1022. var isForward = $wb_card.attr("isforward") == "1" ? true : false;
  1023. var mid;
  1024. if (!isForward) {
  1025. mid = $wb_card.attr("mid")
  1026. } else {
  1027. mid = $wb_card.find(".WB_feed_detail .WB_detail .WB_feed_expand .WB_expand .WB_handle").attr("mid");
  1028. }
  1029. ($.ajax || GM.xmlHttpRequest || GM_xmlHttpRequest)({
  1030. method: 'get',
  1031. url: 'https://' + document.location.host + '/aj/mblog/getover9pic?' + $.param({
  1032. "ajwvr": 6,
  1033. "mid": mid,
  1034. "__rnd": new Date().getTime(),
  1035. }),
  1036. responseType: 'json',
  1037. //headers: {
  1038. // 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36',
  1039. // 'cache-control': 'max-age=0',
  1040. // 'referer': document.location.href,
  1041. // 'cookie': document.cookie
  1042. //},
  1043. success/*onload*/: function(responseType) {
  1044. let response = responseType/*responseType.response*/,
  1045. picIds = pic_ids_str_m[1].split(",");
  1046. if (response.data && response.data.pids && Array.isArray(response.data.pids)) {
  1047. response.data.pids.forEach(function(overPid) {
  1048. if (picIds.indexOf(overPid) == -1) {
  1049. picIds.push(overPid);
  1050. }
  1051. });
  1052. }
  1053.  
  1054. parsePhotosFromIds(picIds, pic_video_ids);
  1055. deferred.resolve(photo_arr);
  1056. },
  1057. error: function() {
  1058. toastr.error('获取18图失败');
  1059. parsePhotosFromIds(pic_ids_str_m[1].split(","), pic_video_ids);
  1060. deferred.resolve(photo_arr);
  1061. }
  1062. });
  1063. // $wb_detail.find(".WB_media_wrap .media_box ul .WB_pic .W_icon_tag_9p").trigger("click");
  1064. // setTimeout(function () {
  1065. // parsePhotosFromIds($wb_detail.find(".WB_media_wrap .media_box ul").attr("action-data").match(/&pic_ids=([^&]+)&/)[1].split(","));
  1066. // deferred.resolve(photo_arr);
  1067. // }, 1500);
  1068. return deferred; // 需要异步获取直接返回
  1069. } else {
  1070. parsePhotosFromIds(pic_ids_str_m[1].split(","), pic_video_ids);
  1071. }
  1072. } else {
  1073. var $wb_pics = $wb_detail.find(".WB_media_wrap .media_box ul .WB_pic img");
  1074. var regexp_search = /^(https?:\/\/(?:(?:ww|wx|ws|tvax|tva)\d+|wxt|wt)\.sinaimg\.(?:cn|com)\/)([\w\.]+)(\/.+)(?:\?.+)?$/i;
  1075. $.each($wb_pics, function (i, img) {
  1076. var photo = {};
  1077. var thumb_url = img.src;
  1078. photo.url = thumb_url;
  1079. var m = thumb_url.match(regexp_search);
  1080. if (m) {
  1081. if (m[2] != "large") {
  1082. photo.url = m[1] + "large" + m[3];
  1083. }
  1084. }
  1085. photo.folder_sort_index = ++photo_parse_index;
  1086. photo.location = "photos";
  1087. photo_arr.push(photo);
  1088. });
  1089. }
  1090. return photo_arr;
  1091. },
  1092. "makeNames_callback": function (photos, location_info, options) {
  1093. var names = {},
  1094. isForward = $wb_card.attr("isforward") == "1" ? true : false; // 是否是转发
  1095. names.infoName = "card_info.txt";
  1096. names.infoValue = "";
  1097. var printCardInfoToText = function (card) {
  1098. let infoValue = '', isForward = card.forward === true;
  1099. infoValue += "-----------" + (isForward ? "forward card" : "card") + "--------------" + "\r\n";
  1100. $.each(card, function (key, value) {
  1101. if (key !== 'user' && key !== 'photos' && key !== 'videos' && key !== 'rootCard') {
  1102. infoValue += (isForward ? "forward_" : "") + "card_" + key + ":" + value + "\r\n";
  1103. }
  1104. });
  1105. infoValue += "-----------------------------------" + "\r\n";
  1106. $.each(card.user, function (key, value) {
  1107. infoValue += (isForward ? "forward_" : "") + "user_" + key + ":" + value + "\r\n";
  1108. });
  1109. infoValue += "-----------------------------------" + "\r\n";
  1110. return infoValue;
  1111. }
  1112. var findCardInfo = function ($wb_detail, isForward) {
  1113. if (resolver.isWeiboDelete($wb_card, isForward)) {
  1114. return {'user': {}};
  1115. }
  1116. var $user_home_link = $wb_detail.find(".W_fb").eq(0);
  1117. var $card_link = $wb_detail.find(".WB_from a").eq(0);
  1118. var user = {};
  1119. user.uid = $user_home_link.attr("usercard").match(/id=(\d+)/)[1];
  1120. user.nickname = $user_home_link.attr("nick-name") || $user_home_link.text();
  1121. user.home_link = $user_home_link.prop("href").replace(/\?.*/, "");
  1122. var card = {};
  1123. card.forward = isForward;
  1124. card.link = $card_link.prop("href").replace(/\?.*/, "");
  1125. card.id = card.link.match(/\d+\/([A-Za-z0-9]+)$/)[1];
  1126. card.mid = isForward ? $wb_detail.find(".WB_handle").attr("mid") : $wb_detail.closest(".WB_cardwrap").attr("mid");
  1127. card.date = $card_link.attr("title");
  1128. card.date_timestamp = $card_link.attr("date");
  1129. card.text = $wb_detail.find(".WB_text").eq(0).prop("innerText").replace(/[\u200b]+$/, "").replace(/^\s*|\s*$/g, "");
  1130. var textLines = card.text.split(/\s{4,}|\s*\n\s*/);
  1131. card.name = textLines[0];
  1132. if (card.name.length <= 5 && textLines.length > 1) {
  1133. card.name += textLines[1];
  1134. }
  1135. if (card.name.length > 30) {
  1136. card.name = card.name.substring(0, 30);
  1137. }
  1138. card.photo_count = photos.length;
  1139. var tab_type_flag = $(".WB_main_c").find("div:nth-child(1)").attr("id");
  1140. if (tab_type_flag && /.*(favlistsearch|likelistoutbox)$/.test(tab_type_flag)) {
  1141. var $page_list = $(".WB_cardwrap .W_pages .layer_menu_list ul");
  1142. if ($page_list.length != 0) {
  1143. var maxPage = parseInt($page_list.find("li:nth-child(1) > a").text().match(/第(\d+)页/)[1]);
  1144. var currPage = parseInt($page_list.find(".cur > a").text().match(/第(\d+)页/)[1]);
  1145. card.countdown_page = maxPage - currPage + 1;
  1146. }
  1147. }
  1148. card.user = user;
  1149. card.photos = photos;
  1150. return card;
  1151. };
  1152. names.card = findCardInfo($wb_card.find(".WB_feed_detail .WB_detail"), false); // 主贴的信息
  1153. names.infoValue += printCardInfoToText(names.card);
  1154. if (isForward) {
  1155. // 转发的贴的信息
  1156. names.forwardCard = findCardInfo($wb_card.find(".WB_feed_detail .WB_detail .WB_feed_expand .WB_expand"), true);
  1157. names.infoValue += printCardInfoToText(names.forwardCard);
  1158. }
  1159. names.zipName = names.card.user.nickname + "_" + names.card.user.uid + "_" + names.card.id + "_" + (names.card.name
  1160. .replace(/\.\./g, "")
  1161. .replace(/[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/ig, "").replace(/[\u200b]+$/, "")
  1162. .replace(/(^[_-]+)|([_-]+$)/g, "")
  1163. .replace(/(^\s+)|(\s+$)/g, ""));
  1164. names.folderName = names.zipName;
  1165. names.prefix = null;
  1166. names.suffix = options.suffix;
  1167. return names;
  1168. },
  1169. "beforeFilesDownload_callback": function (photos, names, location_info, options, zip, main_folder) {
  1170. const paddingZeroLength = String(photos.length).length,
  1171. card = (names.forwardCard || names.card),
  1172. sortByOrigin = options.sortByOrigin;
  1173. $.each(photos, function (i, photo) {
  1174. if (!photo.fileName) {
  1175. photo.fileName = photo.url.substring(photo.url.lastIndexOf('/') + 1);
  1176. }
  1177. if (sortByOrigin) {
  1178. photo.originName = photo.fileName;
  1179. photo.fileName = card.user.uid + '_' + card.id + '_' + common_utils.paddingZero(photo.folder_sort_index, paddingZeroLength) + '_' + photo.originName;
  1180. }
  1181. });
  1182. options.failFiles = undefined;
  1183. },
  1184. "beforeFileDownload_callback": function (photo, location_info, options, zipFileLength, zip, main_folder, folder) {
  1185. if (options.only_download_url || options.only_print_url) {
  1186. return false;
  1187. } else {
  1188. if (photo['download_disable']) {
  1189. return false;
  1190. } else {
  1191. return true;
  1192. }
  1193. }
  1194. },
  1195. "eachFileOnload_callback": function (blob, photo, location_info, options, zipFileLength, zip, main_folder, folder) {
  1196. if (blob == null) {
  1197. if (!options.failFiles) {
  1198. options.failFiles = [];
  1199. }
  1200. options.failFiles.push(photo);
  1201. } else if (photo.location == 'photos' && blob.type === 'image/gif' && photo.fileName.indexOf('.gif') === -1) {
  1202. // 如果在超过9张图(over9pic)中含有gif,那么后缀需要根据content-type来判断
  1203. let suffixRegex = /\.[^.]+$/, suffix = '.gif';
  1204. photo.fileName = photo.fileName.replace(suffixRegex, suffix);
  1205. photo.url = photo.url.replace(suffixRegex, suffix);
  1206. photo.originName && (photo.originName = photo.originName.replace(suffixRegex, suffix));
  1207. }
  1208. return true;
  1209. },
  1210. "allFilesOnload_callback": function (photos, names, location_info, options, zip, main_folder) {
  1211. let photo_urls_str = "", failPhotoListStr = "", photo_url, failFile;
  1212. // 链接列表文件
  1213. $.each(photos, function (i, photo) {
  1214. if (photo.location == 'videos' && photo.url.indexOf('f.video.weibocdn.com') != -1 && photo.url.indexOf('&Expires=') == -1 && !/\.mov$/.test(photo.fileName)) {
  1215. photo_url = 'http://f.video.weibocdn.com/' + (photo.originName || photo.fileName) + '?KID=unistore,video';
  1216. } else {
  1217. photo_url = photo.url;
  1218. }
  1219. let line = ((photo.location ? (photo.location + "/") : "" ) + photo.fileName) + "\t" + photo_url + "\r\n";
  1220. photo_urls_str += line;
  1221. });
  1222. main_folder.file("photo_url_list.txt", photo_urls_str);
  1223. // 失败链接列表文件
  1224. if (options.failFiles && options.failFiles.length > 0) {
  1225. toastr.error("共 " + options.failFiles.length + " 张下载失败,已记录在photos_fail_list.txt!", "", {
  1226. "progressBar": false,
  1227. timeOut: 0
  1228. });
  1229. for (let i in options.failFiles) {
  1230. failFile = options.failFiles[i];
  1231. failPhotoListStr += (failFile.location + "/" + failFile.fileName + "\t" + failFile.url + "\r\n");
  1232. }
  1233. main_folder.file("photos_fail_list.txt", failPhotoListStr);
  1234. }
  1235. // 如果只是打印链接
  1236. if (options.only_print_url) {
  1237. resolver.showPhotoLinksPopPanel($wb_card, options);
  1238. toastr.success("已打印");
  1239. return false;
  1240. }
  1241. }
  1242. }
  1243. };
  1244. if (options) {
  1245. $.extend(true, config, options);
  1246. }
  1247. batchDownload(config);
  1248. },
  1249. "addFavWeiboBackup": function ($wb_card) { // 保存备份
  1250. const resolver = this;
  1251. var options = {
  1252. "only_print_url": true,
  1253. "isNeedConfirmDownload": false,
  1254. "callback": {
  1255. "allFilesOnload_callback": function (photos, names, location_info, options, zip, main_folder) {
  1256. let fav_backup_group, beforeSaveCard, saveCard = {}, rootCard = {}, user = {}, picNames = [], livePhotos = [], videos = [], card = names.forwardCard || names.card;
  1257. if (!card.mid) {
  1258. toastr.error('未找到mid,代码需要改进');
  1259. return false;
  1260. }
  1261. fav_backup_group = GM_getValue(Constants.KEY_FAV_BACKUP_GROUP, {});
  1262. saveCard.id = card.id;
  1263. saveCard.mid = card.mid;
  1264. saveCard.date = card.date;
  1265. saveCard.text = card.text;
  1266. saveCard.forward = card.forward;
  1267. card.countdown_page !== undefined && (saveCard.countdown_page = card.countdown_page);
  1268. user.uid = card.user.uid;
  1269. user.nickname = card.user.nickname;
  1270. $.each(photos, function (i, photo) {
  1271. let originName = photo.originName || photo.fileName;
  1272. if (photo.location == 'photos') {
  1273. picNames.push(originName);
  1274. } else if (photo.location == 'videos') {
  1275. if (/\.mov$/.test(originName)) {
  1276. livePhotos.push(originName);
  1277. } else if (photo.url.indexOf('f.video.weibocdn.com') != -1) { // 先存起来,可能以后有新办法
  1278. videos.push(photo.video_id);
  1279. }
  1280. }
  1281. });
  1282. saveCard.user = user;
  1283. saveCard.photos = picNames;
  1284. saveCard.livePhotos = livePhotos;
  1285. saveCard.videos = videos;
  1286. if (names.forwardCard) {
  1287. beforeSaveCard = fav_backup_group[String(saveCard.mid)];
  1288. // 如果被转发的微博执行了明确收藏操作就不覆盖该forward值
  1289. if (saveCard.forward === true && beforeSaveCard && beforeSaveCard.forward === false) {
  1290. saveCard.forward = false;
  1291. }
  1292. rootCard.root = true;
  1293. rootCard.id = names.card.id;
  1294. rootCard.mid = names.card.mid;
  1295. rootCard.date = names.card.date;
  1296. rootCard.text = names.card.text;
  1297. rootCard.forward = names.forwardCard.mid;
  1298. rootCard.user = {
  1299. uid: names.card.user.uid,
  1300. nickname: names.card.user.nickname,
  1301. }
  1302. }
  1303. fav_backup_group[String(saveCard.mid)] = saveCard;
  1304. if (names.forwardCard) {
  1305. fav_backup_group[String(rootCard.mid)] = rootCard;
  1306. }
  1307. GM_setValue(Constants.KEY_FAV_BACKUP_GROUP, fav_backup_group);
  1308. toastr.success("收藏备份到本地成功~");
  1309. return false;
  1310. },
  1311. }
  1312. };
  1313. resolver.downloadWeiboCardPhotos($wb_card, options);
  1314. },
  1315. "getFavWeiboBackup": function (mid) { // 获取备份
  1316. const resolver = this;
  1317. if (!mid) {
  1318. toastr.error('未找到mid,代码需要改进');
  1319. return;
  1320. }
  1321. let fav_backup_group = GM_getValue(Constants.KEY_FAV_BACKUP_GROUP, {}), card = fav_backup_group[String(mid)], rootCard, photos = [], photo_parse_index = 0, video_parse_index = 0;
  1322. if (card) {
  1323. if (card.root) { // 如果传入的mid只是一个转发mid
  1324. rootCard = card;
  1325. card = fav_backup_group[String(rootCard.forward)]; // 获取实际存储数据的mid
  1326. if (card) {
  1327. card.forward = true;
  1328. card.rootCard = rootCard;
  1329. if (!rootCard.name) {
  1330. rootCard.name = (rootCard.text ? rootCard.text.substr(0, 15) : rootCard.mid);
  1331. }
  1332. } else {
  1333. card = rootCard;
  1334. }
  1335. }
  1336. card.photos && $.each(card.photos, function(i, fileName) {
  1337. let photo = {url: 'https://wx3.sinaimg.cn/large/' + fileName, fileName: fileName, location: 'photos', folder_sort_index: ++photo_parse_index};
  1338. photos.push(photo);
  1339. });
  1340. card.livePhotos && $.each(card.livePhotos, function(i, fileName) {
  1341. let photo = {url: 'https://video.weibo.com/media/play?livephoto=//us.sinaimg.cn/' + fileName + '&KID=unistore,videomovSrc', fileName: fileName, location: 'videos', folder_sort_index: ++video_parse_index};
  1342. photos.push(photo);
  1343. });
  1344. // card.videos && $.each(card.videos, function(i, fileName) {
  1345. // let photo = {url: 'http://f.video.weibocdn.com/' + fileName + '?KID=unistore,video', fileName: fileName, location: 'videos', folder_sort_index: ++video_parse_index};
  1346. // photos.push(photo);
  1347. // });
  1348. card.videos && $.each(card.videos, function(i, fileName) {
  1349. if (fileName.indexOf(':') !== -1) {
  1350. let urls = (fileName.indexOf('@') !== -1 ? fileName : (fileName + '@')).split('@');
  1351. let title = 'https://video.weibo.com/show?fid=' + urls[0] + (urls[1] ? '<br>( ' + urls[1] + ' )' : '');
  1352. let photo = {url: 'https://video.weibo.com/show?fid=' + urls[0], short_url: urls[1], fileName: title, location: 'videos', folder_sort_index: ++video_parse_index, 'download_disable': true};
  1353. photos.push(photo);
  1354. }
  1355. });
  1356. card.photos = photos;
  1357. delete card.livePhotos;
  1358. delete card.videos;
  1359. if (!card.name) {
  1360. card.name = (card.text ? card.text.substr(0, 15) : card.mid);
  1361. }
  1362. }
  1363. return card;
  1364. },
  1365. "removeFavWeiboBackup": function (mid, cancleIfNotForward) { // 移除备份
  1366. const resolver = this;
  1367. if (!mid) {
  1368. toastr.error('未找到mid,代码需要改进');
  1369. return 400;
  1370. }
  1371. let fav_backup_group = GM_getValue(Constants.KEY_FAV_BACKUP_GROUP, {}), card = fav_backup_group[String(mid)], forwardCard;
  1372. if (card) {
  1373. if (card.root) { // 如果传入的mid只是一个转发mid
  1374. forwardCard = fav_backup_group[String(card.forward)];
  1375. if (forwardCard && forwardCard.forward === true) { // 如果实际存储数据的mid没有明确收藏操作,则一起删除
  1376. delete fav_backup_group[String(forwardCard.mid)];
  1377. }
  1378. }
  1379. if (card.root || cancleIfNotForward !== true || card.forward === true) {
  1380. delete fav_backup_group[String(card.mid)];
  1381. GM_setValue(Constants.KEY_FAV_BACKUP_GROUP, fav_backup_group);
  1382. toastr.success("删除收藏本地备份成功~");
  1383. }
  1384. return 200;
  1385. } else {
  1386. return 404;
  1387. }
  1388. },
  1389. "generateBatchDownloadOptionsFromBackup": function ($wb_card, card) {
  1390. const resolver = this;
  1391. let options = {
  1392. files: card.photos,
  1393. }
  1394. if (resolver.isWeiboDelete($wb_card, true)) {
  1395. options.callback = {
  1396. 'parseFiles_callback': function () {
  1397. return card.photos;
  1398. },
  1399. 'makeNames_callback': function (files, location_info, options) {
  1400. var names = {};
  1401. names.infoName = "card_info.txt";
  1402. names.infoValue = "";
  1403. var printCardInfoToText = function (card) {
  1404. let infoValue = '', isForward = card.forward === true;
  1405. infoValue += "-----------" + (isForward ? "forward card" : "card") + "--------------" + "\r\n";
  1406. $.each(card, function (key, value) {
  1407. if (key !== 'user' && key !== 'photos' && key !== 'videos' && key !== 'rootCard') {
  1408. infoValue += (isForward ? "forward_" : "") + "card_" + key + ":" + value + "\r\n";
  1409. }
  1410. });
  1411. infoValue += "-----------------------------------" + "\r\n";
  1412. $.each(card.user, function (key, value) {
  1413. infoValue += (isForward ? "forward_" : "") + "user_" + key + ":" + value + "\r\n";
  1414. });
  1415. infoValue += "-----------------------------------" + "\r\n";
  1416. return infoValue;
  1417. }
  1418. card.link = 'https://weibo.com/' + card.user.uid + '/' + card.id;
  1419. card.user.home_link = 'https://weibo.com/u/' + card.user.uid;
  1420. if (card.rootCard) {
  1421. card.rootCard.link = 'https://weibo.com/' + card.rootCard.user.uid + '/' + card.rootCard.id;
  1422. card.rootCard.user.home_link = 'https://weibo.com/u/' + card.rootCard.user.uid;
  1423. names.card = card.rootCard;
  1424. names.forwardCard = card;
  1425. } else {
  1426. names.card = card;
  1427. }
  1428. // 主贴的信息
  1429. names.infoValue += printCardInfoToText(names.card);
  1430. if (names.forwardCard) { // 被转发贴的信息
  1431. names.infoValue += printCardInfoToText(names.forwardCard);
  1432. }
  1433. names.zipName = names.card.user.nickname + "_" + names.card.user.uid + "_" + names.card.id + "_" + (names.card.name
  1434. .replace(/\.\./g, "")
  1435. .replace(/[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/ig, "").replace(/[\u200b]+$/, "")
  1436. .replace(/(^[_-]+)|([_-]+$)/g, "")
  1437. .replace(/(^\s+)|(\s+$)/g, ""));
  1438. names.folderName = names.zipName;
  1439. names.prefix = null;
  1440. names.suffix = options.suffix;
  1441. return names;
  1442. },
  1443. };
  1444. }
  1445. return options;
  1446. },
  1447. "batchDownloadPageWeiboCard": function() {
  1448. const resolver = this;
  1449. if ($('#Pl_Official_Headerv6__1').length == 0 && !/weibo\.com\/(\d+\/profile|\d+(\?|$)|u\/\d+(\?|$)|fav(\?|$)|like\/outbox(\?|$))/.test(document.location.href)) {
  1450. toastr.error('只支持在用户主页下载');
  1451. return;
  1452. }
  1453. if (!confirm('确定要下载本页所有微博吗?')) {
  1454. return;
  1455. }
  1456. let $notify_start = toastr.success("准备批量下载本页~", "", {
  1457. "progressBar": false,
  1458. "hideDuration": 0,
  1459. "showDuration": 0,
  1460. "timeOut": 0,
  1461. "closeButton": false
  1462. }).toggleClass('batch-download-list-tips', true), count = 0, failPhotoCount = 0;
  1463. const downloadQueue = new common_utils.TaskQueue(function($wb_card) {
  1464. return $.Deferred(function(dfd) {
  1465. if ($wb_card.length > 0 && ($wb_card.attr('action-type') === 'feed_list_item' || $wb_card.children().eq(0).attr('action-type') === 'feed_list_item')) {
  1466. count++;
  1467. if ($notify_start.length === 0 || !$notify_start.is(':visible')) {
  1468. $notify_start = toastr.success("本页批量下载第 1 个~", "", {
  1469. "progressBar": false,
  1470. "hideDuration": 0,
  1471. "showDuration": 0,
  1472. "timeOut": 0,
  1473. "closeButton": false
  1474. }).toggleClass('batch-download-list-tips', true);
  1475. } else {
  1476. $notify_start.find(".toast-message").text(`本页批量下载第 ${count} 个~`);
  1477. }
  1478. $wb_card[0].scrollIntoView({block: 'center'});
  1479. let options = {};
  1480. if (resolver.isWeiboDelete($wb_card, true)) {
  1481. let mid = resolver.findWeiboCardMid($wb_card, false),
  1482. omid = resolver.findWeiboCardMid($wb_card, true),
  1483. card;
  1484. card = resolver.getFavWeiboBackup(mid) || (mid != omid && resolver.getFavWeiboBackup(omid)) || null;
  1485. if (card) {
  1486. options = resolver.generateBatchDownloadOptionsFromBackup($wb_card, card);
  1487. } else {
  1488. dfd.resolve();
  1489. return;
  1490. }
  1491. }
  1492. $.extend(true, options, {
  1493. isNeedConfirmDownload: false,
  1494. callback:{
  1495. beforeZipFileDownload_callback:function (zip_blob, files, names, location_info, options, zip, main_folder) {
  1496. common_utils.downloadBlobFile(zip_blob, names.zipName + ".zip");
  1497. failPhotoCount += (options.failFiles && options.failFiles.length || 0);
  1498. dfd.resolve();
  1499. }
  1500. }
  1501. });
  1502. resolver.downloadWeiboCardPhotos($wb_card, options);
  1503. } else {
  1504. dfd.reject();
  1505. }
  1506. }).done(function() {
  1507. $('#toast-container').children('.toast').not('.batch-download-list-tips').hide().remove();
  1508. $notify_start.find(".toast-message").text(`本页批量下载第 ${count} 个完成~`);
  1509. if ($wb_card.next().attr('node-type') === 'lazyload') {
  1510. setTimeout(function(){
  1511. downloadQueue.append($wb_card.next());
  1512. }, 1500);
  1513. } else {
  1514. downloadQueue.append($wb_card.next());
  1515. }
  1516. }).fail(function() {
  1517. $notify_start.hide().remove();
  1518. if (count > 0) {
  1519. toastr.success('本页批量下载完成');
  1520. if (failPhotoCount > 0) {
  1521. toastr.error(`共 ${failPhotoCount} 张图片或视频下载失败~<br>链接已记录在photos_fail_list.txt`);
  1522. }
  1523. } else {
  1524. toastr.error('本页没有微博');
  1525. }
  1526. });
  1527. });
  1528. downloadQueue.append($('.WB_feed').children('.WB_cardwrap[action-type="feed_list_item"]').eq(0));
  1529. },
  1530. "showFavWeiboRestoreBtn": function () {
  1531. $('.WB_cardwrap:not(.has-set-restore-btn)').find('> .WB_empty, .WB_expand > .WB_empty').find('.WB_innerwrap p')
  1532. .prepend('<button class="restore-backup-fav-weibo" style="margin-right:15px;cursor:pointer;" title="made by Jeffrey.Deng">查看备份</button>').closest('.WB_cardwrap:not(.has-set-restore-btn)').addClass('has-set-restore-btn');
  1533. },
  1534. "findFeedCardItemList": function () {
  1535. var list = $('.WB_feed').children('.WB_cardwrap[action-type="feed_list_item"]');
  1536. return list.length > 0 ? list : $('.WB_cardwrap');
  1537. },
  1538. "findCardArrowDown": function () {
  1539. return $(".WB_cardwrap .WB_screen .ficon_arrow_down");
  1540. },
  1541. "findElemClosestCard": function (elem) {
  1542. return $(elem).closest(".WB_cardwrap");
  1543. },
  1544. "findCardPhotoDownloadBtn": function() {
  1545. return $('.WB_cardwrap .WB_screen .layer_menu_list .WB_card_photos_download');
  1546. },
  1547. "findDeletedCard": function() {
  1548. return $('.WB_cardwrap:not(.has-set-restore-btn) > .WB_empty, .WB_cardwrap:not(.has-set-restore-btn) .WB_expand > .WB_empty');
  1549. },
  1550. "findCardFavBtn": function() {
  1551. return $('.WB_cardwrap .WB_feed_handle a[action-type="fl_favorite"]');
  1552. },
  1553. "findCardShowBackupBtn": function() {
  1554. return $('.WB_cardwrap .WB_screen .layer_menu_list .WB_card_photos_show_fav_weibo_backup');
  1555. },
  1556. "findCardRebuildBackupBtn": function() {
  1557. return $('.WB_cardwrap .WB_screen .layer_menu_list .WB_card_photos_rebuild_fav_weibo_backup');
  1558. },
  1559. "findCardRestoreBackupBtn": function() {
  1560. return $('.WB_cardwrap .restore-backup-fav-weibo');
  1561. },
  1562. });
  1563.  
  1564. const NewWeiboResolver = {
  1565. 'getNameFromText': function(text) {
  1566. var textLines = $('<div/>').html(text).text().split(/\s{4,}|\s*\n\s*/);
  1567. var name = textLines[0];
  1568. if (name.length <= 5 && textLines.length > 1) {
  1569. name += textLines[1];
  1570. }
  1571. if (name.length > 30) {
  1572. name = name.substring(0, 30);
  1573. }
  1574. return name
  1575. },
  1576. 'formatWeiboDate': function (dateText) {
  1577. if (!dateText) {
  1578. let date = new Date();
  1579. return common_utils.formatDate(date, 'yyyy-MM-dd hh:mm:ss');
  1580. } else if (/[a-zA-Z].*\+[\d]{4}/.test(dateText)) {
  1581. let date = new Date(dateText);
  1582. return common_utils.formatDate(date, 'yyyy-MM-dd hh:mm:ss');
  1583. } else {
  1584. return dateText;
  1585. }
  1586. },
  1587. 'findCardScrollerItem': function($wb_card) {
  1588. return $wb_card.parent().hasClass('wbpro-scroller-item') ? $wb_card.parent() : null;
  1589. },
  1590. 'findCardDefaultData': function($wb_card) {
  1591. const resolver = this;
  1592. let $cardScrollerItem = resolver.findCardScrollerItem($wb_card), defaultData = null;
  1593. if ($cardScrollerItem) {
  1594. if ($cardScrollerItem[0].hasOwnProperty('__vue__') && $cardScrollerItem[0].__vue__.item) {
  1595. defaultData = $cardScrollerItem[0].__vue__.item;
  1596. }
  1597. }
  1598. return defaultData;
  1599. },
  1600. 'findCardData': function($wb_card) {
  1601. return $wb_card.data('weibo');
  1602. },
  1603. 'findCardMblogId': function ($wb_card) {
  1604. const resolver = this;
  1605. let defaultData = resolver.findCardDefaultData($wb_card), mblogid;
  1606. if (defaultData) {
  1607. mblogid = defaultData['mblogid'];
  1608. } else {
  1609. let $author_head = $wb_card.find('> div > header > .woo-box-item-flex > div > div.woo-box-flex.woo-box-alignCenter.woo-box-justifyCenter > a');
  1610. mblogid = $author_head.attr("href").replace(/\?[^?]*$/,'').match(/\/([a-zA-Z0-9]+)$/) && RegExp.$1;
  1611. }
  1612. return mblogid;
  1613. },
  1614. "batchHandleWeiboCard": function(bacthName, bacthSize, callback) {
  1615. const resolver = this;
  1616. if (!/weibo\.com\/(\d+\/profile|\d+(\?|$)|u\/\d+(\?|$)|fav(\?|$)|like\/outbox(\?|$))|(\/u\/page\/fav\/)/.test(document.location.href)) {
  1617. toastr.error('只支持在用户主页下载');
  1618. return;
  1619. }
  1620. if (!confirm(`确定要${bacthName}本页 ${bacthSize} 个微博吗?`)) {
  1621. return;
  1622. }
  1623. var $firstCard = null;
  1624. if (confirm('点击【是】从当前微博开始,点击【否】从第一条微博开始')) {
  1625. $.each(resolver.findFeedCardItemList(), function(i , $card) {
  1626. if ($card.find('header').length > 0 && common_utils.isOnScreen($card.find('header').eq(0)[0], 30)) {
  1627. $firstCard = $card;
  1628. return false;
  1629. }
  1630. });
  1631. } else {
  1632. document.documentElement.scrollTop = 0;
  1633. }
  1634. let $notify_start = toastr.success(`准备${bacthName}本页~`, "", {
  1635. "progressBar": false,
  1636. "hideDuration": 0,
  1637. "showDuration": 0,
  1638. "timeOut": 0,
  1639. "closeButton": false
  1640. }).toggleClass('batch-download-list-tips', true), count = 0, failPhotoCount = 0;
  1641. const downloadQueue = new common_utils.TaskQueue(function($wb_card) {
  1642. return $.Deferred(function(dfd) {
  1643. if ($wb_card.length > 0 && count < bacthSize) {
  1644. count++;
  1645. if ($notify_start.length === 0 || !$notify_start.is(':visible')) {
  1646. $notify_start = toastr.success(`本页${bacthName}第 1 个~`, "", {
  1647. "progressBar": false,
  1648. "hideDuration": 0,
  1649. "showDuration": 0,
  1650. "timeOut": 0,
  1651. "closeButton": false
  1652. }).toggleClass('batch-download-list-tips', true);
  1653. } else {
  1654. $notify_start.find(".toast-message").text(`本页${bacthName}第 ${count} 个~`);
  1655. }
  1656. $wb_card[0].scrollIntoView({block: 'center'});
  1657. callback && callback.call(this, $wb_card, dfd);
  1658. } else {
  1659. dfd.reject();
  1660. }
  1661. }).done(function() {
  1662. $('#toast-container').children('.toast').not('.batch-download-list-tips').hide().remove();
  1663. $notify_start.find(".toast-message").text(`本页${bacthName}第 ${count} 个完成~`);
  1664. $wb_card[0].scrollIntoView({block: 'start'});
  1665. setTimeout(function(){
  1666. var currentCardIndex = parseInt($wb_card.parent().attr('data-index')), isFindCurrentCard = false, $nextCard;
  1667. var cardList = resolver.findFeedCardItemList()
  1668. $.each(cardList, function(i , $card) {
  1669. if (isFindCurrentCard) {
  1670. $nextCard = $card;
  1671. return false;
  1672. }
  1673. if (parseInt($card.parent().attr('data-index')) == currentCardIndex) {
  1674. isFindCurrentCard = true;
  1675. }
  1676. });
  1677. downloadQueue.append($nextCard);
  1678. }, 500);
  1679. }).fail(function() {
  1680. $notify_start.hide().remove();
  1681. if (count > 0) {
  1682. toastr.success(`本页${bacthName}完成`);
  1683. if (failPhotoCount > 0) {
  1684. toastr.error(`共 ${failPhotoCount} 张图片或视频${bacthName}失败~<br>链接已记录在photos_fail_list.txt`);
  1685. }
  1686. } else {
  1687. toastr.error('本页没有微博');
  1688. }
  1689. });
  1690. });
  1691. $firstCard = $firstCard || resolver.findFeedCardItemList()[0];
  1692. downloadQueue.append($firstCard);
  1693. },
  1694. "batchBackFavWeiboCard": function() {
  1695. const resolver = this;
  1696. let fav_backup_group = GM_getValue(Constants.KEY_FAV_BACKUP_GROUP, {});
  1697. resolver.batchHandleWeiboCard("批量收藏", 20, function($wb_card, dfd) {
  1698. if (!resolver.isWeiboDelete($wb_card, true)) {
  1699. try {
  1700. let mid = resolver.findWeiboCardMid($wb_card, false), card = fav_backup_group[String(mid)]
  1701. if (card) {
  1702. dfd.resolve();
  1703. } else {
  1704. resolver.addFavWeiboBackup($wb_card, function () {
  1705. dfd.resolve();
  1706. });
  1707. }
  1708. } catch (e) {
  1709. let herf = $wb_card.find('> div > header > .woo-box-item-flex > div > div.woo-box-flex.woo-box-alignCenter.woo-box-justifyCenter > a').attr("href");
  1710. toastr.error(`此微博备份失败 <a href="${herf}" target="_blank">${herf}</a>`, "", {
  1711. "progressBar": false,
  1712. "hideDuration": 0,
  1713. "showDuration": 0,
  1714. "timeOut": 0,
  1715. "closeButton": false
  1716. });
  1717. dfd.resolve();
  1718. }
  1719. } else {
  1720. dfd.resolve();
  1721. }
  1722. });
  1723. }
  1724. };
  1725.  
  1726. Interface.impl($.extend(NewWeiboResolver,OldWeiboResolver), WeiboResolver, {
  1727. "getResolverName": function() {
  1728. return "NewWeiboResolver"
  1729. },
  1730. "addDownloadBtnToWeiboCard": function ($wb_card) {
  1731. setTimeout(function () {
  1732. var $card_btn_list = $wb_card.find('.woo-pop-wrap .woo-pop-wrap-main');
  1733. if ($card_btn_list.find(".WB_card_photos_download").length == 0) {
  1734. $card_btn_list.append('<li class="woo-box-flex woo-box-alignCenter woo-pop-item-main WB_card_photos_download" title="下载文件和链接"><a>打包下载</a></li>');
  1735. $card_btn_list.append('<li class="woo-box-flex woo-box-alignCenter woo-pop-item-main WB_card_photos_download WB_card_photos_download_only_download_url" title="仅下载链接,不下载文件"><a>下载链接</a></li>');
  1736. $card_btn_list.append('<li class="woo-box-flex woo-box-alignCenter woo-pop-item-main WB_card_photos_download WB_card_photos_download_only_print_url" title="仅打印出链接"><a>打印链接</a></li>');
  1737. $card_btn_list.append('<li class="woo-box-flex woo-box-alignCenter woo-pop-item-main WB_card_photos_show_fav_weibo_backup" title="查看当前微博是否有备份"><a>显示备份</a></li>');
  1738. $card_btn_list.append('<li class="woo-box-flex woo-box-alignCenter woo-pop-item-main WB_card_photos_rebuild_fav_weibo_backup" title="重新备份当前微博"><a>重新备份</a></li>');
  1739. }
  1740. $card_btn_list.children().each(function (i, li) {
  1741. if (li.innerText == '收藏' || li.innerText == '取消收藏') {
  1742. $(li).addClass('fav_btn');
  1743. }
  1744. });
  1745. }, 500);
  1746. if (!$wb_card.data('weibo') || ($wb_card.data('weibo').mblogid != this.findCardMblogId($wb_card))) {
  1747. var mblogid = this.findCardMblogId($wb_card);
  1748. $.get('https://' + document.location.host + '/ajax/statuses/show?id=' + mblogid, function (resp) {
  1749. $wb_card.data('weibo', resp);
  1750. });
  1751. }
  1752. },
  1753. "showPhotoLinksPopPanel": function ($wb_card, options, removeOnClickBody) {
  1754. const resolver = this;
  1755. options.names || (options.names = {});
  1756. options.callback || (options.callback = {});
  1757. const $pop = $('<div class="W_layer W_layer_pop download-link-pop"><div class="content link-list">' +
  1758. '<div class="sidebar"><span class="sidebar-btn download-all-link" title="全部打包下载">打包下载</span><span class="sidebar-btn copy-all-link" title="复制所有链接">全部复制</span></div>' +
  1759. '<ul></ul></div><div class="content preview"><img></div></div>'),
  1760. $link_ul = $pop.find('.link-list ul'),
  1761. $preview_img = $pop.find('.preview img'),
  1762. photos = options.files, card = options.names.card || options.names.forwardCard || {};
  1763. removeOnClickBody = removeOnClickBody !== false;
  1764. $wb_card.append($pop);
  1765. if (resolver.getResolverName() === 'NewWeiboResolver') {
  1766. if (/(?:www\.)?weibo\.com\/(u\/page\/(fav|like)\/)|(u\/\d+)|(\d+\/\w+)/.test(document.location.href)) {
  1767. $pop.find('.preview').attr('style', 'position: absolute;width: 70%;right: 100%;top: 0%;')
  1768. .find('img').attr('style', 'border: 3px solid #f1912d3b;border-radius: 3px;');
  1769. } else {
  1770. $pop.find('.sidebar')[0].style.left= '-67px';
  1771. $pop.find('.sidebar')[0].style.top= '0px';
  1772. $pop.find('.sidebar')[0].style.borderLeftWidth= '1px';
  1773. $pop.find('.sidebar')[0].style.height= 'min-content';
  1774. }
  1775. var $scroller = $pop.closest('.vue-recycle-scroller__item-view');
  1776. if ($scroller.length > 0) {
  1777. $scroller[0].style.zIndex = "10001";
  1778. $scroller.siblings().each(function (i, scroller) {
  1779. if (scroller.style.zIndex == "10001") {
  1780. scroller.style.zIndex = null;
  1781. }
  1782. });
  1783. }
  1784. }
  1785. $.each(photos, function(i, photo) {
  1786. if (photo.location == 'videos' && photo.download_disable) {
  1787. if (photo.short_url) {
  1788. $link_ul.append(`<li>
  1789. <a href="${photo.url}" target="_blank" data-location="${photo.location}" class="clearfix" title="视频文件链接暂时无法备份,请通过视频链接去尝试获取">${photo.url}</a>
  1790. <br><a href="${photo.short_url}" target="_blank" data-location="${photo.location}" class="clearfix" title="视频文件链接暂时无法备份,请通过视频链接去尝试获取">( ${photo.short_url} )</a>
  1791. </li>`);
  1792. } else {
  1793. $link_ul.append(`<li><a href="${photo.url}" target="_blank" data-location="${photo.location}" class="clearfix" title="视频文件链接暂时无法备份,请通过视频链接去尝试获取">${photo.fileName}</a></li>`);
  1794. }
  1795. } else {
  1796. $link_ul.append(`<li><a href="${photo.url}" target="_blank" download="${photo.fileName}" data-location="${photo.location}" class="clearfix" title="${photo.location == 'photos' ? '点击下载,右键链接另存为' : '右键链接另存为'}">${photo.url}</a></li>`);
  1797. }
  1798. });
  1799. $link_ul.on({
  1800. 'mouseenter': function() {
  1801. let $self = $(this);
  1802. if ($self.attr('data-location') == 'photos') {
  1803. $preview_img.attr('src', $self.attr('href').replace('/large/', '/mw690/')).parent().show(); //css('visibility', 'visible');
  1804. }
  1805. },
  1806. 'mouseleave': function() {
  1807. $preview_img.attr('src', '').parent().hide(); //css('visibility', 'hidden');
  1808. },
  1809. 'click': function(e) {
  1810. let $self = $(this), url = $self.attr('href'), fileName = $self.attr('download');
  1811. if ($self.attr('data-location') == 'photos') {
  1812. let notify_download_media = toastr.success('正在下载图片~', '', {
  1813. "progressBar": false,
  1814. "hideDuration": 0,
  1815. "showDuration": 0,
  1816. "timeOut": 0,
  1817. "closeButton": false,
  1818. });
  1819. // GM_download({'url': url, 'name': fileName, 'saveAs': true});
  1820. common_utils.ajaxDownload(url, function (blob) {
  1821. if (blob) {
  1822. if (blob.type === 'image/gif' && fileName.indexOf('.gif') === -1) {
  1823. fileName = fileName.replace(/\.[^.]+$/, '.gif');
  1824. }
  1825. common_utils.downloadBlobFile(blob, fileName);
  1826. } else {
  1827. toastr.error('请手动打开链接下载', '下载失败');
  1828. }
  1829. notify_download_media.css("display", "none").remove();
  1830. });
  1831. // e.stopImmediatePropagation();
  1832. return false;
  1833. }
  1834. }
  1835. }, 'li a');
  1836. $pop.on('click', '.copy-all-link', function() {
  1837. GM_setClipboard($link_ul[0].innerText);
  1838. toastr.success('复制全部链接成功');
  1839. }).on('click', '.download-all-link', function() {
  1840. options.isNeedConfirmDownload = true;
  1841. options.only_download_url = false;
  1842. options.only_print_url = false;
  1843. // if (options.files && !options.callback.parseFiles_callback) {
  1844. // options.callback.parseFiles_callback = function () {
  1845. // return options.files;
  1846. // }
  1847. // }
  1848. resolver.downloadWeiboCardPhotos($wb_card, options);
  1849. });
  1850. if (removeOnClickBody) {
  1851. function remove(ev) {
  1852. if(!ev.target.classList.contains('woo-font--angleDown') && !ev.target.classList.contains('download-temp-node') && !$pop[0].contains(ev.target)){
  1853. var $scroller = $pop.closest('.vue-recycle-scroller__item-view');
  1854. if ($scroller.length > 0) {
  1855. $scroller[0].style.zIndex = null;
  1856. }
  1857. $pop.remove();
  1858. $('body').off("click", remove);
  1859. }
  1860. }
  1861. $('body').on("click", remove);
  1862. }
  1863. console.log('\n--★-- print -- ' + (options.names.folderName || card.name || (card.text ? card.text.substr(0, 15) : card.mid) || '照片链接') + ' ----★--');
  1864. console.table(JSON.parse(JSON.stringify(photos)), ['location', 'url']);
  1865. console.log('当url被省略可以复制下面的链接,也可从上面 >Array(' + photos.length + ') 查看');
  1866. $.each(photos, function (i, photo) {
  1867. console.log(photo.url);
  1868. });
  1869. return $pop;
  1870. }, //
  1871. "findWeiboCardMid": function ($wb_card, findForwardIfHas) {
  1872. const resolver = this;
  1873. let defaultData = resolver.findCardDefaultData($wb_card), weiboData;
  1874. if (defaultData) {
  1875. weiboData = defaultData;
  1876. } else {
  1877. weiboData = $wb_card.data('weibo')
  1878. }
  1879. let mid, isForward = weiboData.retweeted_status ? true : false; // 是否是转发
  1880. if (isForward && findForwardIfHas) {
  1881. mid = weiboData.retweeted_status.mid || weiboData.mid;
  1882. } else {
  1883. mid = weiboData.mid
  1884. }
  1885. return mid;
  1886. }, //
  1887. "isWeiboDelete": function ($wb_card, findForwardIfHas) {
  1888. const resolver = this;
  1889. let defaultData = resolver.findCardDefaultData($wb_card), weiboData;
  1890. if (defaultData) {
  1891. weiboData = defaultData;
  1892. } else {
  1893. weiboData = $wb_card.data('weibo');
  1894. }
  1895. if (weiboData) {
  1896. let isForward = weiboData.retweeted_status ? true : false; // 是否是转发
  1897. if (isForward && weiboData.retweeted_status.deleted == '1') {
  1898. return true;
  1899. } else if (weiboData['deleted'] == '1') {
  1900. return true;
  1901. }
  1902. return false;
  1903. } else {
  1904. return $wb_card.find('header').length === 0 || $wb_card.hasClass('Fav-Panel-Card');
  1905. }
  1906. }, //
  1907. "downloadWeiboCardPhotos": function (wb_card_node, options) {
  1908. const resolver = this;
  1909. var $wb_card = (wb_card_node instanceof $) ? wb_card_node : $(wb_card_node);
  1910. var config = {
  1911. "$wb_card": $wb_card,
  1912. "type": 2,
  1913. "isNeedConfirmDownload": true, // 下载前是否需要弹出确认框
  1914. "isNeedHasFiles": false,
  1915. "useQueueDownloadThreshold": 0,
  1916. "only_download_url": false, // 是否仅下载链接,true: 只下链接,false:下载文件和链接
  1917. "only_print_url": false, // 是否仅打印出链接
  1918. "sortByOrigin": true, // 照片按原页面显示顺序排序
  1919. "suffix": null,
  1920. "callback": {
  1921. "parseFiles_callback": function (location_info, options) {
  1922. // var $author_head = $wb_card.find('> div > header > .woo-box-item-flex > div > div.woo-box-flex.woo-box-alignCenter.woo-box-justifyCenter > a');
  1923. // var mblogid = $author_head.attr("href").replace(/\?[^?]*$/,'').match(/\/([a-zA-Z0-9]+)$/) && RegExp.$1;
  1924. var mblogid = resolver.findCardMblogId($wb_card);
  1925. var photo_parse_index = 0;
  1926. var video_parse_index = 0;
  1927. var photo_arr = [];
  1928. var buildPhoto = function (photo_id, photo_url, photo_video_id) {
  1929. var photo = {};
  1930. photo.photo_id = photo_id;
  1931. photo.url = photo_url;
  1932. photo.folder_sort_index = ++photo_parse_index;
  1933. photo.location = "photos";
  1934. photo_arr.push(photo);
  1935. if (photo_video_id) {
  1936. var video = {};
  1937. video.video_id = photo_video_id;
  1938. video.url = "https://video.weibo.com/media/play?livephoto=//us.sinaimg.cn/" + photo_video_id + ".mov&KID=unistore,videomovSrc";
  1939. video.fileName = photo_video_id + ".mov";
  1940. video.folder_sort_index = ++video_parse_index;
  1941. video.location = "videos";
  1942. photo_arr.push(video);
  1943. }
  1944. };
  1945. var buildVideo = function (video_id, video_url, short_url, video_cover_url) {
  1946. var video = {};
  1947. video.video_id = video_id;
  1948. video.url = video_url;
  1949. video.short_url = short_url;
  1950. video.fileName = video.url.match(/\/([^/?]+?(\.mp4)?)\?/)[1] + (RegExp.$2 ? "" : ".mp4");
  1951. video.folder_sort_index = ++video_parse_index;
  1952. video.location = "videos";
  1953. photo_arr.push(video);
  1954. var photo = {};
  1955. video_cover_url.match(/\/(([^\/]+)\.[a-zA-Z0-9]+)$/);
  1956. photo.photo_id = RegExp.$2;
  1957. photo.url = 'https://wx2.sinaimg.cn/large/' + RegExp.$1;
  1958. photo.folder_sort_index = ++photo_parse_index;
  1959. photo.location = "photos";
  1960. photo_arr.push(photo);
  1961. }
  1962. var parsePhotosFromIds = function (weibo, dfd) {
  1963. // 用户发表微博引用了别的用户的视频会有这种情况
  1964. if (weibo.page_info && weibo.retweeted_status && !weibo.retweeted_status.page_info) {
  1965. weibo.retweeted_status.page_info = weibo.page_info;
  1966. }
  1967. weibo = weibo.retweeted_status || weibo;
  1968. weibo.pic_infos && $.each(weibo.pic_infos, function(id, p) {
  1969. buildPhoto(id, p.largest.url, p.type == "livephoto" ? p.fid : null);
  1970. });
  1971. if (weibo.page_info && weibo.page_info.object_type=='video') {
  1972. weibo.page_info.media_info.playback_list.sort(function (a, b) {
  1973. return a.meta.quality_index > b.meta.quality_index ? -1 : 1;
  1974. });
  1975. buildVideo(
  1976. weibo.page_info.object_id,
  1977. weibo.page_info.media_info.playback_list[0].play_info.url,
  1978. weibo.page_info.short_url,
  1979. weibo.page_info.page_pic
  1980. );
  1981. }
  1982. if (weibo.mix_media_info && weibo.mix_media_info.items) {
  1983. const videoIds = [], videoDfds = [];
  1984. function loadVideoDetail (item) {
  1985. return $.Deferred(function (dfd) {
  1986. if (item.type === 'video') {
  1987. videoIds.push(item.id);
  1988. $.ajax({
  1989. type: 'POST',
  1990. url: 'https://' + document.location.host + '/tv/api/component?page=' + encodeURIComponent('/tv/show/' + item.id),
  1991. beforeSend: function (XMLHttpRequest) {
  1992. XMLHttpRequest.setRequestHeader("content-type", 'application/x-www-form-urlencoded');
  1993. XMLHttpRequest.setRequestHeader("page-referer", "/tv/show/" + item.id);
  1994. },
  1995. // headers: {
  1996. // 'content-type': 'application/x-www-form-urlencoded',
  1997. // 'page-referer': "/tv/show/" + item.id,
  1998. // },
  1999. // dataType: 'json',
  2000. contentType: 'application/json',
  2001. data: 'data=' + JSON.stringify({ "Component_Play_Playinfo": { "oid": item.id } }),
  2002. success: function (resp) {
  2003. var video = {}, photo = {};
  2004. if (resp.code = "100000") {
  2005. const data = resp.data.Component_Play_Playinfo, videoUrls = [];
  2006. $.each(data.urls, function (k, v) {
  2007. videoUrls.push({
  2008. "k": k,
  2009. "v": v
  2010. });
  2011. });
  2012. videoUrls.sort(function (l, r) {
  2013. let l_level = parseInt(l.k.match(/\s(\d+)([PK])/)[1]);
  2014. let r_level = parseInt(r.k.match(/\s(\d+)([PK])/)[1]);
  2015. l_level == 2 && (l_level = 1440);
  2016. r_level == 2 && (r_level = 1440);
  2017. l_level == 4 && (l_level = 2560);
  2018. r_level == 4 && (r_level = 2560);
  2019. return l_level > r_level ? -1 : 1;
  2020. });
  2021. let videoUrl = videoUrls[0].v;
  2022. /^https?:/.test(videoUrl) || (videoUrl = 'https:' + videoUrl);
  2023. buildVideo(
  2024. item.id,
  2025. videoUrl,
  2026. data.url_short,
  2027. data.cover_image
  2028. );
  2029. } else {
  2030. buildVideo(
  2031. item.id,
  2032. item.media_info.mp4_720p_mp4 || item.media_info.mp4_hd_url || item.media_info.mp4_sd_url,
  2033. item.data.short_url,
  2034. item.data.page_pic
  2035. );
  2036. }
  2037. dfd.resolve();
  2038. }
  2039. });
  2040. }
  2041. });
  2042. }
  2043. for (let item of weibo.mix_media_info.items) {
  2044. if (item.type === 'video') {
  2045. videoDfds.push(loadVideoDetail(item));
  2046. }
  2047. }
  2048. for (let item of weibo.mix_media_info.items) {
  2049. if (item.type === 'pic') {
  2050. buildPhoto(
  2051. item.id,
  2052. item.data.largest.url,
  2053. item.data.type == 'livephoto' ? item.data.fid : null
  2054. );
  2055. }
  2056. }
  2057. $.when.apply(this, videoDfds).done(function () {
  2058. dfd.resolve(photo_arr);
  2059. });
  2060. } else {
  2061. dfd.resolve(photo_arr);
  2062. }
  2063. };
  2064. return $.Deferred(function (dfd) {
  2065. let weibo = $wb_card.data('weibo');
  2066. if (weibo && ($wb_card.data('weibo').mblogid == resolver.findCardMblogId($wb_card))) {
  2067. parsePhotosFromIds(weibo, dfd);
  2068. } else {
  2069. $.get('https://' + document.location.host + '/ajax/statuses/show?id=' + mblogid, function (resp) {
  2070. $wb_card.data('weibo', resp);
  2071. parsePhotosFromIds(resp, dfd);
  2072. });
  2073. }
  2074. });
  2075. },
  2076. "makeNames_callback": function (photos, location_info, options) {
  2077. var names = {},
  2078. weibo = $wb_card.data('weibo'),
  2079. isForward = weibo.retweeted_status ? true : false; // 是否是转发
  2080. names.infoName = "card_info.txt";
  2081. names.infoValue = "";
  2082. var printCardInfoToText = function (card) {
  2083. let infoValue = '', isForward = card.forward === true;
  2084. infoValue += "-----------" + (isForward ? "forward card" : "card") + "--------------" + "\r\n";
  2085. $.each(card, function (key, value) {
  2086. if (key !== 'user' && key !== 'photos' && key !== 'videos' && key !== 'rootCard') {
  2087. infoValue += (isForward ? "forward_" : "") + "card_" + key + ":" + value + "\r\n";
  2088. }
  2089. });
  2090. infoValue += "-----------------------------------" + "\r\n";
  2091. $.each(card.user, function (key, value) {
  2092. infoValue += (isForward ? "forward_" : "") + "user_" + key + ":" + value + "\r\n";
  2093. });
  2094. infoValue += "-----------------------------------" + "\r\n";
  2095. return infoValue;
  2096. }
  2097. var findCardInfo = function (weibo, isForward) {
  2098. var user = {};
  2099. user.uid = weibo.user.id;
  2100. user.nickname = weibo.user.screen_name;
  2101. user.home_link = 'https://www.weibo.com' + weibo.user.profile_url;
  2102. var card = {};
  2103. card.forward = isForward;
  2104. card.link = `https://weibo.com/${weibo.user.id}/${weibo.mblogid}`;
  2105. card.id = weibo.mblogid;
  2106. card.mid = weibo.mid;
  2107. card.date = resolver.formatWeiboDate(weibo.created_at);
  2108. card.date_timestamp = weibo.created_at;
  2109. card.fav_date = resolver.formatWeiboDate();
  2110. card.text = weibo.text;
  2111. card.name = resolver.getNameFromText(card.text) || card.mid;
  2112. card.photo_count = photos.length;
  2113. card.user = user;
  2114. card.photos = photos;
  2115. return card;
  2116. };
  2117. names.card = findCardInfo(weibo, false); // 主贴的信息
  2118. names.infoValue += printCardInfoToText(names.card);
  2119. if (isForward) {
  2120. // 转发的贴的信息
  2121. names.forwardCard = findCardInfo(weibo.retweeted_status, true);
  2122. names.infoValue += printCardInfoToText(names.forwardCard);
  2123. }
  2124. names.zipName = names.card.user.nickname + "_" + names.card.user.uid + "_" + names.card.id + "_" + (names.card.name
  2125. .replace(/\.\./g, "")
  2126. .replace(/[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/ig, "").replace(/[\u200b]+$/, "")
  2127. .replace(/(^[_-]+)|([_-]+$)/g, "")
  2128. .replace(/(^\s+)|(\s+$)/g, ""));
  2129. names.folderName = names.zipName;
  2130. names.prefix = null;
  2131. names.suffix = options.suffix;
  2132. return names;
  2133. },
  2134. "beforeFilesDownload_callback": function (photos, names, location_info, options, zip, main_folder) {
  2135. const paddingZeroLength = String(photos.length).length,
  2136. card = (names.forwardCard || names.card),
  2137. sortByOrigin = options.sortByOrigin;
  2138. $.each(photos, function (i, photo) {
  2139. if (!photo.fileName) {
  2140. photo.fileName = photo.url.substring(photo.url.lastIndexOf('/') + 1);
  2141. }
  2142. if (sortByOrigin) {
  2143. photo.originName = photo.fileName;
  2144. photo.fileName = card.user.uid + '_' + card.id + '_' + common_utils.paddingZero(photo.folder_sort_index, paddingZeroLength) + '_' + photo.originName;
  2145. }
  2146. });
  2147. options.failFiles = undefined;
  2148. },
  2149. "beforeFileDownload_callback": function (photo, location_info, options, zipFileLength, zip, main_folder, folder) {
  2150. if (options.only_download_url || options.only_print_url) {
  2151. return false;
  2152. } else {
  2153. return true;
  2154. }
  2155. },
  2156. "eachFileOnload_callback": function (blob, photo, location_info, options, zipFileLength, zip, main_folder, folder) {
  2157. if (blob == null) {
  2158. if (!options.failFiles) {
  2159. options.failFiles = [];
  2160. }
  2161. options.failFiles.push(photo);
  2162. } else if (photo.location == 'photos' && blob.type === 'image/gif' && photo.fileName.indexOf('.gif') === -1) {
  2163. // 如果在超过9张图(over9pic)中含有gif,那么后缀需要根据content-type来判断
  2164. let suffixRegex = /\.[^.]+$/, suffix = '.gif';
  2165. photo.fileName = photo.fileName.replace(suffixRegex, suffix);
  2166. photo.url = photo.url.replace(suffixRegex, suffix);
  2167. photo.originName && (photo.originName = photo.originName.replace(suffixRegex, suffix));
  2168. }
  2169. return true;
  2170. },
  2171. "allFilesOnload_callback": function (photos, names, location_info, options, zip, main_folder) {
  2172. let photo_urls_str = "", failPhotoListStr = "", photo_url, failFile;
  2173. // 链接列表文件
  2174. $.each(photos, function (i, photo) {
  2175. if (photo.location == 'videos' && photo.url.indexOf('f.video.weibocdn.com') != -1 && !options.only_download_url && !/\.mov$/.test(photo.fileName)) {
  2176. // photo_url = 'http://f.video.weibocdn.com/' + (photo.originName || photo.fileName) + '?KID=unistore,video';
  2177. photo_url = 'https://video.weibo.com/show?fid=' + photo.video_id + (photo.short_url ? '(' + photo.short_url + ')' : '');
  2178. } else {
  2179. photo_url = photo.url;
  2180. }
  2181. let line = ((photo.location ? (photo.location + "/") : "" ) + photo.fileName) + "\t" + photo_url + "\r\n";
  2182. photo_urls_str += line;
  2183. });
  2184. main_folder.file("photo_url_list.txt", photo_urls_str);
  2185. // 失败链接列表文件
  2186. if (options.failFiles && options.failFiles.length > 0) {
  2187. toastr.error("共 " + options.failFiles.length + " 张下载失败,已记录在photos_fail_list.txt!", "", {
  2188. "progressBar": false,
  2189. timeOut: 0
  2190. });
  2191. for (let i in options.failFiles) {
  2192. failFile = options.failFiles[i];
  2193. failPhotoListStr += (failFile.location + "/" + failFile.fileName + "\t" + failFile.url + "\r\n");
  2194. }
  2195. main_folder.file("photos_fail_list.txt", failPhotoListStr);
  2196. }
  2197. // 如果只是打印链接
  2198. if (options.only_print_url) {
  2199. resolver.showPhotoLinksPopPanel($wb_card, options);
  2200. toastr.success("已打印");
  2201. return false;
  2202. }
  2203. }
  2204. }
  2205. };
  2206. if (options) {
  2207. $.extend(true, config, options);
  2208. }
  2209. batchDownload(config);
  2210. },
  2211. "addFavWeiboBackup": function ($wb_card, callback) { // 保存备份
  2212. const resolver = this;
  2213. var options = {
  2214. "only_print_url": true,
  2215. "isNeedConfirmDownload": false,
  2216. "callback": {
  2217. "allFilesOnload_callback": function (photos, names, location_info, options, zip, main_folder) {
  2218. let fav_backup_group, beforeSaveCard, saveCard = {}, rootCard = {}, user = {}, picNames = [], livePhotos = [], videos = [], card = names.forwardCard || names.card;
  2219. if (!card.mid) {
  2220. toastr.error('未找到mid,代码需要改进');
  2221. return false;
  2222. }
  2223. fav_backup_group = GM_getValue(Constants.KEY_FAV_BACKUP_GROUP, {});
  2224. saveCard.id = card.id;
  2225. saveCard.mid = card.mid;
  2226. saveCard.date = resolver.formatWeiboDate(card.date);
  2227. saveCard.fav_date = card.fav_date || saveCard.date;
  2228. saveCard.text = card.text;
  2229. saveCard.forward = card.forward;
  2230. card.countdown_page !== undefined && (saveCard.countdown_page = card.countdown_page);
  2231. user.uid = card.user.uid;
  2232. user.nickname = card.user.nickname;
  2233. $.each(photos, function (i, photo) {
  2234. let originName = photo.originName || photo.fileName;
  2235. if (photo.location == 'photos') {
  2236. picNames.push(originName);
  2237. } else if (photo.location == 'videos') {
  2238. if (/\.mov$/.test(originName)) {
  2239. livePhotos.push(originName);
  2240. } else if (photo.url.indexOf('f.video.weibocdn.com') != -1) { // 先存起来,可能以后有新办法
  2241. videos.push(photo.video_id + '@' + photo.short_url);
  2242. }
  2243. }
  2244. });
  2245. saveCard.user = user;
  2246. saveCard.photos = picNames;
  2247. saveCard.livePhotos = livePhotos;
  2248. saveCard.videos = videos;
  2249. if (names.forwardCard) {
  2250. beforeSaveCard = fav_backup_group[String(saveCard.mid)];
  2251. // 如果被转发的微博执行了明确收藏操作就不覆盖该forward值
  2252. if (saveCard.forward === true && beforeSaveCard && beforeSaveCard.forward === false) {
  2253. saveCard.forward = false;
  2254. }
  2255. rootCard.root = true;
  2256. rootCard.id = names.card.id;
  2257. rootCard.mid = names.card.mid;
  2258. rootCard.date = resolver.formatWeiboDate(names.card.date);
  2259. rootCard.fav_date = names.card.fav_date || rootCard.date;
  2260. rootCard.text = names.card.text;
  2261. rootCard.forward = names.forwardCard.mid;
  2262. rootCard.user = {
  2263. uid: names.card.user.uid,
  2264. nickname: names.card.user.nickname,
  2265. }
  2266. }
  2267. fav_backup_group[String(saveCard.mid)] = saveCard;
  2268. if (names.forwardCard) {
  2269. fav_backup_group[String(rootCard.mid)] = rootCard;
  2270. }
  2271. GM_setValue(Constants.KEY_FAV_BACKUP_GROUP, fav_backup_group);
  2272. callback || toastr.success("收藏备份到本地成功~");
  2273. callback && callback.call(this, saveCard);
  2274. return false;
  2275. },
  2276. }
  2277. };
  2278. resolver.downloadWeiboCardPhotos($wb_card, options);
  2279. },
  2280. "getFavWeiboBackup": function (mid) { // 获取备份
  2281. const resolver = this;
  2282. if (!mid) {
  2283. toastr.error('未找到mid,代码需要改进');
  2284. return;
  2285. }
  2286. let fav_backup_group = GM_getValue(Constants.KEY_FAV_BACKUP_GROUP, {}), card = fav_backup_group[String(mid)], rootCard, photos = [], photo_parse_index = 0, video_parse_index = 0;
  2287. if (card) {
  2288. if (card.root) { // 如果传入的mid只是一个转发mid
  2289. rootCard = card;
  2290. card = fav_backup_group[String(rootCard.forward)]; // 获取实际存储数据的mid
  2291. if (card) {
  2292. card.forward = true;
  2293. card.rootCard = rootCard;
  2294. if (!rootCard.name) {
  2295. rootCard.name = resolver.getNameFromText(rootCard.text) || rootCard.mid;
  2296. }
  2297. } else {
  2298. card = rootCard;
  2299. }
  2300. }
  2301. card.photos && $.each(card.photos, function(i, fileName) {
  2302. let photo = {url: 'https://wx3.sinaimg.cn/large/' + fileName, fileName: fileName, location: 'photos', folder_sort_index: ++photo_parse_index};
  2303. photos.push(photo);
  2304. });
  2305. card.livePhotos && $.each(card.livePhotos, function(i, fileName) {
  2306. let photo = {url: 'https://video.weibo.com/media/play?livephoto=//us.sinaimg.cn/' + fileName + '&KID=unistore,videomovSrc', fileName: fileName, location: 'videos', folder_sort_index: ++video_parse_index};
  2307. photos.push(photo);
  2308. });
  2309. // card.videos && $.each(card.videos, function(i, fileName) {
  2310. // let photo = {url: 'http://f.video.weibocdn.com/' + fileName + '?KID=unistore,video', fileName: fileName, location: 'videos', folder_sort_index: ++video_parse_index};
  2311. // photos.push(photo);
  2312. // });
  2313. card.videos && $.each(card.videos, function(i, fileName) {
  2314. if (fileName.indexOf(':') !== -1) {
  2315. let urls = (fileName.indexOf('@') !== -1 ? fileName : (fileName + '@')).split('@');
  2316. let title = 'https://video.weibo.com/show?fid=' + urls[0] + (urls[1] ? '<br>( ' + urls[1] + ' )' : '');
  2317. let photo = {url: 'https://video.weibo.com/show?fid=' + urls[0], short_url: urls[1], fileName: title, location: 'videos', folder_sort_index: ++video_parse_index, 'download_disable': true};
  2318. photos.push(photo);
  2319. }
  2320. });
  2321. card.photos = photos;
  2322. delete card.livePhotos;
  2323. delete card.videos;
  2324. if (!card.name) {
  2325. card.name = resolver.getNameFromText(card.text) || card.mid;
  2326. }
  2327. card.date = resolver.formatWeiboDate(card.date);
  2328. card.fav_date = card.fav_date || card.date;
  2329. }
  2330. return card;
  2331. },
  2332. "removeFavWeiboBackup": function (mid, cancleIfNotForward) { // 移除备份
  2333. const resolver = this;
  2334. if (!mid) {
  2335. toastr.error('未找到mid,代码需要改进');
  2336. return 400;
  2337. }
  2338. let fav_backup_group = GM_getValue(Constants.KEY_FAV_BACKUP_GROUP, {}), card = fav_backup_group[String(mid)], forwardCard;
  2339. if (card) {
  2340. if (card.root) { // 如果传入的mid只是一个转发mid
  2341. forwardCard = fav_backup_group[String(card.forward)];
  2342. if (forwardCard && forwardCard.forward === true) { // 如果实际存储数据的mid没有明确收藏操作,则一起删除
  2343. delete fav_backup_group[String(forwardCard.mid)];
  2344. }
  2345. }
  2346. if (card.root || cancleIfNotForward !== true || card.forward === true) {
  2347. delete fav_backup_group[String(card.mid)];
  2348. GM_setValue(Constants.KEY_FAV_BACKUP_GROUP, fav_backup_group);
  2349. toastr.success("删除收藏本地备份成功~");
  2350. }
  2351. return 200;
  2352. } else {
  2353. return 404;
  2354. }
  2355. },
  2356. "generateBatchDownloadOptionsFromBackup": function ($wb_card, card) {
  2357. const resolver = this;
  2358. let options = {
  2359. files: card.photos,
  2360. }
  2361. if (resolver.isWeiboDelete($wb_card, true)) {
  2362. options.callback = {
  2363. 'parseFiles_callback': function () {
  2364. return card.photos;
  2365. },
  2366. 'makeNames_callback': function (files, location_info, options) {
  2367. var names = {};
  2368. names.infoName = "card_info.txt";
  2369. names.infoValue = "";
  2370. var printCardInfoToText = function (card) {
  2371. let infoValue = '', isForward = card.forward === true;
  2372. infoValue += "-----------" + (isForward ? "forward card" : "card") + "--------------" + "\r\n";
  2373. $.each(card, function (key, value) {
  2374. if (key !== 'user' && key !== 'photos' && key !== 'videos' && key !== 'rootCard') {
  2375. infoValue += (isForward ? "forward_" : "") + "card_" + key + ":" + value + "\r\n";
  2376. }
  2377. });
  2378. infoValue += "-----------------------------------" + "\r\n";
  2379. $.each(card.user, function (key, value) {
  2380. infoValue += (isForward ? "forward_" : "") + "user_" + key + ":" + value + "\r\n";
  2381. });
  2382. infoValue += "-----------------------------------" + "\r\n";
  2383. return infoValue;
  2384. }
  2385. card.link = 'https://weibo.com/' + card.user.uid + '/' + card.id;
  2386. card.user.home_link = 'https://weibo.com/u/' + card.user.uid;
  2387. if (card.rootCard) {
  2388. card.rootCard.link = 'https://weibo.com/' + card.rootCard.user.uid + '/' + card.rootCard.id;
  2389. card.rootCard.user.home_link = 'https://weibo.com/u/' + card.rootCard.user.uid;
  2390. names.card = card.rootCard;
  2391. names.forwardCard = card;
  2392. } else {
  2393. names.card = card;
  2394. }
  2395. // 主贴的信息
  2396. names.infoValue += printCardInfoToText(names.card);
  2397. if (names.forwardCard) { // 被转发贴的信息
  2398. names.infoValue += printCardInfoToText(names.forwardCard);
  2399. }
  2400. names.zipName = names.card.user.nickname + "_" + names.card.user.uid + "_" + names.card.id + "_" + (names.card.name
  2401. .replace(/\.\./g, "") .replace(/[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/ig, "").replace(/[\u200b]+$/, "")
  2402. .replace(/(^[_-]+)|([_-]+$)/g, "")
  2403. .replace(/(^\s+)|(\s+$)/g, ""));
  2404. names.folderName = names.zipName;
  2405. names.prefix = null;
  2406. names.suffix = options.suffix;
  2407. return names;
  2408. },
  2409. };
  2410. }
  2411. return options;
  2412. },
  2413. "batchDownloadPageWeiboCard": function() {
  2414. const resolver = this;
  2415. let failPhotoCount = 0;
  2416. resolver.batchHandleWeiboCard("批量下载", 50, function($wb_card, dfd) {
  2417. let options = {};
  2418. if (resolver.isWeiboDelete($wb_card, true)) {
  2419. let mid = resolver.findWeiboCardMid($wb_card, false),
  2420. omid = resolver.findWeiboCardMid($wb_card, true),
  2421. card;
  2422. card = resolver.getFavWeiboBackup(mid) || (mid != omid && resolver.getFavWeiboBackup(omid)) || null;
  2423. if (card) {
  2424. options = resolver.generateBatchDownloadOptionsFromBackup($wb_card, card);
  2425. } else {
  2426. dfd.resolve();
  2427. return;
  2428. }
  2429. }
  2430. $.extend(true, options, {
  2431. isNeedConfirmDownload: false,
  2432. callback:{
  2433. beforeZipFileDownload_callback:function (zip_blob, files, names, location_info, options, zip, main_folder) {
  2434. common_utils.downloadBlobFile(zip_blob, names.zipName + ".zip");
  2435. failPhotoCount += (options.failFiles && options.failFiles.length || 0);
  2436. dfd.resolve();
  2437. }
  2438. }
  2439. });
  2440. resolver.downloadWeiboCardPhotos($wb_card, options);
  2441. });
  2442. },
  2443. "showFavWeiboRestoreBtn": function () {
  2444. // $('.WB_cardwrap:not(.has-set-restore-btn)').find('> .WB_empty, .WB_expand > .WB_empty').find('.WB_innerwrap p')
  2445. // .prepend('<button class="restore-backup-fav-weibo" style="margin-right:15px;cursor:pointer;" title="made by Jeffrey.Deng">查看备份</button>').closest('.WB_cardwrap:not(.has-set-restore-btn)').addClass('has-set-restore-btn');
  2446. const resolver = this;
  2447. resolver.findFeedCardItemList().filter(function(i, $wb_card) {
  2448. // !$wb_card.hasClass('has-set-restore-btn')
  2449. let isDeleted = resolver.isWeiboDelete($wb_card);
  2450. if (!isDeleted) {
  2451. $wb_card.find('> .Fav-Panel-Card, >.restore-fav-pop').remove();
  2452. }
  2453. return isDeleted && $wb_card.find('.restore-backup-fav-weibo').length === 0
  2454. }).each(function(i, $wb_card) {
  2455. $wb_card.find('.wbpro-feed-content .wbpro-feed-ogText > div')
  2456. .prepend('<button class="restore-backup-fav-weibo" style="margin-right: 12px;cursor:pointer;background-color: #3a90c6;border-width: 0.1px;border-radius: 2px;color: #FFF;padding: 2px 3px 2px 3px;" title="made by Jeffrey.Deng">查看备份</button>')
  2457. // .closest('article.woo-panel-main').addClass('has-set-restore-btn');
  2458. $wb_card.find('.restore-backup-fav-weibo').click(resolver.RestoreBackupBtnEventFunc);
  2459. });
  2460. },
  2461. "findFeedCardItemList": function () {
  2462. var cardList = $('#scroller').find('.vue-recycle-scroller__item-wrapper').children().filter(function (i, c) {
  2463. // c.getAttribute('style').indexOf('z-index: -1;') === -1
  2464. return c.firstChild.getAttribute('data-active') === "true"
  2465. }).sort(function(l, r) {
  2466. let $l = $(l).children(0);
  2467. let $r = $(r).children(0);
  2468. return $l.attr('data-index') - $r.attr('data-index') > 0 ? 1 : -1
  2469. }).map(function(i, card) {
  2470. return $(card).find("article.woo-panel-main");
  2471. });
  2472. return cardList;
  2473. },
  2474. "findCardArrowDown": function () {
  2475. return $('article.woo-panel-main .woo-box-flex .woo-font.woo-font--angleDown');
  2476. },
  2477. "findElemClosestCard": function (elem) {
  2478. return $(elem).closest("article.woo-panel-main");
  2479. },
  2480. "findCardPhotoDownloadBtn": function() {
  2481. return $('article.woo-panel-main .woo-pop-wrap .woo-pop-wrap-main .WB_card_photos_download');
  2482. },
  2483. "findDeletedCard": function() {
  2484. return $('.WB_cardwrap:not(.has-set-restore-btn) > .WB_empty, .WB_cardwrap:not(.has-set-restore-btn) .WB_expand > .WB_empty');
  2485. },
  2486. "findCardFavBtn": function() {
  2487. return $('article.woo-panel-main .woo-pop-wrap .woo-pop-wrap-main > .woo-pop-item-main.fav_btn');
  2488. },
  2489. "findCardShowBackupBtn": function() {
  2490. return $('article.woo-panel-main .woo-pop-wrap .woo-pop-wrap-main .WB_card_photos_show_fav_weibo_backup');
  2491. },
  2492. "findCardRebuildBackupBtn": function() {
  2493. return $('article.woo-panel-main .woo-pop-wrap .woo-pop-wrap-main .WB_card_photos_rebuild_fav_weibo_backup');
  2494. },
  2495. "findCardRestoreBackupBtn": function() {
  2496. return $('#scroller .vue-recycle-scroller__item-wrapper article.woo-panel-main .wbpro-feed-ogText .restore-backup-fav-weibo');
  2497. },
  2498. });
  2499.  
  2500. var resolver;
  2501.  
  2502. const core = {
  2503.  
  2504. getWeiBoResolver: function () {
  2505. if ($('body > #app').length > 0) {
  2506. return NewWeiboResolver;
  2507. } else {
  2508. return OldWeiboResolver;
  2509. }
  2510. },
  2511. init: function () {
  2512.  
  2513. unsafeWindow.$ = $;
  2514.  
  2515. resolver = this.getWeiBoResolver();
  2516.  
  2517. unsafeWindow.showFavWeiboRestoreBtn = function() {
  2518. resolver.showFavWeiboRestoreBtn.call(resolver)
  2519. };
  2520.  
  2521. // hack: 仪表盘控制栏添加按钮,暂时不启用
  2522. if (false && document.location.href.match(/^https:\/\/(www\.)?weibo\.com\/(?!u\/)\d{8,}\/(?!profile\/)\w{8,}\?.*$/)) {
  2523. GM_registerMenuCommand('下载', function() {
  2524. resolver.downloadWeiboCardPhotos(resolver.findFeedCardItemList().eq(0));
  2525. }, 'd');
  2526. GM_registerMenuCommand('备份', function() {
  2527. let $wb_card = resolver.findFeedCardItemList().eq(0), mid = resolver.findWeiboCardMid($wb_card, true);
  2528. if (!mid) {
  2529. toastr.error('未找到mid,代码需要改进');
  2530. return;
  2531. };
  2532. if (resolver.isWeiboDelete($wb_card, true) && resolver.getFavWeiboBackup(mid)) {
  2533. if (!unsafeWindow.confirm('原博已被删除,这会删除之前的备份,是否继续?')) {
  2534. return;
  2535. }
  2536. }
  2537. resolver.addFavWeiboBackup($wb_card);
  2538. }, 'b');
  2539. }
  2540.  
  2541. $("body").on("mousedown", resolver.findCardArrowDown().selector, function (e) {
  2542. if (e.data == 'mu') {
  2543. return;
  2544. }
  2545. resolver.addDownloadBtnToWeiboCard(resolver.findElemClosestCard(this));
  2546. });
  2547. $("body").on("click", resolver.findCardPhotoDownloadBtn().selector, function () {
  2548. var $self = $(this);
  2549. var options = {"only_download_url": $self.hasClass('WB_card_photos_download_only_download_url'), "only_print_url": $self.hasClass('WB_card_photos_download_only_print_url')};
  2550. if (options.only_print_url) {
  2551. options.isNeedConfirmDownload = false;
  2552. }
  2553. resolver.downloadWeiboCardPhotos(resolver.findElemClosestCard($self), options);
  2554. if (core.getWeiBoResolver() === NewWeiboResolver) {
  2555. resolver.findElemClosestCard(this).find('.woo-box-flex .woo-font.woo-font--angleDown').trigger('click', 'mu');
  2556. }
  2557. });
  2558.  
  2559. GM_registerMenuCommand('一键下载(用户|收藏)一页的微博', function() {
  2560. resolver.batchDownloadPageWeiboCard();
  2561. });
  2562.  
  2563. // 收藏备份
  2564. let switch_auto_backup_fav_id;
  2565. var getAutoBackUpSetting = function () { // 获取是否开启收藏时自动备份的设置值
  2566. if (GM_getValue(Constants.KEY_SETTING_AUTO_BACKUP_FAV) == undefined) {
  2567. return true;
  2568. }
  2569. return !!GM_getValue(Constants.KEY_SETTING_AUTO_BACKUP_FAV);
  2570. };
  2571. var switchAutoBackUpSetting = function(on) { // 切换收藏时自动备份的设置值
  2572. let saveValue = !!on;
  2573. if (switch_auto_backup_fav_id) {
  2574. GM_unregisterMenuCommand(switch_auto_backup_fav_id);
  2575. }
  2576. if (saveValue) {
  2577. switch_auto_backup_fav_id = GM_registerMenuCommand('开启收藏时确认备份弹窗', function() {
  2578. switchAutoBackUpSetting(false);
  2579. toastr.success("已关闭自动备份~");
  2580. });
  2581. } else {
  2582. switch_auto_backup_fav_id = GM_registerMenuCommand('关闭收藏时确认备份弹窗', function() {
  2583. switchAutoBackUpSetting(true);
  2584. toastr.success("已开启自动备份~");
  2585. });
  2586. }
  2587. if (getAutoBackUpSetting() !== saveValue) {
  2588. GM_setValue(Constants.KEY_SETTING_AUTO_BACKUP_FAV, saveValue);
  2589. }
  2590. };
  2591. if (resolver.getResolverName() === "OldWeiboResolver") {
  2592. $('body').on('click', '.WB_cardwrap .W_pages', function() {
  2593. setTimeout(function() {
  2594. resolver.showFavWeiboRestoreBtn();
  2595. }, 3000);
  2596. setTimeout(function() {
  2597. resolver.showFavWeiboRestoreBtn();
  2598. }, 4500);
  2599. setTimeout(function() {
  2600. resolver.showFavWeiboRestoreBtn();
  2601. }, 6000);
  2602. });
  2603. $('body').on('mouseenter', resolver.findDeletedCard().selector, function() {
  2604. resolver.showFavWeiboRestoreBtn();
  2605. });
  2606. } else {
  2607. $(document).on('scroll', function () {
  2608. if (!/(?:www\.)?weibo\.com\/(u\/page\/(fav|like)\/)/.test(document.location.href)) {
  2609. return;
  2610. }
  2611. resolver.showFavWeiboRestoreBtn();
  2612. });
  2613. }
  2614. $('body').on('mousedown', resolver.findCardFavBtn().selector, function () {
  2615. var $self = $(this), $wb_card = resolver.findElemClosestCard($self),
  2616. isHasFavorite = $self.attr('favorite') == '1' || $self.text() === '取消收藏',
  2617. mid = resolver.findWeiboCardMid($wb_card, false),
  2618. omid = resolver.findWeiboCardMid($wb_card, true);
  2619. if (!mid || !omid) {
  2620. toastr.error('未找到mid,代码需要改进');
  2621. return;
  2622. }
  2623. if (!isHasFavorite) {
  2624. if (!getAutoBackUpSetting() && !unsafeWindow.confirm('是否将收藏中的链接备份到缓存,以防止博主删除?')) {
  2625. return;
  2626. }
  2627. if (resolver.isWeiboDelete($wb_card, true) && resolver.getFavWeiboBackup(omid)) {
  2628. if (!unsafeWindow.confirm('原博已被删除,这会删除之前的备份,是否继续?')) {
  2629. return;
  2630. }
  2631. }
  2632. resolver.addFavWeiboBackup($wb_card);
  2633. } else {
  2634. // 兼容旧版本
  2635. resolver.removeFavWeiboBackup(mid) === 404 && mid != omid && resolver.removeFavWeiboBackup(omid, true);
  2636. }
  2637. });
  2638. $('body').on('click', resolver.findCardShowBackupBtn().selector, function () {
  2639. let $wb_card = resolver.findElemClosestCard(this),
  2640. mid = resolver.findWeiboCardMid($wb_card, false),
  2641. omid = resolver.findWeiboCardMid($wb_card, true);
  2642. if (!mid || !omid) {
  2643. toastr.error('未找到mid,代码需要改进');
  2644. return;
  2645. }
  2646. // 兼容旧版本
  2647. let card = resolver.getFavWeiboBackup(mid) || (mid != omid && resolver.getFavWeiboBackup(omid)) || null;
  2648. if (card) {
  2649. resolver.showPhotoLinksPopPanel($wb_card, resolver.generateBatchDownloadOptionsFromBackup($wb_card, card));
  2650. } else {
  2651. toastr.info('本地备份没有备份该微博~');
  2652. }
  2653. });
  2654. $('body').on('click', resolver.findCardRebuildBackupBtn().selector, function () {
  2655. let $wb_card = resolver.findElemClosestCard(this),
  2656. mid = resolver.findWeiboCardMid($wb_card, true);
  2657. if (!mid) {
  2658. toastr.error('未找到mid,代码需要改进');
  2659. return;
  2660. };
  2661. if (resolver.isWeiboDelete($wb_card, true) && resolver.getFavWeiboBackup(mid)) {
  2662. if (!unsafeWindow.confirm('原博已被删除,这会删除之前的备份,是否继续?')) {
  2663. return;
  2664. }
  2665. }
  2666. resolver.addFavWeiboBackup($wb_card);
  2667. });
  2668. resolver.RestoreBackupBtnEventFunc = function () {
  2669. var $self = $(this), $wb_card = resolver.findElemClosestCard(this), mid, omid;
  2670. mid = resolver.findWeiboCardMid($wb_card, false);
  2671. omid = resolver.findWeiboCardMid($wb_card, true);
  2672. if (!mid || !omid) {
  2673. toastr.error('未找到mid,代码需要改进');
  2674. return;
  2675. }
  2676. if (!$self.hasClass('has-restore')) {
  2677. let card = resolver.getFavWeiboBackup(mid) || (mid != omid && resolver.getFavWeiboBackup(omid)) || null;
  2678. if (card) {
  2679. let $pop;
  2680. if (resolver.getResolverName() === 'OldWeiboResolver') {
  2681. $wb_card.append('<div class="WB_feed_detail clearfix" style="padding-top:0px;"><div class="WB_detail"><div class="WB_info" style="display:inline-block;"><a target="_blank"></a></div>' +
  2682. '<div class="WB_from" style="display:inline-block;margin-left:10px"><a target="_blank"></a></div><div class="WB_text"><div><div></div>');
  2683. $wb_card.find('.WB_detail .WB_info a').text(card.user.nickname).attr('href', '//weibo.com/u/' + card.user.uid);
  2684. $wb_card.find('.WB_detail .WB_from a').text(card.date).attr('href', '//weibo.com/' + card.user.uid + '/' + card.id);
  2685. $wb_card.find('.WB_detail .WB_text').html(card.text);
  2686. $pop = resolver.showPhotoLinksPopPanel($wb_card, resolver.generateBatchDownloadOptionsFromBackup($wb_card, card), false);
  2687. $pop.attr('style', 'position:relative;top:0px;right:0px;margin:0 auto;padding:4px 20px 6px;width:75%;z-index:50;').find('.preview').attr('style', 'position:absolute;width:84%;');
  2688. $self.text('删除备份').attr('disabled', 'disabled').addClass('has-restore'); // 设置禁用,防止连续点击两下把备份删了
  2689. setTimeout(function(){
  2690. $self.removeAttr('disabled');
  2691. }, 800);
  2692. } else {
  2693. $wb_card.append(`<div class="WB_cardwrap S_bg2 has-set-restore-btn Fav-Panel-Card" omid="${card.mid}" mid="${card.mid}">
  2694. <div style="display:none;"><header><div class="woo-box-item-flex"><div><div class="woo-box-flex woo-box-alignCenter woo-box-justifyCenter"><a href="//weibo.com/${card.user.uid}/${card.id}"></a></div></div></div></header></div>
  2695. <div class="WB_feed_detail clearfix" style="padding-top:0px;padding: 13px 20px 10px 20px;border-top-width: 0.1px;border-top-color: #8080800f;border-top-style: inset;">
  2696. <div class="WB_detail"><div class="WB_info" style="display:inline-block;font-size: 15px;"><a target="_blank"></a></div>
  2697. <div class="WB_from" style="display:inline-block;margin-left:10px;font-size: 15px;"><a target="_blank"></a></div><div class="WB_text" style="font-size: 14px;padding: 10px 10px 0px 10px;"><div>
  2698. <div></div></div>`
  2699. );
  2700. $wb_card.find('.WB_detail .WB_info a').text(card.user.nickname).attr('href', '//weibo.com/u/' + card.user.uid);
  2701. $wb_card.find('.WB_detail .WB_from a').text(card.date).attr('href', '//weibo.com/' + card.user.uid + '/' + card.id);
  2702. $wb_card.find('.WB_detail .WB_text').html(card.text);
  2703. $pop = resolver.showPhotoLinksPopPanel($wb_card, resolver.generateBatchDownloadOptionsFromBackup($wb_card, card), false);
  2704. $pop.addClass('restore-fav-pop').attr('style', 'position:relative;top:0px;right:0px;margin:0 auto;padding:4px 20px 6px;width:75%;z-index:50;margin-bottom:15px;')
  2705. .find('.preview').attr('style', 'position: absolute;width: 70%;right: 100%;top: 0px;')
  2706. .find('img').attr('style', 'border: 3px solid #f1912d3b;border-radius: 3px;');
  2707. $self.text('删除备份').attr('disabled', 'disabled').addClass('has-restore'); // 设置禁用,防止连续点击两下把备份删了
  2708. resolver.findCardScrollerItem($wb_card)[0].__vue__.item.user = {'id': card.user.uid, 'screen_name': card.user.nickname};
  2709. setTimeout(function(){
  2710. $self.removeAttr('disabled');
  2711. }, 800);
  2712. }
  2713. } else {
  2714. toastr.info('本地备份没有备份该微博~');
  2715. }
  2716. } else {
  2717. if (!unsafeWindow.confirm('你确定要删除收藏备份吗?删除后不可恢复')) {
  2718. return;
  2719. }
  2720. // 兼容旧版本
  2721. resolver.removeFavWeiboBackup(mid) === 404 && mid != omid && resolver.removeFavWeiboBackup(omid, true);
  2722. $self.remove();
  2723. }
  2724. };
  2725. resolver.getResolverName() === 'OldWeiboResolver' && $('body').on('click', resolver.findCardRestoreBackupBtn().selector, resolver.RestoreBackupBtnEventFunc);
  2726.  
  2727. // 批量备份一页收藏
  2728. GM_registerMenuCommand('备份本页中所有的收藏', function() {
  2729. if (!/(?:www\.)?weibo\.com\/(fav|u\/page\/fav\/)/.test(document.location.href)) {
  2730. toastr.error('只能在收藏页执行本操作');
  2731. return;
  2732. }
  2733. if (resolver.getResolverName() === "OldWeiboResolver") {
  2734. let $weibo_cards = resolver.findFeedCardItemList(), $not_backup_cards, $weibo_card, mid, count = 0;
  2735. $not_backup_cards = $weibo_cards.filter(function(i) {
  2736. $weibo_card = $(this);
  2737. mid = resolver.findWeiboCardMid($weibo_card, true);
  2738. if (!resolver.isWeiboDelete($weibo_card, true) && !resolver.getFavWeiboBackup(mid)) {
  2739. count++;
  2740. return true;
  2741. } else {
  2742. return false;
  2743. }
  2744. });
  2745. if (count > 0) {
  2746. if (confirm(`共 ${count} 条微博未备份,是否备份?`)) {
  2747. $not_backup_cards.each(function () {
  2748. resolver.addFavWeiboBackup($(this));
  2749. });
  2750. toastr.success(`新备份 ${count} 个`);
  2751. }
  2752. } else {
  2753. toastr.info('本页之前已全部完成备份~');
  2754. }
  2755. } else {
  2756. resolver.batchBackFavWeiboCard();
  2757. }
  2758. });
  2759. // 收藏时自动备份提示开关
  2760. switchAutoBackUpSetting(getAutoBackUpSetting());
  2761. // 添加已删除微博还原按钮
  2762. if (resolver.getResolverName() === "OldWeiboResolver") {
  2763. setTimeout(function() {
  2764. var tab_type_flag = $(".WB_main_c").find("div:nth-child(1)").attr("id");
  2765. if (tab_type_flag && /.*(favlistsearch)$/.test(tab_type_flag)) {
  2766. resolver.showFavWeiboRestoreBtn();
  2767. }
  2768. }, 1200);
  2769. setTimeout(function() {
  2770. var tab_type_flag = $(".WB_main_c").find("div:nth-child(1)").attr("id");
  2771. if (tab_type_flag && /.*(favlistsearch)$/.test(tab_type_flag)) {
  2772. resolver.showFavWeiboRestoreBtn();
  2773. }
  2774. }, 4000);
  2775. } else {
  2776. setTimeout(function() {
  2777. resolver.showFavWeiboRestoreBtn();
  2778. }, 3000);
  2779. }
  2780. },
  2781. };
  2782.  
  2783. var selectWeiboFavToPanel = function (searchText, searchSize) {
  2784. searchSize = searchSize || 20;
  2785. var getFavWeiboBackup = function (fav_json, mid) { // 获取备份
  2786. let fav_backup_group = fav_json, card = fav_backup_group[String(mid)], rootCard, photos = [], photo_parse_index = 0, video_parse_index = 0;
  2787. if (card) {
  2788. if (card.root) { // 如果传入的mid只是一个转发mid
  2789. rootCard = card;
  2790. card = fav_backup_group[String(rootCard.forward)]; // 获取实际存储数据的mid
  2791. if (card) {
  2792. //card.forward = true;
  2793. //card.rootCard = rootCard;
  2794. //if (!rootCard.name) {
  2795. // rootCard.name = (rootCard.text ? rootCard.text.substr(0, 15) : rootCard.mid);
  2796. //}
  2797. } else {
  2798. card = rootCard;
  2799. }
  2800. }
  2801. if (card.photos && card.photos.length > 0 && typeof card.photos[0] == 'object') {
  2802. return card;
  2803. }
  2804. card.photos && $.each(card.photos, function(i, fileName) {
  2805. let photo = {url: 'https://wx3.sinaimg.cn/large/' + fileName, fileName: fileName, location: 'photos', folder_sort_index: ++photo_parse_index};
  2806. photos.push(photo);
  2807. });
  2808. card.livePhotos && $.each(card.livePhotos, function(i, fileName) {
  2809. let photo = {url: 'https://video.weibo.com/media/play?livephoto=//us.sinaimg.cn/' + fileName + '&KID=unistore,videomovSrc', fileName: fileName, location: 'videos', folder_sort_index: ++video_parse_index};
  2810. photos.push(photo);
  2811. });
  2812. // card.videos && $.each(card.videos, function(i, fileName) {
  2813. // let photo = {url: 'http://f.video.weibocdn.com/' + fileName + '?KID=unistore,video', fileName: fileName, location: 'videos', folder_sort_index: ++video_parse_index};
  2814. // photos.push(photo);
  2815. // });
  2816. card.videos && $.each(card.videos, function(i, fileName) {
  2817. if (fileName.indexOf(':') !== -1) {
  2818. let urls = (fileName.indexOf('@') !== -1 ? fileName : (fileName + '@')).split('@');
  2819. let title = 'https://video.weibo.com/show?fid=' + urls[0] + (urls[1] ? '<br>( ' + urls[1] + ' )' : '');
  2820. let photo = {url: 'https://video.weibo.com/show?fid=' + urls[0], short_url: urls[1], fileName: title, location: 'videos', folder_sort_index: ++video_parse_index, 'download_disable': true};
  2821. photos.push(photo);
  2822. }
  2823. });
  2824. card.photos = photos;
  2825. delete card.livePhotos;
  2826. delete card.videos;
  2827. if (!card.name) {
  2828. card.name = resolver.getNameFromText(card.text) || card.mid;
  2829. }
  2830. card.date = resolver.formatWeiboDate(card.date);
  2831. card.fav_date = card.fav_date || card.date;
  2832. }
  2833. return card;
  2834. }
  2835.  
  2836. var getFavList = function (fav_list_json, searchText, searchSize) {
  2837. var fav_json = fav_list_json, fav_list = [], card, mid_list = [], index = 0;
  2838. let keys = Object.keys(fav_json);
  2839. for (let i = keys.length - 1; i >= 0; i--) {
  2840. card = getFavWeiboBackup(fav_json, fav_json[keys[i]].mid);
  2841. if (searchText && (card.user.nickname || '').indexOf(searchText) === -1 && (card.text || '').indexOf(searchText) === -1) {
  2842. continue;
  2843. }
  2844. //if ((card.text || '').indexOf('甲') === -1){
  2845. // continue;
  2846. //}
  2847. if (mid_list.indexOf(card.mid) === -1) {
  2848. mid_list.push(card.mid);
  2849. fav_list.push(card);
  2850. }
  2851. if (searchSize !== undefined && ++index >= searchSize) {
  2852. break;
  2853. }
  2854. }
  2855. return fav_list;
  2856. }
  2857. // let fav_list_json = Object.values(GM_getValue(Constants.KEY_FAV_BACKUP_GROUP, {})).sort(function (l, r) {
  2858. // return (l.fav_date || l.date) > (r.fav_date || r.date) ? 1 : -1
  2859. // }).map(function (card) {
  2860. // return (card.mid, card);
  2861. // });
  2862. let fav_list_json = GM_getValue(Constants.KEY_FAV_BACKUP_GROUP, {});
  2863. let fav_list = getFavList(fav_list_json, searchText, searchSize), fav_list_html = '';
  2864. for (let card of fav_list) {
  2865. fav_list_html +=
  2866. `<div class="WB_cardwrap S_bg2 has-set-restore-btn Fav-Panel-Card" omid="${card.mid}" mid="${card.mid}">
  2867. <div style="display:none;"><header><div class="woo-box-item-flex"><div><div class="woo-box-flex woo-box-alignCenter woo-box-justifyCenter"><a href="//weibo.com/${card.user.uid}/${card.id}"></a></div></div></div></header></div>
  2868. <div class="WB_feed_detail clearfix WB_empty" style="padding-top:0px;">
  2869. <div class="WB_detail">
  2870. <div class="WB_info" style="display:inline-block;">
  2871. <a target="_blank" href="//weibo.com/u/${card.user.uid}">${card.user.nickname}</a>
  2872. </div>
  2873. <div class="WB_from" style="display:inline-block;margin-left:10px">
  2874. <a target="_blank" href="//weibo.com/${card.user.uid}/${card.id}">${card.date}</a>
  2875. </div>
  2876. <div class="WB_text">${card.text}</div>
  2877. </div>
  2878. </div>
  2879. </div>`;
  2880. }
  2881. let containerName = resolver.getResolverName() === "OldWeiboResolver" ? "#plc_main .WB_main_c" : "#app";
  2882. $(containerName).find('.wb-fav-panel-content').html(fav_list_html);
  2883. //document.querySelector('.wb-fav-panel-content').innerHTML = ;
  2884.  
  2885. $('.wb-fav-panel-content').find('.WB_cardwrap').each(function (i, wb_card) {
  2886. let $wb_card = $(wb_card), $pop, mid = $wb_card.attr('mid'), card = fav_list_json[mid];
  2887. if (!card) {
  2888. return false;
  2889. }
  2890. $pop = resolver.showPhotoLinksPopPanel($wb_card, resolver.generateBatchDownloadOptionsFromBackup($wb_card, card), false);
  2891. $pop.attr('style', 'position:relative;top:0px;right:0px;margin:0 auto;padding:4px 20px 6px;width:75%;z-index:50;')
  2892. // .find('.preview').attr('style', 'position:absolute;width:84%;');
  2893. .find('.preview').attr('style', 'position: absolute;width: 33%;right: 100%;top: 0%;');
  2894. });
  2895. }
  2896. var initWeiboFavPanel = function () {
  2897. $('#WeiboFavPanelStyle').remove();
  2898. $('head').append(`
  2899. <style id="WeiboFavPanelStyle">
  2900.  
  2901. .wb-fav-panel-location {
  2902. position: fixed;
  2903. width: 100%;
  2904. left: 0%;
  2905. top: 0%;
  2906. z-index: 100000;
  2907. background-color: rgba(189, 175, 175, 0.78);
  2908. height: 100%;
  2909. }
  2910.  
  2911. .wb-fav-panel {
  2912. position: fixed;
  2913. width: 70%;
  2914. left:0%;
  2915. top:0%;
  2916. z-index: 100000;
  2917. padding: 5px 15% 5px 15%;
  2918. background-color: rgba(237, 229, 229, 0.45);
  2919. height: 100%;
  2920. overflow: scroll;
  2921. margin-top: 20px;
  2922. }
  2923.  
  2924. .wb-fav-panel-header {
  2925. padding: 5px;
  2926. }
  2927.  
  2928. .wb-fav-panel-content {
  2929. position: relative;
  2930. background: #fff;
  2931. border-radius: 3px;
  2932. border: 1px solid #ccc;
  2933. box-shadow: 0 4px 20px 1px rgba(0,0,0,0.2);
  2934. padding-bottom: 500px;
  2935. }
  2936. .wb-fav-panel-content .WB_cardwrap {
  2937. padding: 20px;
  2938. position: relative;
  2939. border-top-width: 2px;
  2940. border-top-style: outset;
  2941. }
  2942. .wb-fav-panel-content .WB_cardwrap:nth-child(1) {
  2943. padding-top: 30px;
  2944. }
  2945. .wb-fav-panel-content .WB_text {
  2946. font-size: 14px;
  2947. padding: 20px 0px 20px 20px;
  2948. }
  2949. .wb-fav-panel-content .WB_text img {
  2950. width: 1em;
  2951. }
  2952. .wb-fav-panel-content .WB_from, .wb-fav-panel-content .WB_info {
  2953. font-size: 16px;
  2954. }
  2955. .wb-fav-panel-content .WB_empty {
  2956. text-align: unset;
  2957. }
  2958.  
  2959. .wb-fav-panel-content .download-link-pop .preview {
  2960. display: none;
  2961. /*position: relative!important;*/
  2962. }
  2963.  
  2964. /*
  2965. #toast-container {
  2966. font-size: 15px;
  2967. }
  2968. */
  2969.  
  2970. </style>
  2971. `);
  2972.  
  2973. let containerName = resolver.getResolverName() === "OldWeiboResolver" ? "#plc_main .WB_main_c" : "#app";
  2974. $(containerName).append(
  2975. `<div class="WB_feed WB_feed_v3 WB_feed_v4 wb-fav-panel-location" node-type="feed_list">
  2976. <div class="wb-fav-panel">
  2977. <div class="wb-fav-panel-header">
  2978. <label>作者<input type="text" class="wb-fav-panel-search-input-author" placeholder="作者或微博内容"></label>
  2979. <label>数量<input type="text" class="wb-fav-panel-search-input-size" value="20"></label>
  2980. <button value="搜索" class="wb-fav-panel-search-submit">搜索</button>
  2981. </div>
  2982. <div class="wb-fav-panel-content"></div>
  2983. </div>
  2984. </div>`);
  2985. //document.querySelector('.wb-fav-panel-content').innerHTML = ;
  2986.  
  2987. $(containerName).find('.wb-fav-panel .wb-fav-panel-header .wb-fav-panel-search-submit').click(function() {
  2988. let text = $(this).closest('.wb-fav-panel-header').find('.wb-fav-panel-search-input-author').val();
  2989. let size = $(this).closest('.wb-fav-panel-header').find('.wb-fav-panel-search-input-size').val();
  2990. selectWeiboFavToPanel(text,size);
  2991. });
  2992.  
  2993. $('.wb-fav-panel-location').click(function(e) {
  2994. if (e.target.classList.contains('wb-fav-panel')) {
  2995. $(containerName).find('.wb-fav-panel-location').remove();
  2996. switchWeiboFavPanel(true);
  2997. }
  2998. });
  2999. selectWeiboFavToPanel(undefined, 15);
  3000.  
  3001. }
  3002. unsafeWindow.initWeiboFavPanel = initWeiboFavPanel;
  3003.  
  3004. let switch_weibo_fav_panel;
  3005. var switchWeiboFavPanel = function(on) { // 切换收藏面板
  3006. let saveValue = !!on;
  3007. if (switch_weibo_fav_panel) {
  3008. GM_unregisterMenuCommand(switch_weibo_fav_panel);
  3009. }
  3010. if (saveValue) {
  3011. switch_weibo_fav_panel = GM_registerMenuCommand('开启收藏备份面板', function() {
  3012. initWeiboFavPanel();
  3013. toastr.success("开启收藏备份面板~");
  3014. switchWeiboFavPanel(false);
  3015. });
  3016. } else {
  3017. switch_weibo_fav_panel = GM_registerMenuCommand('关闭收藏备份面板', function() {
  3018. let containerName = resolver.getResolverName() === "OldWeiboResolver" ? "#plc_main .WB_main_c" : "#app";
  3019. $(containerName).find('.wb-fav-panel-location').remove();
  3020. toastr.success("关闭收藏备份面板~");
  3021. switchWeiboFavPanel(true);
  3022. });
  3023. }
  3024. };
  3025.  
  3026. setTimeout(function(){switchWeiboFavPanel(true)}, 100);
  3027.  
  3028. core.init();
  3029.  
  3030.  
  3031. });