ADDimgdown

imgdown

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.sleazyfork.org/scripts/468541/1398387/ADDimgdown.js

  1. var numselected=0;
  2. function ADDimgdown() {
  3. if (GM_info.script.namespace!="Z3JlYXN5Zm9yaw=="){
  4. return
  5. }
  6. $(".data-down-ui").remove();
  7. // create thumbnail container
  8. var thumbnailContainer = $("<div>")
  9. .css({
  10. "position": "fixed",
  11. "top": "50%",
  12. "left": "50%",
  13. "transform": "translate(-50%, -50%)",
  14. "z-index": 99999999999999,
  15. "width": "80vw",
  16. "height": "80vh",
  17. "background-color": "#eee",
  18. "overflow": "auto",
  19. "text-align": "center"
  20. })
  21. .hide()
  22. .attr("class", "data-down-ui")
  23. .appendTo($("body"));
  24. // create media type switch buttons
  25. var switchBtnContainer = $("<div>")
  26. .css({
  27. "position": "absolute",
  28. "top": "0",
  29. "left": "0",
  30. "right": "0",
  31. "width": "100%",
  32. "background-color": "#fff",
  33. "padding": "10px",
  34. "text-align": "center",
  35. "box-shadow": "0 2px 5px rgba(0, 0, 0, 0.3)"
  36. })
  37. .appendTo(thumbnailContainer);
  38. var switchBtnVideo = $("<button>")
  39. .css({
  40. "margin-right": "10px"
  41. })
  42. .text("视频列表")
  43. .attr("data-down-button", "video")
  44. .attr("class", "data-down-button")
  45. .appendTo(switchBtnContainer);
  46. var switchBtnAudio = $("<button>")
  47. .css({
  48. "margin-right": "10px"
  49. })
  50. .text("音频列表")
  51. .attr("data-down-button", "audios")
  52. .attr("class", "data-down-button")
  53. .appendTo(switchBtnContainer);
  54. var switchBtnImage = $("<button>")
  55. .text("图片列表")
  56. .css({
  57. "margin-right": "10px"
  58. })
  59. .attr("data-down-button", "images")
  60. .attr("class", "data-down-button")
  61. .appendTo(switchBtnContainer);
  62. var switchBtnselected = $("<button>")
  63. .text("选择全部")
  64. .css({
  65. "margin-right": "10px"
  66. })
  67. .attr("data-down-button", "selected")
  68. .attr("class", "data-down-buttonselected")
  69. .appendTo(switchBtnContainer);
  70. var switchBtnsmall = $("<button>")
  71. .text("过滤小图片")
  72. .css({
  73. "margin-right": "10px"
  74. })
  75. .attr("data-down-button", "small")
  76. .attr("class", "data-down-buttonsmall")
  77. .appendTo(switchBtnContainer);
  78. var switchBtndown = $("<button>")
  79. .text("下载所选资源")
  80. .css({
  81. "margin-right": "10px"
  82. })
  83. .attr("data-down-button", "down")
  84. .attr("class", "data-down-down")
  85. .appendTo(switchBtnContainer);
  86. var switchBtnurl = $("<button>")
  87. .text("导出链接")
  88. .css({
  89. "margin-right": "10px"
  90. })
  91. .attr("data-down-button", "url")
  92. .attr("class", "data-down-url")
  93. .appendTo(switchBtnContainer);
  94. var switchBtnM3U8Video = $("<button>")
  95. .css({
  96. "margin-right": "10px"
  97. })
  98. .text("M3U8格式视频下载")
  99. .attr("data-down-button", "downm3u8")
  100. .attr("class", "data-down-button")
  101. .appendTo(switchBtnContainer);
  102. switchBtnM3U8Video.click(function() {
  103. sectionm3u8menu(GM_getValue("m3u8url", ""));
  104. });
  105. // create thumbnail list
  106. var thumbnailList = $("<div>")
  107. .css({
  108. "margin-top": "50px"
  109. })
  110. .attr("thumbnailList", "thumbnailList")
  111. .appendTo(thumbnailContainer);
  112. // create close button
  113. var closeBtn = $("<button>")
  114. .css({
  115. "position": "absolute",
  116. "top": "10px",
  117. "right": "10px",
  118. "background-color": "rgb(167 166 166)",
  119. "border": "1px solid rgb(204, 204, 204)",
  120. "padding": "5px",
  121. "font-size": "16px",
  122. "color": "aliceblue",
  123. "font-weight": "600",
  124. })
  125. .text("×")
  126. .appendTo(thumbnailContainer);
  127. var mediaLinks = [];
  128. var mediaTypes = ["video", "audio", "img"];
  129. function findKeyPath(obj, targetKey) {
  130. let paths = [];
  131.  
  132. function traverse(currentObj, currentPath) {
  133. for (let key in currentObj) {
  134. if (currentObj.hasOwnProperty(key)) {
  135. let newPath = currentPath ? `${currentPath}.${key}` : key;
  136. if (key === targetKey) {
  137. paths.push(newPath);
  138. }
  139.  
  140. if (typeof currentObj[key] === 'object' && currentObj[key] !== null) {
  141. traverse(currentObj[key], newPath);
  142. }
  143. }
  144. }
  145. }
  146.  
  147. traverse(obj, '');
  148. return paths;
  149. }
  150.  
  151. if (location.hostname.includes('xiaohongshu.com')) {
  152. mediaLinks = [];
  153. var imglistxhs = unsafeWindow.__INITIAL_STATE__;
  154. // console.log(imglistxhs);
  155.  
  156. // 查找 "originVideoKey" 的路径
  157. // let Keypaths = findKeyPath(imglistxhs, 'originVideoKey');
  158. // 打印所有找到的路径
  159. // console.log(Keypaths);
  160.  
  161. var windowurl = window.location.href;
  162. if (windowurl.includes("explore/")) {
  163.  
  164. var match = windowurl.match(/explore\/([^?]+)/);
  165. if (match && match[1]) {
  166. var result = match[1];
  167. } else {
  168. console.log("没有找到匹配的字符串");
  169. }
  170.  
  171. if ( imglistxhs.note.noteDetailMap.hasOwnProperty(result)) {
  172. var imageList = imglistxhs.note.noteDetailMap[result]["note"].imageList;
  173. for (var i = 0; i < imageList.length; i++) {
  174. var image = imageList[i];
  175. var traceId="";
  176. if (image.traceId !== undefined && image.traceId !== "") {
  177. traceId=image.traceId;
  178. }else {
  179. traceId=image.infoList[0].url;
  180. var regex = /\/([a-zA-Z0-9]+)!/;
  181. var match = regex.exec(traceId);
  182. traceId = match[1];
  183. }
  184. mediaLinks.push({
  185. "type": "img",
  186. "src": "https://sns-img-bd.xhscdn.com/"+traceId+"?imageView2/2/w/1080/format/jpg",
  187. "width": image.width,
  188. "height": image.height,
  189. });
  190. }
  191. //"note.noteDetailMap.66584ddb000000000f00c9d6.note.video.consumer.originVideoKey"
  192. var videoList = imglistxhs.note.noteDetailMap[result]["note"].video;
  193. //console.log(videoList);
  194. if (videoList && videoList["consumer"]["originVideoKey"]) {
  195. var videourl = videoList["consumer"]["originVideoKey"];
  196. console.log("http://sns-video-bd.xhscdn.com/"+videourl);
  197. mediaLinks.push({
  198. "type": "video",
  199. "src": "http://sns-video-bd.xhscdn.com/"+videourl,
  200. });
  201. }
  202.  
  203. }
  204. }
  205. if (imglistxhs.feed.feeds.hasOwnProperty('_rawValue')) {
  206. var imageList =imglistxhs.feed.feeds._rawValue;
  207. for (var i = 0; i < imageList.length; i++) {
  208. var image = imageList[i];
  209. mediaLinks.push({
  210. "type": "img",
  211. "src": "https://sns-img-bd.xhscdn.com/"+image.noteCard.cover.traceId+"?imageView2/2/w/1080/format/jpg",
  212. "width": image.noteCard.cover.width,
  213. "height": image.noteCard.cover.height,
  214. });
  215. }
  216. }
  217. }
  218. var videoiframes = document.querySelectorAll('iframe');
  219. videoiframes.forEach(function(iframe) {
  220. try {
  221. var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
  222. var imgElements = iframeDocument.querySelectorAll('img');
  223. var canvasElements = iframeDocument.querySelectorAll('canvas');
  224. imgElements.forEach(function(img) {
  225. var imgSrc = img.getAttribute('src');
  226. mediaLinks.push({
  227. "type": "img",
  228. "src": imgSrc,
  229. "width": img.naturalWidth,
  230. "height": img.naturalHeight,
  231. });
  232. });
  233. canvasElements.forEach(function(img) {
  234. try {
  235. var imageData = img.toDataURL('image/png');
  236. let imageSize = getImageSize(imageData);
  237. let base64Image = imageData.split(',')[1];
  238. mediaLinks.push({
  239. "type": "img",
  240. "src": "data:image/png;base64,"+base64Image,
  241. "width": imageSize.width,
  242. "height": imageSize.height,
  243. });
  244. } catch (err) {
  245. console.error('获取某些图片失败:Failed to export canvas as PNG', err);
  246. // 使用其他方式导出图片
  247. }
  248. });
  249. var videoElements = iframeDocument.querySelectorAll('video');
  250. videoElements.forEach(function(video) {
  251. var sourceElement = video.querySelector('source');
  252. var videoSrc="";
  253. if (sourceElement) {
  254. videoSrc = sourceElement.getAttribute('src');
  255. } else {
  256. videoSrc = video.getAttribute('src');
  257. }
  258. if (videoSrc.includes('.mp4')) {
  259. mediaLinks.push({
  260. "type": "video",
  261. "src": videoSrc,
  262. });
  263. }
  264. });
  265. } catch (error) {
  266. console.log("发生错误:", error);
  267. }
  268. });
  269. // get all media links
  270. mediaTypes.forEach(function(mediaType) {
  271. $(mediaType).each(function() {
  272. let checkreturn=true;
  273. if (checkreturn) {
  274. let src=$(this).attr("src");
  275. if ($(this).find('source').length > 0) {
  276. $(this).find('source').each(function() {
  277. if ($(this).attr('src')){
  278. let vsrc=$(this).attr('src');
  279. vsrc=completeUrl(vsrc);
  280. if (vsrc.includes('m3u8')) {
  281. mediaLinks.push({
  282. "type": mediaType,
  283. "src": vsrc,
  284. "m3u8": vsrc,
  285. });
  286. }else{
  287. mediaLinks.push({
  288. "type": mediaType,
  289. "src": vsrc
  290. });
  291. }
  292. }
  293. });
  294. }
  295. if (src && mediaType!="img"){
  296. if (src.includes('rgb(') || src.includes('linear-gradient')) {
  297. src=false;
  298. }
  299. }
  300. if (src){
  301. if (mediaType=="img"){
  302. // src=src.replace(/webp/g, 'png');
  303. }
  304. let width = $(this).width();
  305. let height = $(this).height();
  306. src=completeUrl(src);
  307. if ($(this).attr('m3u8')){
  308. mediaLinks.push({
  309. "type": mediaType,
  310. "src": src,
  311. "m3u8": $(this).attr('m3u8'),
  312. "width": width,
  313. "height": height,
  314. });
  315. }else{
  316. mediaLinks.push({
  317. "type": mediaType,
  318. "src": src,
  319. "width": width,
  320. "height": height,
  321. });
  322. }
  323. }
  324. }
  325. });
  326. });
  327. $('canvas').each(function() {
  328. let canvas = $(this)[0];
  329. try {
  330. var imageData = canvas.toDataURL('image/png');
  331. let imageSize = getImageSize(imageData);
  332. let base64Image = imageData.split(',')[1];
  333. mediaLinks.push({
  334. "type": "img",
  335. "src": "data:image/png;base64,"+base64Image,
  336. "width": imageSize.width,
  337. "height": imageSize.height,
  338. });
  339. } catch (err) {
  340. console.error('获取某些图片失败:Failed to export canvas as PNG', err);
  341. // 使用其他方式导出图片
  342. }
  343. });
  344. // 获取图片大小的函数
  345. function getImageSize(imageData) {
  346. let image = new Image();
  347. image.src = imageData;
  348. let width = image.width;
  349. let height = image.height;
  350. return {
  351. width: width,
  352. height: height
  353. };
  354. }
  355. $('div, a, span, body').each(function() {
  356. let checkreturn=true;
  357. if (window.location.href.includes('xiaohongshu.com')) {
  358. checkreturn=false;
  359. }
  360. if (checkreturn) {
  361. // 获取元素的计算样式
  362. var computedStyle = window.getComputedStyle(this);
  363. // 判断元素是否存在 background-image 样式
  364. var backgroundImage = computedStyle.getPropertyValue('background-image');
  365. if (backgroundImage !== 'none') {
  366. let matches = [...backgroundImage.matchAll(/url *\(['"]?([^'"\(\)]+)['"]?\)/g)];
  367. for (let i=0; i<matches.length; i++) {
  368. let src = matches[i][1];
  369. if (src.includes('rgb(') || src.includes('linear-gradient')) {
  370. continue; // 跳过背景图中的线性渐变和 RGB 颜色值
  371. }
  372. let width = $(this).width();
  373. let height = $(this).height();
  374. src = src.replace(/webp/g, 'png'); // 替换图片格式
  375. src = completeUrl(src); // 补全图片链接
  376. mediaLinks.push({
  377. "type": 'img',
  378. "src":src,
  379. "width": width,
  380. "height": height,
  381. });
  382. }
  383. }
  384. // 判断元素是否存在 background: url() 样式
  385. var backgroundUrl = computedStyle.getPropertyValue('background');
  386. var matches = backgroundUrl.match(/url\(['"]?([^'"\(\)]*)['"]?\)/g);
  387. if (matches) {
  388. for (let i=0; i<matches.length; i++) {
  389. let src = matches[i].replace(/url\(['"]*/, '').replace(/['"]*\)/, '');
  390. if (src.includes('rgb(') || src.includes('linear-gradient')) {
  391. continue; // 跳过背景图中的线性渐变和 RGB 颜色值
  392. }
  393. let width = $(this).width();
  394. let height = $(this).height();
  395. src = src.replace(/webp/g, 'png'); // 替换图片格式
  396. src = completeUrl(src); // 补全图片链接
  397. mediaLinks.push({
  398. "type": 'img',
  399. "src":src,
  400. "width": width,
  401. "height": height,
  402. });
  403. }
  404. }
  405. }
  406. });
  407. // 去除重复项
  408. mediaLinks = mediaLinks.filter(function(item, index, self) {
  409. return index === self.findIndex(function(t) {
  410. return t.src === item.src;
  411. });
  412. });
  413. // generate thumbnail list
  414. var videonum=0;
  415. var audionum=0;
  416. var imgnum=0;
  417. mediaLinks.forEach(function(mediaLink, index, array) {
  418. var thumbnail = $("<div>")
  419. .css({
  420. "display": "inline-block",
  421. "margin": "10px",
  422. "cursor": "pointer"
  423. })
  424. .attr("class", "data-down-list");
  425. if (mediaLink.type === "video") {
  426. var videoThumbnail = $("<video>")
  427. .attr("src", mediaLink.src)
  428. .attr("height", "120px")
  429. .attr("type", "video/mp4")
  430. .attr("controls", "controls");
  431. if (mediaLink.m3u8){
  432. thumbnail.attr("data-url", mediaLink.m3u8);
  433. thumbnail.attr("m3u8", mediaLink.m3u8);
  434. }else{
  435. thumbnail.attr("data-url", mediaLink.src)
  436. }
  437. thumbnail.append(videoThumbnail);
  438. videonum++
  439. $('[data-down-button="video"]').text('视频列表('+videonum+')');
  440. }
  441. else if (mediaLink.type === "audio") {
  442. var audioThumbnail = $("<audio>")
  443. .attr("src", mediaLink.src)
  444. .attr("controls", "controls");
  445. thumbnail.attr("data-url", mediaLink.src)
  446. thumbnail.append(audioThumbnail);
  447. audionum++
  448. $('[data-down-button="audios"]').text('音频列表('+audionum+')');
  449. }
  450. else {
  451. var imageThumbnail = $("<img>")
  452. .attr("src", mediaLink.src)
  453. .attr("data-width", mediaLink.width)
  454. .attr("data-height", mediaLink.height)
  455. .attr("height", "120px");
  456. thumbnail.attr("data-url", mediaLink.src)
  457. thumbnail.append(imageThumbnail);
  458. imgnum++
  459. $('[data-down-button="images"]').text('图片列表('+imgnum+')');
  460. }
  461. thumbnailList.append(thumbnail);
  462. // bind click event to thumbnails
  463. thumbnail.on("click", function() {
  464. GM_setClipboard(mediaLink.src);
  465. if (mediaLink.src && mediaLink.src.startsWith('blob:')) {
  466. if (mediaLink.m3u8 ){
  467. GM_setClipboard(mediaLink.m3u8);
  468. sectionm3u8menu(mediaLink.m3u8);
  469. toastr.success('该视频为特殊格式的m3u8视频。已跳转至下载页面。请在页面上继续操作', '', { positionClass: 'toast-bottom-right', showDuration: 300, hideDuration: 1000, timeOut: 3000, extendedTimeOut: 1000, showEasing: 'swing', hideEasing: 'linear', showMethod: 'fadeIn', hideMethod: 'fadeOut' });
  470. /* if (confirm("此链接为加密链接?是否跳转到解析网站上破解下载?【补充说明:如果脚本提示跨域是否允许访问,请选择允许全部】")) {
  471. let kw=encodeURIComponent(mediaLink.m3u8);
  472. let hostname = new URL(mediaLink.m3u8).hostname;
  473. let date = new Date();
  474. let timestamp = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}_${date.getHours().toString().padStart(2, '0')}-${date.getMinutes().toString().padStart(2, '0')}-${date.getSeconds().toString().padStart(2, '0')}`;
  475. let filename = `${hostname}_${timestamp}.html`;
  476. let vurl='https://tools.thatwind.com/tool/m3u8downloader#m3u8='+kw+'&referer='+kw+'&filename='+filename;
  477. GM_openInTab(vurl, {active: !0});
  478. }
  479. */
  480. }else{
  481. toastr.error('此视频已经被该网站加密,暂时不能提供下载', '', { positionClass: 'toast-top-center', showDuration: 300, hideDuration: 1000, timeOut: 3000, extendedTimeOut: 1000, showEasing: 'swing', hideEasing: 'linear', showMethod: 'fadeIn', hideMethod: 'fadeOut' });
  482. }
  483. }else{
  484. toastr.success('已选择并复制了此资源链接!', '', { positionClass: 'toast-bottom-right', showDuration: 300, hideDuration: 1000, timeOut: 3000, extendedTimeOut: 1000, showEasing: 'swing', hideEasing: 'linear', showMethod: 'fadeIn', hideMethod: 'fadeOut' });
  485. }
  486. });
  487. });
  488. // show thumbnail container
  489. thumbnailContainer.show();
  490. // bind click event to switch buttons
  491. switchBtnVideo.on("click", function() {
  492. thumbnailList.find("div").hide();
  493. thumbnailList.find("video").parent().show();
  494. });
  495. switchBtnAudio.on("click", function() {
  496. thumbnailList.find("div").hide();
  497. thumbnailList.find("audio").parent().show();
  498. });
  499. switchBtnImage.on("click", function() {
  500. thumbnailList.find("div").hide();
  501. thumbnailList.find("img").parent().show();
  502. });
  503. // bind click event to close button
  504. closeBtn.on("click", function() {
  505. thumbnailContainer.hide();
  506. thumbnailList.empty();
  507. });
  508. // add custom styles
  509. GM_addStyle(`
  510. ::selection {
  511. background-color: #4285f4;
  512. color: #fff;
  513. }
  514. `);
  515. };
  516. $("body").on('click', '.data-down-list', function() {
  517. if ($(this).hasClass('selected')) {
  518. $(".data-down-buttonselected").removeClass("selected");
  519. }
  520. $(this).toggleClass("selected");
  521. getallnum();
  522. })
  523. $("body").on('click', '.data-down-button', function() {
  524. $(".data-down-button").removeClass("selected");
  525. $(this).toggleClass("selected");
  526. $(".data-down-buttonsmall").removeClass("selected");
  527. getallnum();
  528. if ($(this).attr("data-down-button")=="video"){
  529. toastr.success('说明:某些网页的视频下载地址被他们自己加密过所以下载不了!如果你能解密下载地址可以反馈我添加上去。过滤小图片可以过滤宽度或者高度小于120px的图片。有BUG和建议可以反馈我', '', { positionClass: 'toast-bottom-right', showDuration: 300, hideDuration: 1000, timeOut: 3000, extendedTimeOut: 1000, showEasing: 'swing', hideEasing: 'linear', showMethod: 'fadeIn', hideMethod: 'fadeOut' });
  530. }
  531. })
  532. $("body").on('click', '.data-down-buttonselected', function() {
  533. var list=false;
  534. $(".data-down-list").each(function(k,v){
  535. if ($(this).hasClass('selected')) {
  536. list=true;
  537. }
  538. })
  539. if (list){
  540. $(".data-down-list").removeClass("selected");
  541. $(this).removeClass("selected");
  542. $(this).text("选择全部");
  543. }else{
  544. $(".data-down-list").addClass("selected");
  545. $(this).addClass("selected");
  546. getallnum();
  547. }
  548. })
  549. function getallnum() {
  550. let selectedindex=0;
  551. $(".data-down-list.selected").each(function(k,v){
  552. if ($(this).is(':visible')){
  553. selectedindex++;
  554. }
  555. })
  556. $(".data-down-buttonselected").text("选择全部("+selectedindex+")");
  557. numselected=selectedindex;
  558. }
  559. $("body").on('click', '.data-down-buttonsmall', function() {
  560. if ($(this).hasClass('selected')) {
  561. $(".data-down-list").find("img").each(function(k,v){
  562. $(this).parent().show();
  563. })
  564. $(this).removeClass("selected");
  565. } else{
  566. $(".data-down-list").find("img").each(function(k,v){
  567. if ($(this).attr('data-width')) {
  568. let width=$(this).attr('data-width');
  569. let height=$(this).attr('data-height');
  570. if (width<120 || height<120){
  571. $(this).parent().hide();
  572. }
  573. }
  574. })
  575. $(this).addClass("selected");
  576. }
  577. getallnum();
  578. })
  579. $("body").on('click', '.data-down-url', function() {
  580. var urls = [];
  581. let selectedindex=0;
  582. $(".data-down-list.selected").each(function(k,v){
  583. if ($(this).is(':visible')){
  584. urls.push($(this).attr('data-url'));
  585. selectedindex++;
  586. }
  587. })
  588. let downstr='导出失败';
  589. if (selectedindex==0){
  590. if ($(".data-down-list").length>0){
  591. downstr=downstr+",请选择需要导出的资源。点击屏幕中的资源即可选中";
  592. }else{
  593. downstr=downstr+",当前没有搜索到任何资源";
  594. }
  595. toastr.error(downstr, '', { positionClass: 'toast-top-center', showDuration: 300, hideDuration: 1000, timeOut: 3000, extendedTimeOut: 1000, showEasing: 'swing', hideEasing: 'linear', showMethod: 'fadeIn', hideMethod: 'fadeOut' });
  596. }
  597. else{
  598. var myText = urls.join('\n\n');
  599. GM_setClipboard(myText);
  600. var container = document.createElement('div');
  601. container.style.position = 'fixed';
  602. container.style.top = '50%';
  603. container.style.left = '50%';
  604. container.style.transform = 'translate(-50%, -50%)';
  605. container.style.maxWidth = '600px';
  606. container.style.minWidth = '300px';
  607. container.style.width = '80%';
  608. container.style.maxHeight = '80%';
  609. container.style.overflowY = 'auto';
  610. container.style.backgroundColor = 'rgb(255, 255, 255)';
  611. container.style.border = '1px solid rgb(204, 204, 204)';
  612. container.style.borderRadius = '5px';
  613. container.style.boxShadow = 'rgba(0, 0, 0, 0.2) 0px 2px 10px';
  614. container.style.zIndex = '999999999999';
  615. document.body.appendChild(container);
  616. // 创建复制按钮
  617. var copyButton = document.createElement('button');
  618. copyButton.innerText = '复制';
  619. copyButton.style.width = '50px';
  620. copyButton.style.height = '25px';
  621. copyButton.style.borderRadius = '5px';
  622. copyButton.style.backgroundColor = '#6175bd';
  623. copyButton.style.margin='8px 10px';
  624. copyButton.style.border= 'none';
  625. copyButton.style.marginRight= '10px';
  626. copyButton.style.color= 'white';
  627. copyButton.onclick = function() {
  628. myTextarea.select();
  629. document.execCommand('copy');
  630. alert('已复制到剪贴板');
  631. };
  632. container.appendChild(copyButton);
  633. // 创建关闭按钮
  634. var closeButton = document.createElement('button');
  635. closeButton.innerText = '关闭';
  636. closeButton.style.width = '50px';
  637. closeButton.style.height = '25px';
  638. closeButton.style.borderRadius = '5px';
  639. closeButton.style.backgroundColor = '#6175bd';
  640. closeButton.style.margin='8px 10px';
  641. closeButton.style.border= 'none';
  642. closeButton.style.marginRight= '10px';
  643. closeButton.style.color= 'white';
  644. closeButton.onclick = function() {
  645. container.style.display = 'none';
  646. };
  647. container.appendChild(closeButton);
  648. // 创建文本框
  649. var myTextarea = document.createElement('textarea');
  650. var style = myTextarea.style;
  651. style.width = '600px';
  652. style.height = '700px';
  653. style.resize = 'none';
  654. style.textAlign = 'left';
  655. style.display= 'inline-block';
  656. style.caretColor='black';
  657. myTextarea.value = myText;
  658. container.appendChild(myTextarea);
  659. // 将复制按钮和关闭按钮添加到文本框上方
  660. container.insertBefore(copyButton, myTextarea);
  661. container.insertBefore(closeButton, myTextarea);
  662. }
  663. })
  664. $("body").on('click', '.data-down-down', function() {
  665. let selectedindex=0;
  666. var urls = [];
  667. $(".data-down-list.selected").each(function(k,v){
  668. if ($(this).is(':visible')){
  669. selectedindex++;
  670. urls.push($(this).attr('data-url'))
  671. }
  672. })
  673. let downstr='下载失败';
  674. if (selectedindex==0){
  675. if ($(".data-down-list").length>0){
  676. downstr=downstr+",请选择需要下载的资源。点击屏幕中的资源即可选中";
  677. }else{
  678. downstr=downstr+",当前没有搜索到任何资源";
  679. }
  680. toastr.error(downstr, '', { positionClass: 'toast-top-center', showDuration: 300, hideDuration: 1000, timeOut: 3000, extendedTimeOut: 1000, showEasing: 'swing', hideEasing: 'linear', showMethod: 'fadeIn', hideMethod: 'fadeOut' });
  681. return
  682. }else{
  683. urls.forEach(function(url, index) {
  684. if (url.startsWith('data:image')){
  685. var blob = b64toBlob(url);
  686. url=URL.createObjectURL(blob);
  687. }
  688. let fileName=getFileNameFromUrl(url);
  689. if (url.includes('sns-video-') && url.includes('xhscdn') ) {
  690. fileName= Date.now().toString() + Math.floor(1000 + Math.random() * 9000).toString()+'.mp4';
  691. }
  692. GM_download({
  693. url: url,
  694. name: fileName,
  695. saveAs: false,
  696. onload: function() {
  697. toastr.success("已下载"+(index+1)+"个资源,剩余"+(selectedindex-(index+1 ))+"...", '', { positionClass: 'toast-bottom-right', showDuration: 300, hideDuration: 1000, timeOut: 3000, extendedTimeOut: 1000, showEasing: 'swing', hideEasing: 'linear', showMethod: 'fadeIn', hideMethod: 'fadeOut' });
  698. },
  699. onerror: function(err) {
  700. console.error('第'+(index)+'个资源下载失败'+err , err);
  701. }
  702. });
  703. });
  704. }
  705. })
  706. $("<style>")
  707. .prop("type", "text/css")
  708. .html(`
  709. .data-down-ui .data-down-list:hover {
  710. border: 2px solid #00BFFF;
  711. box-shadow: 0 0 5px #00BFFF;
  712. }
  713. .data-down-ui .selected {
  714. border: 2px solid #ed3f68;
  715. box-shadow: 0 0 5px #ff8ef1;
  716. }
  717. .data-down-ui .data-down-button, .data-down-buttonselected, .data-down-buttonsmall, .data-down-down, .data-down-url{
  718. border: none;
  719. border-radius: 10px;
  720. background-color: #6175bd;
  721. color: white;
  722. padding: 8px 10px;
  723. font-size: 15px;
  724. display: initial;
  725. }
  726. `)
  727. .appendTo("head");
  728. function completeUrl(url) {
  729. if (url){
  730. if (/^https?:\/\//i.test(url)) {
  731. return url;
  732. }
  733. if (/^\/\//.test(url)) {
  734. if (location.protocol === 'https:') {
  735. return 'https:' + url;
  736. }else{
  737. return 'http:' + url;
  738. }
  739. }
  740. if (url.startsWith('data:')) {
  741. var commaIndex = url.indexOf(',');
  742. if (commaIndex !== -1) {
  743. var base64String = url.slice(commaIndex + 1);
  744. return url;
  745. }
  746. }
  747. var origin = window.location.origin;
  748. if (url.indexOf('http') == -1){
  749. if (url.startsWith('/')){
  750. return origin + url;
  751. }else{
  752. return origin +'/'+ url;
  753. }
  754. }
  755. return url;
  756. }
  757. }
  758. function b64toBlob(base64Data) {
  759. var byteString = atob(base64Data.split(',')[1]);
  760. var ab = new ArrayBuffer(byteString.length);
  761. var ia = new Uint8Array(ab);
  762. for (var i = 0; i < byteString.length; i++) {
  763. ia[i] = byteString.charCodeAt(i);
  764. }
  765. return new Blob([ab], {type: 'image/jpeg'});
  766. }
  767. function getFileNameFromUrl(url) {
  768. var fileName = '';
  769. if (url.indexOf('?') >= 0) {
  770. url = url.split('?')[0]; // 去掉查询参数
  771. }
  772. var pos = url.lastIndexOf('/');
  773. if (pos < 0) {
  774. pos = url.lastIndexOf('\\');
  775. }
  776. if (pos >= 0) {
  777. fileName = url.substring(pos + 1);
  778. } else {
  779. fileName = url;
  780. }
  781. return fileName;
  782. }
  783. function extractM3u8Links() {
  784. if (GM_info.script.namespace!="Z3JlYXN5Zm9yaw=="){
  785. return
  786. }
  787. var m3u8Links = "";
  788. var open = XMLHttpRequest.prototype.open;
  789. XMLHttpRequest.prototype.open = function() {
  790. this.addEventListener('load', function() {
  791. var responseURL = this.responseURL;
  792. if (responseURL && responseURL.endsWith('.m3u8')) {
  793. m3u8Links = responseURL;
  794. addgom3u8(m3u8Links);
  795. return m3u8Links;
  796. }
  797. });
  798. open.apply(this, arguments);
  799. };
  800. var originalFetch = window.fetch;
  801. window.fetch = function() {
  802. return originalFetch.apply(this, arguments).then(function(response) {
  803. var responseURL = response.url;
  804. if (responseURL && responseURL.endsWith('.m3u8')) {
  805. m3u8Links = responseURL;
  806. addgom3u8(m3u8Links);
  807. return response;
  808. }
  809. return response;
  810. });
  811. };
  812. var regex =/https:(.*?)\.m3u8/g;
  813. var matches = document.documentElement.innerHTML.match(regex);
  814. if (matches && matches.length > 0) {
  815. matches[0]=matches[0].replace(/\\/g, '');
  816. let startIndex = matches[0].lastIndexOf("https://");
  817. if (startIndex !== -1) {
  818. matches[0]= matches[0].substring(startIndex);
  819. }
  820. var m3u8Links = matches[0];
  821. addgom3u8(m3u8Links);
  822. return
  823. }
  824. return m3u8Links;
  825. }
  826. function addgom3u8(url) {
  827. if (url!="" && url !="https:(.*?).m3u8"){
  828. var startIndex = url.lastIndexOf("https://");
  829. if (startIndex !== -1) {
  830. url= url.substring(startIndex);
  831. }
  832. GM_setClipboard(url);
  833. $("video").attr("m3u8",url);
  834. GM_setValue('m3u8url',url);
  835. let substr = '/20220707/nP0uddUJ/2000kb/hls/index.m3u8';
  836. if (url.includes(substr)) {
  837. return
  838. }
  839. toastr.options.onclick = function() {
  840. sectionm3u8menu(GM_getValue("m3u8url"));
  841. }
  842. // 显示 toastr 弹出框
  843. toastr.success('刚刚获取到页面的m3u8资源,已经复制该链接,打开左侧【视频工具】粘贴该链接即可解析下载。', '', {
  844. positionClass: 'toast-bottom-right',
  845. showDuration: 300,
  846. hideDuration: 1000,
  847. timeOut: 3000,
  848. extendedTimeOut: 1000,
  849. showEasing: 'swing',
  850. hideEasing: 'linear',
  851. showMethod: 'fadeIn',
  852. hideMethod: 'fadeOut'
  853. });
  854. }
  855. }
  856. extractM3u8Links();