小红书无水印媒体

读取小红书页面内容的图片视频的无水印媒体数据,左侧按钮是解析出链接,显示在页面内,右边蓝色按钮是把链接发送到网页文件下载器,进行下载。评论的图片,只发送到网页下载器下载。

  1. // ==UserScript==
  2. // @name 小红书无水印媒体
  3. // @namespace http://your.namespace.com
  4. // @version 0.2
  5. // @description 读取小红书页面内容的图片视频的无水印媒体数据,左侧按钮是解析出链接,显示在页面内,右边蓝色按钮是把链接发送到网页文件下载器,进行下载。评论的图片,只发送到网页下载器下载。
  6. // @author Your name
  7. // @match https://www.xiaohongshu.com/*
  8.  
  9. // @icon https://www.xiaohongshu.com/favicon.ico
  10. // ==/UserScript==
  11.  
  12. // 创建按钮元素
  13. // var button = document.createElement('button');
  14. // button.innerHTML = '按钮';
  15. // // 设置按钮样式
  16. // button.style.position = 'fixed';
  17. // button.style.top = '0';
  18. // button.style.left = '0';
  19. // button.style.zIndex = '9999';
  20. // button.addEventListener('click', function (event) {
  21. // console.log(unsafeWindow.__INITIAL_STATE__)
  22. // })
  23. //将按钮添加到页面的body元素中
  24. // document.body.appendChild(button);
  25.  
  26. //目录路径,一定要把单斜杠换成双斜杠
  27. var 文件保存目录 = '';
  28. //示例 var 服务器地址='http://127.0.0.1:5001/Mediadown';
  29. var 服务器地址 = '';
  30. var globalJSON = [];
  31. var subJSON = [];
  32. var 页面源码 = '';
  33.  
  34. var 阻止更新页面内容 = localStorage.getItem('阻止更新页面内容');
  35. (function () {
  36. var css = `
  37. .author[target="_blank"]{
  38. max-width: 100px;
  39. }
  40. .mianban {
  41. background-color: rgb(140 153 133 / 80%);
  42. border-radius: 5px;
  43. border: 2px solid #bbb;
  44. box-shadow: 0 1px 0 rgb(0 0 0 / 30%), 0 2px 2px -1px rgb(0 0 0 / 50%), 0 1px 0 rgb(255 255 255 / 30%) inset;
  45. z-index: 888;
  46. width: 400px;
  47. overflow-y: scroll;
  48. position: fixed;
  49. left: 25px;
  50. max-height: 60vh;
  51. }
  52. .Recordingpanel {
  53. z-index: 999;
  54. width: 350px;
  55. left: 25px;
  56. position: fixed;
  57. overflow-y: scroll;
  58. background-color: rgb(184 168 40 / 80%);
  59. border-radius: 5px;
  60. border: 2px solid #bbb;
  61. box-shadow: 0 1px 0 rgb(0 0 0 / 30%), 0 2px 2px -1px rgb(0 0 0 / 50%), 0 1px 0 rgb(255 255 255 / 30%) inset;
  62. }
  63. .提示 {
  64. position: relative;
  65. transform: translate(50%, 0%);
  66. color: #fce001;
  67. height: 15px;
  68. width: 250px;
  69. user-select: none;
  70. }
  71. .Recordingpanel.hidden {
  72. display: none;
  73. }
  74. .Recordingtext {
  75. padding: 5px 10px;
  76. border-radius: 15px;
  77. margin: 5px 0px;
  78. cursor: pointer;
  79. position: relative;
  80. background: #b1a125;
  81. }
  82. .Recordingtext.error {
  83. background: #b13325;
  84. }
  85. .selectList{
  86. overflow-y: scroll;
  87. height: 560px;
  88. }
  89. .jilubutton{
  90. left: 0px;
  91. padding: 10px 0px 5px 3px;
  92. border-radius: 0px 5px 5px 0px;
  93. position: fixed;
  94. width: 20px;
  95. height: 50px;
  96. background: #b1a125;
  97. cursor: pointer;
  98. user-select: none;
  99. }
  100. .jilubutton:hover {
  101. background: rgb(203 169 0);
  102. }
  103. .jilubutton:active {
  104. background: rgb(203 103 0); /* 点击时的颜色 */
  105. }
  106. .set-button {
  107. color: #fff;
  108. border-color: #269CE9;
  109. background-color: #269CE9;
  110. border-radius: 5px;
  111. border: 2px solid #bbb;
  112. box-shadow: 0 1px 0 rgb(0 0 0 / 30%), 0 2px 2px -1px rgb(0 0 0 / 50%), 0 1px 0 rgb(255 255 255 / 30%) inset;
  113. }
  114. .set-button:hover::after {
  115. background-color: rgba(255, 255, 255, 0.3) !important;
  116. }
  117. .set-button:hover {
  118. background-color: #70B9E8;
  119. }
  120. .set-button:active {
  121. background: #2db628;
  122. /* position: fixed; */
  123. /* margin-bottom: 9px; */
  124. text-shadow: none;
  125. box-shadow: 10px 10px 10px rgba(0, 0, 0, .3) inset;
  126. }
  127. .yidong{
  128. background: #074836;
  129. }
  130.  
  131. .set-button {
  132. color: #fff !important;
  133. box-shadow:
  134. 0 1px 0 rgb(0 0 0 / 30%),
  135. 1px 2px 2px 0px rgb(247 23 23 / 50%),
  136. 0px -8px 9px rgb(21 133 207 / 100%) inset; /* 只在右边和下边添加内阴影效果 */
  137. }
  138. .list_down_button {
  139. position: absolute;
  140. bottom: 0px;
  141. right: -9px;
  142. background: #516447;
  143. width: 20px;
  144. height: 20px;
  145. border-radius: 8px;
  146. cursor: pointer; /* 设置鼠标样式为链接指针 */
  147. overflow: hidden; /* 隐藏伪元素溢出部分 */
  148. }
  149. .list_down_button:hover {
  150. background-color: rgb(252 167 4);
  151. }
  152.  
  153. .list_down_button:active {
  154. background: rgb(231, 106, 4); /* 点击时的颜色 */
  155. }
  156. .list_down_button.wait {
  157. background: #1DA1F2;
  158. }
  159. .list_down_button.fail {
  160. background: brown;
  161. }
  162. .list_down_button.success {
  163. background: green;
  164. }
  165. .meiti_button {
  166. width: 23px;
  167. height: 23px;
  168. background: #0f77c72b;
  169. cursor: pointer;
  170. border-radius: 5px;
  171. margin: 5px;
  172. display: inline-block;
  173. vertical-align: middle;
  174. }
  175. // .meiti_button:hover {
  176. // background-color: rgb(252 167 4);
  177. // }
  178.  
  179. // .meiti_button:active {
  180. // background: rgb(231, 106, 4); /* 点击时的颜色 */
  181. // }
  182. // .pinglun_button{
  183. // width: 20px;
  184. // height: 20px;
  185. // background: rgb(239 199 0);
  186. // cursor: pointer;
  187. // border-radius: 5px;
  188. // }
  189. // .pinglun_button:hover {
  190. // background: rgb(203 169 0);
  191. // }
  192.  
  193. .pinglun_button:active {
  194. background: rgb(203 103 0); /* 点击时的颜色 */
  195. }
  196. .meiti_send_button[down_status="wait"]{
  197. background: linear-gradient(to bottom right, #36a5ff, #03599f);
  198. /* 10s 是动画持续时间,可以根据需要调整 */
  199. animation: rotateBackground 10s linear infinite;
  200. }
  201. .meiti_send_button[down_status="success"]{
  202. background:green;
  203. }
  204. .meiti_send_button[down_status="fail"]{
  205. background:brown;
  206. }
  207. .libox{
  208. display:flex;
  209. background-color: #99a294;
  210. }
  211. .mouseover .libox{
  212. background-color: rgb(0 200 200 / 50%)
  213. }
  214. .loca_download svg {
  215. width: 23px !important;
  216. height: 23px !important;
  217. color: #1DA1F2;
  218. cursor: pointer;
  219. }
  220. .loca_download svg:hover {
  221. color: #d48600;
  222. }
  223. .loca_download svg:active {
  224. color: rgb(231, 106, 4);
  225. }
  226. [down_status="download"] g.download, [down_status="fail"] g.failed, [down_status="success"] g.completed, [down_status="wait"] g.loading{
  227. display: unset;
  228. }
  229. .loca_download g {
  230. display: none;
  231. }
  232. [down_status="wait"] g.loading {
  233. transform-origin: center;
  234. }
  235. @keyframes rotateBackground {
  236. from {
  237. transform: rotate(0deg);
  238. }
  239. to {
  240. transform: rotate(360deg);
  241. }
  242. }
  243. [down_status="wait"] g.loading {
  244. animation: rotate 5s linear infinite;
  245. }
  246. /* 定义旋转动画 */
  247. @keyframes rotate {
  248. from {
  249. transform: rotate(0deg);
  250. }
  251. to {
  252. transform: rotate(360deg);
  253. }
  254. }
  255. .meiti_send_button{
  256. width: 20px;
  257. height: 20px;
  258. cursor: pointer;
  259. border-radius: 5px;
  260. background:#b1a125;
  261. }
  262. .meiti_send_button:hover {
  263. background: rgb(203 169 0);
  264. }
  265. .meiti_send_button:active {
  266. background: rgb(203 103 0); /* 点击时的颜色 */
  267. }
  268. [meiti_send_button="red"]{
  269. background:red;
  270. }
  271. [meiti_send_button="green"]{
  272. background:green;
  273. }
  274. [meiti_send_button="brown"]{
  275. background:brown;
  276. }
  277. .chaxun{
  278. background:#2a7da54a;
  279. border-radius: 20px;
  280. color: #0068ab ;
  281. }
  282. /*设置按钮*/
  283. .main_button {
  284. position: fixed;
  285. top: 55.5vh;
  286. width: 20px;
  287. height: 50px;
  288. /*background: linear-gradient(to right, , rgba(252, 247, 224, 0.5));*/
  289. background: #FF2442;
  290. cursor: pointer;
  291. z-index: 9999;
  292. border-radius: 0px 5px 5px 0px;
  293. padding: 10px 0px 5px 3px;
  294. user-select: none;
  295. }
  296. /*设置面板*/
  297. .settingPanel {
  298. position: fixed;
  299. top: 55.5vh;
  300. left: 20px;
  301. padding: 10px;
  302. color: #376339;
  303. background: linear-gradient(to right, #DCF0B0, #FCF7E0);
  304. border: 1px solid #000;
  305. z-index: 9999;
  306. display: none;
  307. }
  308.  
  309. .button:hover::after {
  310. border-radius: 5px;
  311. background-color: rgba(255, 255, 255, 0.3) !important;
  312. }
  313.  
  314. .button:hover {
  315. border-radius: 5px;
  316. background-color: #70B9E8;
  317. }
  318.  
  319. .button:active {
  320. border-radius: 5px;
  321. background: #2db628;
  322. /* position: fixed; */
  323. /* margin-bottom: 9px; */
  324. text-shadow: none;
  325. box-shadow: 10px 10px 10px rgba(0, 0, 0, .3) inset;
  326. }
  327. .dfturl {
  328. background-size: contain;
  329. background-repeat: no-repeat;
  330. width: 50px;
  331. height: 50px;
  332. }
  333. /**/
  334. .floating-btn-sets[data-v-75df8d6e]{
  335. right: 0px;
  336. width: 35px;
  337. }
  338. /*置顶按钮*/
  339. .back-top[data-v-396124b4] {
  340. background: #13b5db00;
  341. width: 35px;
  342. height: 35px;
  343. }
  344. /*置顶按钮*/
  345. .back-top .btn-wrapper[data-v-396124b4] {
  346. background: #49b0ffa1;
  347. }
  348. /*刷新按钮*/
  349. .reload[data-v-053c290a] {
  350. background: #13b5db00;
  351. width: 35px;
  352. height: 35px;
  353. }
  354. /*刷新按钮*/
  355. .reload .btn-wrapper[data-v-053c290a] {
  356. background: #49b0ffa1;
  357. }
  358. .全赞 {
  359. position: fixed;
  360. top: 61.5vh;
  361. width: 20px;
  362. height: 67px;
  363. background: #ff7524;
  364. cursor: pointer;
  365. z-index: 9999;
  366. border-radius: 0px 5px 5px 0px;
  367. padding: 0px 0px 5px 3px;
  368. user-select: none;
  369. }
  370. .全赞:hover{
  371. background: #f98948;
  372. }
  373. .全赞:active{
  374. background: #d14d00;
  375. }
  376. .阻止更新 {
  377. position: fixed;
  378. top: 70vh;
  379. width: 20px;
  380. height: 67px;
  381. background: #ff7524;
  382. cursor: pointer;
  383. z-index: 9999;
  384. border-radius: 0px 5px 5px 0px;
  385. padding: 0px 0px 5px 3px;
  386. user-select: none;
  387. }
  388. .阻止更新:hover{
  389. background: #f98948;
  390. }
  391. .阻止更新:active{
  392. background: #d14d00;
  393. }
  394. .reds-toast[data-v-2ec4f68e] {
  395. background: #0047a5 !important;
  396. }
  397. `
  398. if (!document.querySelector('.custom')) {
  399. var style = document.createElement('style');
  400. style.type = 'text/css';
  401. style.textContent = css;
  402. style.className = "custom";
  403. document.head.appendChild(style);
  404. }
  405.  
  406. })();
  407.  
  408. (function () {
  409. // 创建设置按钮
  410. var settingButton = document.createElement('div');
  411. settingButton.className = 'main_button';
  412. settingButton.innerHTML = '设置';
  413. document.body.appendChild(settingButton);
  414.  
  415. var settingPanel = document.createElement('div');
  416. settingPanel.className = 'settingPanel';
  417. settingPanel.innerHTML = `
  418. <label for="server">服务器地址:</label>
  419. <br>
  420. <textarea class="server_address" placeholder="http://127.0.0.1:5001/Mediadown" style="width: 200px;resize: none; height: 40px;"></textarea>
  421. <br>
  422. <label for="directory">文件保存目录:</label>
  423. <br>
  424. <textarea class="filedirectory" placeholder="D:\\临时文件\\临时保存" style="width: 200px;resize: none; height: 40px;"></textarea>
  425. <br>
  426. <button id="saveButton" class="button" style="font-weight: bold; color: #333333;cursor:pointer; position: relative;margin: 6px 0px 0px 0px;right: -170px;padding: 4px;">保存</button>
  427. <br>
  428. <span style="font-size: 12px; padding: 3px; border-radius: 5px;">请复制密码:f4hz,<a href="https://police.lanzouw.com/b01a8pxgj" target="_blank"><br>点击这里</a>下载《网页下载器.exe》</span>
  429.  
  430. `;
  431.  
  432. document.body.appendChild(settingPanel);
  433. settingPanel.addEventListener('mousedown', function (e) {
  434. dragMenu(settingPanel, e);
  435. });
  436. 按钮绑定(settingButton, settingPanel);
  437. function 按钮绑定(settingButton, settingPanel,) {
  438. settingButton.addEventListener('mousedown', function (e) {
  439. dragMenu(settingButton, e);
  440. });
  441. // 定义一个变量用于保存计时器的 ID
  442. var timerId;
  443. // 给设置按钮绑定鼠标移入事件
  444. settingButton.addEventListener('mouseenter', function () {
  445. // 显示设置面板
  446. settingPanel.style.display = 'block';
  447. // 清除计时器
  448. clearTimeout(timerId);
  449. });
  450. // 给设置面板绑定鼠标移入事件,避免鼠标移出设置按钮后立即隐藏设置面板
  451. settingPanel.addEventListener('mouseenter', function () {
  452. // 清除计时器
  453. clearTimeout(timerId);
  454. });
  455. // 给设置按钮绑定鼠标移出事件
  456. settingButton.addEventListener('mouseleave', function () {
  457. // 开始计时,500 毫秒后隐藏设置面板
  458. timerId = setTimeout(function () {
  459. settingPanel.style.display = 'none';
  460. }, 500);
  461. });
  462. // 给设置面板绑定鼠标移出事件,避免鼠标移入设置面板后立即隐藏设置面板
  463. settingPanel.addEventListener('mouseleave', function () {
  464. // 开始计时,500 毫秒后隐藏设置面板
  465. timerId = setTimeout(function () {
  466. settingPanel.style.display = 'none';
  467. }, 500);
  468. });
  469. // 显示/隐藏设置界面
  470. if (settingButton) {
  471. settingButton.addEventListener('click', function () {
  472. if (settingPanel.style.display === 'none') {
  473. settingPanel.style.display = 'block';
  474. console.log('开始设置');
  475. } else {
  476. settingPanel.style.display = 'none';
  477. }
  478. });
  479. }
  480. }
  481.  
  482. let 服务器地址value = document.querySelector('.server_address');
  483. // 读取缓存服务器地址的值
  484. 服务器地址 = localStorage.getItem('服务器地址');
  485. //如果缓存没有内容就使用默认的数值
  486. if (服务器地址) {
  487. 服务器地址value.value = 服务器地址;
  488. } else {
  489. 服务器地址value.value = 'http://127.0.0.1:5001/Mediadown'; // 默认值
  490. 服务器地址 = 服务器地址value.value;
  491. }
  492.  
  493. let 文件保存目录value = document.querySelector('.filedirectory');
  494. // 读取缓存服务器地址的值
  495. 文件保存目录 = localStorage.getItem('directoryPath');
  496. //如果缓存没有内容就使用默认的数值
  497. if (文件保存目录) {
  498. 文件保存目录value.value = 文件保存目录;
  499. } else {
  500. 文件保存目录value.value = 'D:\\临时文件\\临时保存'; // 默认值
  501. 文件保存目录 = 文件保存目录value.value;
  502. }
  503. 判断服务器();
  504.  
  505. // 监听保存按钮的点击事件
  506. var saveButton = document.getElementById('saveButton');
  507. saveButton.addEventListener('click', function () {
  508. localStorage.setItem('服务器地址', 服务器地址value.value);
  509. localStorage.setItem('directoryPath', 文件保存目录value.value);
  510. 文件保存目录 = 文件保存目录value.value
  511.  
  512. });
  513. var 全赞 = document.createElement('div');
  514. 全赞.className = '全赞';
  515. 全赞.textContent = '全部点赞';
  516. document.body.appendChild(全赞);
  517. 全赞.addEventListener('click', function (event) {
  518. event.stopPropagation(); // 阻止事件冒泡
  519. event.preventDefault(); // 阻止默认行为
  520. 当前页全赞();
  521. })
  522. var 阻止更新 = document.createElement('div');
  523. 阻止更新.className = '阻止更新';
  524. 阻止更新.textContent = '阻止更新';
  525. document.body.appendChild(阻止更新);
  526. 阻止更新.addEventListener('click', function (event) {
  527. event.stopPropagation(); // 阻止事件冒泡
  528. event.preventDefault(); // 阻止默认行为
  529. if (阻止更新页面内容 === true) {
  530. 阻止更新页面内容 = false;
  531. localStorage.setItem('')
  532. showToast('已经恢复刷新页面内容。😀', true)
  533. } else {
  534. 阻止更新页面内容 = true;
  535. }
  536.  
  537. })
  538. })();
  539. // function 弹出设置服务器(){
  540. // if (服务器地址) {
  541. // 服务器地址value.value = 服务器地址;
  542. // } else {
  543. // // 创建悬浮框来让用户填写服务器地址
  544. // let 悬浮框 = document.createElement('div');
  545. // 悬浮框.style.position = 'fixed';
  546. // 悬浮框.style.top = '50%';
  547. // 悬浮框.style.left = '50%';
  548. // 悬浮框.style.transform = 'translate(-50%, -50%)';
  549. // 悬浮框.style.backgroundColor = '#fff';
  550. // 悬浮框.style.padding = '20px';
  551. // 悬浮框.style.border = '1px solid #ccc';
  552. // 悬浮框.style.zIndex = '9999';
  553. // 悬浮框.style.borderRadius='10px';
  554.  
  555. // let 输入框 = document.createElement('input');
  556. // 输入框.value = 'http://127.0.0.1:5001/Mediadown';
  557. // 输入框.type = 'text';
  558. // 输入框.style.width='290px';
  559. // 输入框.style.height='30px';
  560. // 输入框.style.margin='0 5px 0 0';
  561. // 输入框.style.backgroundColor='#c9c9c9';
  562. // 输入框.className='severinput'
  563. // 输入框.placeholder = '请输入服务器地址';
  564. // 悬浮框.appendChild(输入框);
  565.  
  566. // let 确认按钮 = document.createElement('button');
  567. // 确认按钮.textContent = '确认';
  568. // 确认按钮.className='severconfirm';
  569. // 确认按钮.style.width='60px';
  570. // 确认按钮.style.height='30px';
  571. // 确认按钮.style.cursor='pointer';
  572. // 确认按钮.style.borderRadius='5px';
  573. // 确认按钮.style.background='antiquewhite';
  574. // 确认按钮.addEventListener('click', function () {
  575. // 服务器地址 = 输入框.value;
  576. // localStorage.setItem('服务器地址', 服务器地址);
  577. // 服务器地址value.value = 服务器地址;
  578. // document.body.removeChild(悬浮框);
  579. // });
  580. // 悬浮框.appendChild(确认按钮);
  581. // document.body.appendChild(悬浮框);
  582. // }
  583. // }
  584. function 判断服务器() {
  585. if (/^(https?:\/\/)/.test(服务器地址) === false) {
  586. showToast('如需要使用外部网页文件下载器,请在源码里填写服务器地址。')
  587. }
  588. }
  589. function 读取页面媒体() {
  590. 'use strict';
  591. var indexValues = [];
  592. // 创建一个数组来保存结果
  593. var resultList = []; // 判断是否存在类名为 '.swiper-wrapper [data-swiper-slide-index]' 的元素
  594. var noteItems = document.querySelectorAll('.swiper-wrapper [data-swiper-slide-index]');
  595. if (noteItems.length != 0) {
  596. // 遍历元素列表
  597. noteItems.forEach(function (element) {
  598. var index = element.getAttribute('data-swiper-slide-index');
  599.  
  600. // 检查该值是否已经存在于数组中
  601. if (indexValues.includes(index)) {
  602. console.log('存在重复值:', index);
  603. return; // 如果存在重复值,停止执行后续命令
  604. }
  605. // 提取 URL 文件名
  606. var backgroundImage = element.style.backgroundImage;
  607.  
  608. if (backgroundImage) {
  609. var matchResult = backgroundImage.match(/([^/]+)$/);
  610.  
  611. if (matchResult) {
  612. var filename = matchResult[0].split('!')[0];
  613. console.log('网址文件名:', filename);
  614. } else {
  615. console.log('未找到匹配的内容');
  616. }
  617. } else {
  618. console.log('backgroundImage 不存在或为空');
  619. }
  620.  
  621. var titleSpanText = document.querySelector('.note-content').textContent.replace(/[<>:"/\\|?*]/g, '');
  622. var username = document.querySelector('.username').textContent.replace(/[<>:"/\\|?*]/g, '');
  623. // 构建结果字符串 用户名-文案-URL文件名
  624. var resultString = username + '----' + titleSpanText + '---' + filename + ".jpeg";
  625. // 将结果字符串添加到结果数组中
  626. var obj = {
  627. filename: resultString,
  628. url: "https://sns-img-hw.xhscdn.net/" + filename,
  629. 小红书ID: 小红书ID
  630. };
  631. resultList.push(obj);
  632. // 记录该值并继续执行后续命令
  633. indexValues.push(index);
  634. });
  635. 创建列表框(resultList);
  636. } else {
  637. // 判断是否存在类名为 '.player-container video' 的元素
  638. noteItems = document.querySelectorAll('.player-container video');
  639. if (noteItems.length !== 0) {
  640. var pageSourceCode = document.documentElement.outerHTML;
  641. var start = pageSourceCode.indexOf('originVideoKey":"') + 'originVideoKey":"'.length;
  642. var end = pageSourceCode.indexOf('"', start);
  643. var filename = pageSourceCode.substring(start, end).replace(/\\u002F/g, "/");
  644. var url;
  645. if (filename == "" || filename == "rflow: hidden;") {
  646. noteItems.forEach(function (item) {
  647. var 小红书ID = window.location.pathname.split('/').pop()
  648. 访问获取笔记源码(小红书ID);
  649. // url= item.src;
  650. // filename = url.split('/').pop().split('.')[0];
  651. // showToast("当前获取到的视频链接,非原链接,请刷新界面后进入笔记页面。", false)
  652. // console.log(filename);
  653. return;
  654. });
  655. } else {
  656. url = "http://sns-video-bd.xhscdn.com/" + filename
  657. var titleSpanText = document.querySelector('.note-content').textContent.replace(/[<>:"/\\|?*]/g, '');
  658. var username = document.querySelector('.username').textContent.replace(/[<>:"/\\|?*]/g, '');
  659. // 构建结果字符串 用户名-文案-URL文件名
  660. var resultString = username + '----' + titleSpanText + '---' + filename + ".MP4";
  661. // 将结果字符串添加到结果数组中
  662. var obj = {
  663. filename: resultString,
  664. url: url,
  665. 小红书ID: 小红书ID
  666. };
  667. resultList.push(obj);
  668. console.log(filename);
  669. 创建列表框(resultList);
  670. }
  671.  
  672.  
  673. } else {
  674. // 判断是否存在类名为 '.note-item:not([class*=" "])' 的元素
  675. noteItems = document.querySelectorAll('.note-item:not([class*=" "])');
  676. if (noteItems.length != 0) {
  677. // 遍历每个 note-item 元素
  678. noteItems.forEach(function (noteItem, index) {
  679. // 获取 .cover.ld.mask 类名的元素
  680. var coverElement = noteItem.querySelector('.cover.ld.mask');
  681. var backgroundURL = "";
  682. if (coverElement) {
  683. // 获取 background 属性的 URL
  684. backgroundURL = window.getComputedStyle(coverElement).background.match(/url\(["']?([^"']+)["']?\)/)[1];
  685.  
  686. // 检查 coverElement 是否是链接
  687. if (coverElement.tagName === 'A') {
  688. var wenshuid = coverElement.href;
  689. console.log(wenshuid);
  690. } else {
  691. console.log('coverElement 不是一个链接');
  692. }
  693. }
  694.  
  695. // 获取 title 下的 span 文本 文案内容
  696. if (noteItem.querySelector('.title span')) {
  697. var titleSpanText = noteItem.querySelector('.title span').textContent.replace(/[<>:"/\\|?*]/g, '')
  698. }
  699.  
  700. //获取用户名
  701. var username = "";
  702. if (noteItem.querySelector('.active.router-link-exact-active.author span')) {
  703. // 获取 active router-link-exact-active author 下的 span 文本
  704. username = noteItem.querySelector('.active.router-link-exact-active.author span').textContent;
  705. } else {
  706. if (noteItem.querySelector('.author-wrapper span')) {
  707. // 获取 active router-link-exact-active author 下的 span 文本
  708. username = noteItem.querySelector('.author-wrapper span').textContent;
  709. }
  710. }
  711.  
  712. // 提取 URL 文件名
  713. var filename = backgroundURL.substring(backgroundURL.lastIndexOf('/') + 1).split('!')[0];
  714. // 构建结果字符串 用户名-文案-URL文件名
  715. var resultString = username + '----' + titleSpanText + '---' + filename + ".jpeg";
  716. // 将结果字符串添加到结果数组中
  717. var obj = {
  718. filename: resultString,
  719. url: "https://sns-img-hw.xhscdn.net/" + filename,
  720. 小红书ID: 小红书ID
  721. };
  722. resultList.push(obj);
  723. 创建列表框(resultList,);
  724. });
  725. }
  726. }
  727. }
  728.  
  729. }
  730.  
  731. function 创建列表框(resultList, 按钮_参数) {
  732. if (document.querySelector('.mianban')) {
  733. document.querySelector('.mianban').remove();
  734. 延时();
  735. showToast("删除列表成功。", false);
  736. }
  737. // 添加选择列表到 body 元素
  738. var mianbanbox = document.createElement('div');
  739. // 创建一个选择列表元素
  740. var selectList = document.createElement('div');
  741. selectList.className = 'selectList';
  742. // 遍历结果数组,创建选项并添加到选择列表中
  743. resultList.forEach(function (result, index) {
  744. // 创建带有序号的元素
  745. var optionWrapper = document.createElement('div');
  746. selectList.appendChild(optionWrapper);
  747. optionWrapper.classList.add('optionWrapper');
  748. optionWrapper.style.margin = "5px 10px";
  749. optionWrapper.setAttribute('murl', result.url);
  750. optionWrapper.setAttribute('xiaohongshuid', result.小红书ID);
  751. optionWrapper.style.cursor = "pointer";
  752. optionWrapper.style.position = "relative";
  753. var indexMarker = document.createElement('div');
  754. indexMarker.style.fontWeight = "bold";
  755. indexMarker.classList.add('indexMarker');
  756. indexMarker.textContent = index + 1 + "、"; // 序号从1开始
  757. optionWrapper.appendChild(indexMarker);
  758. var libox = document.createElement('div');
  759. libox.className = 'libox';
  760. libox.textContent = result.filename;
  761. optionWrapper.appendChild(libox);
  762. // optionWrapper.insertBefore(indexMarker, optionWrapper.firstChild);
  763. var dfturlbox = document.createElement('div');
  764. dfturlbox.className = '图片盒子';
  765. dfturlbox.style.height = '50px';
  766. dfturlbox.style.width = '50px';
  767. libox.appendChild(dfturlbox);
  768. var dfturl = document.createElement('div');
  769. dfturl.className = 'dfturl';
  770. dfturl.style.backgroundImage = `url("${result.dfturl}")`;
  771. dfturlbox.appendChild(dfturl);
  772. // var libox = document.createElement('div');
  773. // libox.className='libox';
  774. // libox.textContent = result.filename;
  775. // optionWrapper.appendChild(libox);
  776. // 添加鼠标右键点击事件
  777. optionWrapper.addEventListener('contextmenu', function (event) {
  778. // 阻止默认的右键菜单
  779. event.preventDefault();
  780. // 修改组件样式
  781. this.style.backgroundColor = "rgb(0 100 100 / 50%)";
  782. var url = this.getAttribute('murl');
  783. // 复制URL到剪贴板
  784. copyToClipboard(url);
  785. });
  786. // 添加鼠标移入事件
  787. optionWrapper.addEventListener('mouseover', function () {
  788. this.style.backgroundColor = "rgb(0 200 200 / 50%) ";
  789. this.classList.add('mouseover');
  790. });
  791. // 添加鼠标移出事件
  792. optionWrapper.addEventListener('mouseout', function () {
  793. // 恢复原始背景色
  794. this.style.backgroundColor = "";
  795. this.classList.remove('mouseover');
  796. });
  797. // optionWrapper.addEventListener('dblclick', function () {
  798. // this.style.backgroundColor = "rgb(140 153 133 / 100%)";
  799. // showToast("开始下载媒体信息", true);
  800. // var url = this.getAttribute('murl');
  801. // var filename = this.textContent;
  802. // // 调用 downloadFile 函数进行下载
  803. // downloadFile(url, filename);
  804. // showToast("请选择保存位置", true)
  805. // });
  806. //原生JS下载代码,
  807. optionWrapper.setAttribute('data', index);
  808. optionWrapper.addEventListener('dblclick', function (event) {
  809. event.preventDefault();
  810. const index = pseudoElement.getAttribute('data');
  811. 下载文件(index, 按钮_参数, list_down_button);
  812. });
  813. //创建伪元素
  814. var list_down_button = document.createElement('div');
  815. list_down_button.className = "list_down_button"
  816. list_down_button.setAttribute('data', index);
  817. list_down_button.addEventListener('click', function () {
  818. const index = list_down_button.getAttribute('data');
  819. 下载文件(index, 按钮_参数, list_down_button);
  820. });
  821. libox.appendChild(list_down_button);
  822. selectList.addEventListener('wheel', function (event) {
  823. // 阻止事件冒泡
  824. event.stopPropagation();
  825. // 阻止默认滚动行为
  826. event.preventDefault();
  827. // 获取滚动距离
  828. var delta = event.deltaY || event.detail || event.wheelDelta;
  829.  
  830. // 根据滚动方向调整 scrollTop 和 scrollLeft
  831. if (delta > 0) {
  832. selectList.scrollTop += 10;
  833. } else {
  834. selectList.scrollTop -= 10;
  835. }
  836. })
  837. });
  838.  
  839. var linkWrappers = document.querySelectorAll('.side-bar .link-wrapper:not([class*=" "])'); // 选择所有class为link-wrapper的元素
  840. var top = "calc(30% + 30px)%";
  841. linkWrappers.forEach(function (element) {
  842. if (element.textContent.trim() === "我") { // 检查元素的文本内容是否为"我"
  843. var rect = element.getBoundingClientRect();
  844. top = rect.top + element.clientHeight + 10 + "px";
  845. console.log(element.textContent.trim())
  846. }
  847. });
  848. mianbanbox.className = "mianban";
  849. console.log(top);
  850. mianbanbox.style.top = top;
  851.  
  852. // 检查是否存在 .side-bar .information-wrapper 元素
  853. var informationWrapper = document.querySelector('.side-bar .information-wrapper:not([class*=" "])');
  854. if (informationWrapper) {
  855. // 如果 .side-bar .information-wrapper 存在,设置 mianbanbox 的高度为该元素顶部到页面顶部的距离
  856. var topOffset = informationWrapper.getBoundingClientRect().top;
  857. mianbanbox.style.height = parseFloat(topOffset) - parseFloat(top) + "px";
  858. console.log(parseFloat(topOffset) - parseFloat(top) + "px")
  859. } else {
  860. // 如果 .side-bar .information-wrapper 不存在,设置 mianbanbox 的高度为 580px
  861. mianbanbox.style.height = "580px";
  862. }
  863.  
  864. // if (document.querySelector('div.side-bar:not([class*=" "])').clientWidth + 24 < 490) {
  865. // mianbanbox.style.overflowX = 'scroll'; // 设置垂直滚动条
  866. // }
  867. var 提示 = document.createElement('div');
  868. 提示.className = '提示';
  869. 提示.textContent = `双击此处,可关闭此窗口。`;
  870. mianbanbox.appendChild(提示);
  871. 提示.addEventListener('dblclick', function (event) {
  872. event.stopPropagation(); // 阻止事件冒泡
  873. event.preventDefault(); // 阻止默认行为
  874. mianbanbox.remove();
  875. });
  876.  
  877. mianbanbox.appendChild(selectList);
  878.  
  879. document.querySelector('body').appendChild(mianbanbox);
  880. mianbanbox.addEventListener('mousedown', function (e) {
  881. dragMenu(mianbanbox, e);
  882. });
  883. mianbanbox.addEventListener('scroll', function (event) {
  884. event.stopPropagation(); // 阻止事件冒泡
  885. event.preventDefault(); // 阻止默认行为
  886. });
  887. function 下载文件(index, 按钮_参数, list_down_button) {
  888. list_down_button.classList.add('wait')
  889. var 剩余下载 = false;
  890. var 列表 = document.querySelectorAll(".optionWrapper");
  891. const wenjian = 列表[index];
  892. wenjian.style.backgroundColor = "rgb(140 153 133 / 100%)";
  893. showToast("开始下载媒体信息", true)
  894. var url = wenjian.getAttribute('murl');
  895. var 小红书ID = wenjian.getAttribute('xiaohongshuid');
  896. var filename = wenjian.textContent;
  897. var xhr = new XMLHttpRequest();
  898. xhr.responseType = 'blob';
  899. xhr.onload = function () {
  900. var fileSize = xhr.response.size; // 获取文件大小
  901. var maxSize = 10 * 1024; // 设置最大文件大小为5MB
  902. if (fileSize < maxSize) {
  903. // 如果文件大于5MB,显示警告并返回
  904. showToast("文件过小,疑似URL异常。", true);
  905. list_down_button.classList.remove('wait');
  906. list_down_button.classList.add('error'); // 可以为过大的文件设置一个错误状态
  907. return; // 结束函数执行
  908. }
  909. showToast("文件预下载完成了", true)
  910. list_down_button.classList.add('success')
  911. var a = document.createElement('a');
  912. a.href = window.URL.createObjectURL(xhr.response);
  913. a.download = filename;
  914. a.style.display = 'none';
  915. document.body.appendChild(a);
  916. showToast("请选择保存位置,并保存文件", true)
  917. a.click();
  918. window.URL.revokeObjectURL(a.href);
  919. document.body.removeChild(a);
  920. 添加成功下载文件(小红书ID);
  921. wenjian.setAttribute('down', 'success');
  922. list_down_button.classList.remove('wait');
  923. 列表.forEach(element => {
  924. if (element.getAttribute('xiaohongshuid') === 小红书ID) {
  925. if (element.getAttribute('down') != 'success') {
  926. 剩余下载 = true;
  927. }
  928. }
  929. });
  930. if (!剩余下载) {
  931. 按钮_参数.setAttribute('down_status', 'success');
  932. }
  933. };
  934.  
  935. // 处理下载错误
  936. xhr.onerror = function () {
  937. showToast("下载失败,请检查网络连接并重试", true);
  938. list_down_button.classList.remove('wait');
  939. list_down_button.classList.add('error'); // 可以添加一个错误的类来改变按钮的样式,表示下载失败
  940. wenjian.style.backgroundColor = ""; // 或设置为其他颜色,表示错误状态
  941. };
  942.  
  943. xhr.open('GET', url);
  944. xhr.send();
  945. return false;
  946. }
  947.  
  948. }
  949.  
  950. // var 远程下载记录 = localStorage.getItem('远程下载记录');
  951. // //如果缓存没有内容就使用默认的数值
  952. // if (远程下载记录) {
  953. // 远程下载记录 = JSON.parse(远程下载记录)
  954. // for (let index = 0; index < 远程下载记录.length; index++) {
  955.  
  956. // 创建下载记录列表框(远程下载记录[index].记录)
  957. // }
  958. // } else {
  959. // var jsonString = '[]';
  960. // 远程下载记录 = JSON.parse(jsonString);
  961.  
  962. // }
  963. var jsonString = '[]';
  964. var 远程下载记录 = JSON.parse(jsonString);
  965. 创建下载记录列表框("")
  966.  
  967. function 创建下载记录列表框(content, 添加, 异常) {
  968. if (!document.querySelector('.Recordingpanel')) {
  969. var linkWrappers = document.querySelectorAll('.side-bar .link-wrapper:not([class*=" "])'); // 选择所有class为link-wrapper的元素
  970. var top = "30%";
  971. linkWrappers.forEach(function (element) {
  972. if (element.textContent.trim() === "我") { // 检查元素的文本内容是否为"我"
  973. var rect = element.getBoundingClientRect();
  974. top = rect.top + element.clientHeight + 10 + "px";
  975. }
  976. });
  977. var parent = document.createElement('div');
  978. parent.className = 'Recordingpanel hidden';
  979. parent.style.top = top;
  980. parent.addEventListener('mousedown', function (e) {
  981. dragMenu(parent, e);
  982. });
  983. var informationWrapper = document.querySelector('.side-bar .information-wrapper:not([class*=" "])');
  984. if (informationWrapper) {
  985. // 如果 .side-bar .information-wrapper 存在,设置 mianbanbox 的高度为该元素顶部到页面顶部的距离
  986. var topOffset = informationWrapper.getBoundingClientRect().top;
  987. parent.style.height = parseFloat(topOffset) - parseFloat(top) + "px";
  988. } else {
  989. // 如果 .side-bar .information-wrapper 不存在,设置 mianbanbox 的高度为 580px
  990. parent.style.height = "580px";
  991. }
  992. parent.addEventListener('scroll', function (event) {
  993. event.preventDefault();
  994. if (parent.scrollTop === 0) {
  995. document.querySelector('.Recordingtext').style.background = '#5ab125'
  996. } else {
  997. console.log(parent.scrollHeight, parent.scrollTop);
  998. if (parent.scrollHeight - parent.scrollTop === parent.clientHeight) {
  999. let Recordingtext = document.querySelectorAll('.Recordingtext')
  1000. Recordingtext[Recordingtext.length - 1].style.background = '#7f7313'
  1001. }
  1002.  
  1003. }
  1004.  
  1005. });
  1006. document.querySelector('body').appendChild(parent);
  1007. var jilubutton = document.createElement('div');
  1008. document.querySelector('body').appendChild(jilubutton);
  1009. jilubutton.className = "jilubutton";
  1010. let jilubutton_top = (document.querySelector('.main_button')?.getBoundingClientRect()?.top - jilubutton?.getBoundingClientRect()?.height - 10)
  1011. if (jilubutton_top != 'NAN') {
  1012. jilubutton.style.top = jilubutton_top + 'px'
  1013. }
  1014. jilubutton.style.top = + 'px';
  1015. jilubutton.textContent = "记录";
  1016. jilubutton.addEventListener('click', function () {
  1017. // 恢复原始背景色
  1018. if (document.querySelector('.Recordingpanel.hidden')) {
  1019. parent.classList.remove('hidden');
  1020. jilubutton.textContent = "关闭";
  1021. } else {
  1022. parent.classList.add('hidden');
  1023. jilubutton.textContent = "记录";
  1024. }
  1025. });
  1026. }
  1027. if (content) {
  1028. var record_content = document.createElement('div');
  1029. record_content.classList.add('Recordingtext');
  1030. if (异常) {
  1031. record_content.classList.add('error');
  1032. }
  1033. record_content.textContent = content;
  1034. var Recordingpanel = document.querySelector('.Recordingpanel')
  1035. Recordingpanel.appendChild(record_content)
  1036. record_content.addEventListener('mouseover', function (event) {
  1037. event.stopPropagation();
  1038. var str = record_content.textContent;
  1039. var 小红书ID = str.match(/\b[A-Fa-f0-9]{16,}\b/g)?.[0];
  1040. if (小红书ID) {
  1041. // 获取所有类名为 "title" 的元素
  1042. var elements = document.querySelectorAll('.title[href]');
  1043. // 遍历这些元素,检查它们的 href 属性
  1044. for (var i = 0; i < elements.length; i++) {
  1045. if (elements[i].href.includes(小红书ID)) {
  1046. elements[i].parentElement?.classList.add('chaxun')
  1047. break;
  1048. // 如果需要在找到匹配元素后执行其他操作,可以在这里添加代码
  1049. }
  1050. }
  1051. elements = document.querySelectorAll('.cover.ld.mask[href]');
  1052. // 遍历这些元素,检查它们的 href 属性
  1053. for (var j = 0; j < elements.length; j++) {
  1054. if (elements[j].href.includes(小红书ID)) {
  1055. elements[j].parentElement?.querySelector('.footer').classList.add('chaxun')
  1056. break;
  1057. // 如果需要在找到匹配元素后执行其他操作,可以在这里添加代码
  1058. }
  1059. }
  1060. // //这是笔记详情页面
  1061. // var script = document.querySelectorAll('script');
  1062. // for (let index = 0; index < script.length; index++) {
  1063. // if (script[index].textContent.includes(小红书ID)) {
  1064. // document.querySelector('.note-content').classList.add('chaxun');
  1065. // break;
  1066. // }
  1067.  
  1068. var content = document.querySelectorAll('meta[content]');
  1069. for (let index = 0; index < content.length; index++) {
  1070. if (content[index].getAttribute('content').includes(小红书ID)) {
  1071. document.querySelector('.note-content').classList.add('chaxun');
  1072. break;
  1073. }
  1074. }
  1075. var comment_item = document.querySelectorAll('.comment-item');
  1076. for (let index = 0; index < comment_item.length; index++) {
  1077. if (comment_item[index].id.includes(小红书ID)) {
  1078. comment_item[index].classList.add('chaxun');
  1079. break;
  1080. }
  1081. }
  1082.  
  1083. }
  1084. // 禁止父元素移动的代码
  1085. // parent.style.pointerEvents = 'none';
  1086. });
  1087. record_content.addEventListener('mouseout', function (event) {
  1088. event.stopPropagation();
  1089. document.querySelector('.chaxun')?.classList?.remove('chaxun');
  1090. // 启用父元素移动的代码
  1091. // parent.style.pointerEvents = 'auto';
  1092. });
  1093. if (添加) {
  1094. let json = {
  1095. 记录: content,
  1096. };
  1097. 远程下载记录.push(json)
  1098. // localStorage.setItem('远程下载记录', JSON.stringify(远程下载记录));
  1099. }
  1100.  
  1101. }
  1102. }
  1103.  
  1104. //zhushezhi();
  1105. function zhushezhi() {
  1106. var div3 = document.createElement('div');
  1107. div3.style.fontSize = '15px';
  1108. div3.style.padding = "7px 10px 0px 10px";
  1109. div3.style.zIndex = "999";
  1110. div3.style.width = "70px";
  1111. div3.style.height = "33px";
  1112. div3.style.position = "fixed";
  1113. div3.style.cursor = "pointer";
  1114.  
  1115. div3.style.userSelect = "none";
  1116. div3.textContent = "取图片";
  1117. div3.className = "set-button";
  1118. document.querySelector('body').appendChild(div3);
  1119. var top = null;
  1120. var left = null;
  1121. var gao = document.querySelector('#link-guide')
  1122. if (gao) {
  1123. var rect = gao.getBoundingClientRect();
  1124. top = rect.top - 4 + "px";
  1125. left = rect.left + 2 + rect.width + "px";
  1126. }
  1127.  
  1128. div3.style.top = top;
  1129. div3.style.left = left;
  1130. div3.addEventListener('click', function () {
  1131. console.log('执行打印命令');
  1132. if (document.querySelector('.mianban')) {
  1133. document.querySelector('.mianban').remove();
  1134. showToast("删除列表成功。", false);
  1135. }
  1136. 读取页面媒体();
  1137. showToast("读取页面内小红书媒体信息完成,获取媒体数:" + document.querySelectorAll(".optionWrapper").length, false);
  1138. });
  1139. div3.addEventListener('contextmenu', function (event) {
  1140. event.preventDefault(); // 阻止默认右键菜单
  1141. if (document.querySelector('.mianban')) {
  1142. document.querySelector('.mianban').remove();
  1143. 延时();
  1144. showToast("删除列表成功。", false);
  1145. }
  1146. });
  1147.  
  1148. }
  1149.  
  1150. function 延时() {
  1151. var count = 0;
  1152. var intervalId = setInterval(function () {
  1153. var a = 1;
  1154. console.log(a);
  1155. count++;
  1156. if (count === 5) {
  1157. clearInterval(intervalId);
  1158. }
  1159. }, 1000);
  1160. }
  1161.  
  1162.  
  1163. //页面元素监测,判断小红书笔记列表是否出现
  1164. (function () {
  1165. // 创建一个 MutationObserver 实例
  1166. var observer = new MutationObserver(function (mutations) {
  1167. mutations.forEach(function (mutation) {
  1168. // 检查每个变化的类型
  1169. if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
  1170. // 循环遍历添加的节点
  1171. mutation.addedNodes.forEach(function (addedNode) {
  1172. // 检查添加的节点是否为目标元素
  1173. if (addedNode.classList) {
  1174. const authorWrapperElements = addedNode.querySelectorAll('.footer .author-wrapper');
  1175. if (authorWrapperElements.length > 0) {
  1176. 小红书媒体(authorWrapperElements);
  1177. } else {
  1178. const authorWrapperElements = addedNode.querySelectorAll('[data-v-ed4befca ][class="author-wrapper"]');
  1179. if (authorWrapperElements.length > 0) {
  1180. 评论媒体(authorWrapperElements);
  1181. } else {
  1182. const authorWrapperElements = addedNode.querySelectorAll('.right .author-wrapper');
  1183. if (authorWrapperElements.length > 0) {
  1184. 评论媒体(authorWrapperElements);
  1185. } else {
  1186. const interact_container = addedNode.querySelectorAll('.interact-container');
  1187. if (interact_container.length > 0) {
  1188. 评论框(interact_container);
  1189. } else {
  1190. const bottom_channel = addedNode.querySelector('.bottom-channel[href="https://creator.xiaohongshu.com/publish/publish?source=official"]');
  1191. if (bottom_channel) {
  1192. bottom_channel.parentElement.remove();
  1193. } else {
  1194. var 报错提示 = addedNode.querySelector('[class="reds-toast center"]');
  1195. if (报错提示) {
  1196. if (报错提示.textContent.includes('XMLHttpRequest')) {
  1197. 报错提示.textContent = '已经成功阻止页面刷新,😀';
  1198. }
  1199.  
  1200. } else {
  1201. var 关闭登录(不可用) = addedNode.querySelector('[class="icon-btn-wrapper close-button"]')
  1202. if (关闭登录(不可用)) {
  1203. 关闭登录(不可用).click();
  1204. }
  1205. }
  1206.  
  1207. }
  1208. }
  1209. }
  1210. }
  1211. }
  1212. }
  1213. });
  1214. }
  1215. });
  1216. });
  1217. // 开始观察父节点下的变化
  1218. observer.observe(document.body, { childList: true, subtree: true });
  1219. })();
  1220.  
  1221. function 访问获取笔记源码(小红书ID, 类型, 按钮) {
  1222. get("https://www.xiaohongshu.com/explore/" + 小红书ID + "?exSource=", '', function (content) {
  1223. let initialStateText = 取源码JSON文本(content);
  1224. if (!initialStateText) {
  1225. showToast(小红书ID + '--读取页面该笔记源码失败', false);
  1226. return;
  1227. }
  1228. 笔记源码分析(initialStateText, 类型, 按钮);
  1229. });
  1230. }
  1231. function 取源码JSON文本(content) {
  1232. var regex = /window\.__INITIAL_STATE__=(.*?)(?=<\/script>)/s;
  1233. var match = regex.exec(content);
  1234. if (match && match.length >= 2) {
  1235. let initialStateText = match[1];
  1236. // 现在 initialStateText 中存储了 window.__INITIAL_STATE__ 和 </script> 之间的内容
  1237. initialStateText = initialStateText.replace(/:undefined/g, ':"undefined"');
  1238. return initialStateText;
  1239. }
  1240. }
  1241. // 请求函数,接受url和回调函数作为参数,callback为响应文本
  1242. function get(url, post, callback) {
  1243. var xhr = new XMLHttpRequest();
  1244. xhr.open(post ? 'POST' : 'GET', url, false);// 第三个参数设置为false表示同步请求
  1245. xhr.withCredentials = true;
  1246. xhr.onreadystatechange = function () {
  1247. if (xhr.readyState === 4) {
  1248. if (xhr.status === 200) {
  1249. callback(xhr.responseText);
  1250. } else if (xhr.status === 404) {
  1251. showToast("资源未找到");
  1252. } else if (xhr.status === 500) {
  1253. showToast("服务器内部错误");
  1254. } else if (xhr.status === 0) {
  1255. showToast("连接错误");
  1256. } else {
  1257. showToast("网络错误或其他错误");
  1258. }
  1259. }
  1260. };
  1261. if (post) {
  1262. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  1263. xhr.send(post);
  1264. } else {
  1265. xhr.send();
  1266. }
  1267. }
  1268. // fetch('https://m.v.qq.com/x/m/play?cid=mcv8hkc8zk8lnov',{
  1269. // credentials: 'include' // 包含cookie信息
  1270. // })
  1271.  
  1272. // .then(response => response.text())
  1273. // .then(data => {
  1274. // // 打印元素的文本内容
  1275. // doc=元素转DOM对象(data)
  1276. // console.log(doc.querySelector('body'))
  1277. // })
  1278. // .catch(error => console.error(error));
  1279.  
  1280. // function 元素转DOM对象(data){
  1281. // let htmlString = data;
  1282. // // 创建一个 DOMParser 实例
  1283. // let parser = new DOMParser();
  1284. // // 使用 DOMParser 的 parseFromString 方法将 HTML 文本解析为 DOM 对象
  1285. // return parser.parseFromString(htmlString, 'text/html');
  1286. // }
  1287. //下载方式,1发送至下载器,2,在本地下载,解析到面板
  1288. function 笔记源码分析(initialStateText, 下载方式, 按钮) {
  1289. // 然后再进行 JSON 解析
  1290.  
  1291. var initialStateJSON = JSON.parse(initialStateText);
  1292. // 从 JSON 对象中提取特定路径的内容
  1293. var resultList = [];
  1294. var 小红书ID, 用户名, 用户id, 文件名ID, title, desc, time, 时间, currentDate, mediaurl, mediaurl2, mediaurl3, 文件名, 文案, 文件类型, dfturl, 新文件名, savedDirectoryPath;
  1295. var mediajs = [];
  1296. var downjs = {};
  1297. // 赋值操作
  1298. savedDirectoryPath = localStorage.getItem("directoryPath");
  1299. if (!savedDirectoryPath) {
  1300. 按钮.setAttribute('down_status', 'fail');
  1301. showToast('请在设置里,填写文件保存目录');
  1302. return;
  1303. }
  1304. 小红书ID = initialStateJSON.note?.firstNoteId;
  1305. if (typeof 小红书ID === 'string') {
  1306. console.log('jsonData2.a 是一个字符串');
  1307. } else if (typeof 小红书ID === 'object' && 小红书ID !== null) {
  1308. 小红书ID = initialStateJSON.note?.firstNoteId?._rawValue;
  1309. }
  1310. 用户名 = initialStateJSON.note?.noteDetailMap[小红书ID]?.note?.user?.nickname;
  1311. 用户id = initialStateJSON.note?.noteDetailMap[小红书ID]?.note?.user?.userId;
  1312. 文件名ID = initialStateJSON.note?.noteDetailMap[小红书ID]?.note?.video?.consumer?.originVideoKey;
  1313. title = initialStateJSON.note?.noteDetailMap[小红书ID]?.note?.title;
  1314. desc = initialStateJSON.note?.noteDetailMap[小红书ID]?.note?.desc;
  1315. time = initialStateJSON.note?.noteDetailMap[小红书ID]?.note?.time;
  1316. IP地址 = initialStateJSON.note?.noteDetailMap[小红书ID]?.note?.ipLocation ?? '';
  1317. 文案 = title + desc;
  1318. 文案 = 文案.length > 100 ? 文案.substring(0, 100) : 文案;
  1319. currentDate = new Date(time);
  1320. 时间 = currentDate.toISOString().replace(/[-T:Z.]/g, '').slice(0, 14).replace(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/, '\$1-\$2-\$3_\$4\$5\$6');
  1321.  
  1322. if (文件名ID != undefined) {
  1323. 新文件名 = `${用户名}----${用户id}----${文案}----${小红书ID}----${文件名ID}----${时间}_${IP地址}.mp4`;
  1324. if (新文件名.length > 360) {
  1325. const maxLength = 360 - (新文件名.length - text.length);
  1326. const 文案 = text.substring(0, maxLength);
  1327. 新文件名 = `${用户名}----${用户id}----${文案}----${小红书ID}----${文件名ID}----${时间}_${IP地址}.mp4`;
  1328. }
  1329. mediaurl = "http://sns-video-bd.xhscdn.com/" + 文件名ID
  1330. dfturl = initialStateJSON.note?.noteDetailMap[小红书ID]?.note?.imageList[0].urlDefault;
  1331. if (下载方式 === 1) {
  1332. 文件类型 = "mp4"
  1333. // const json = JSON数据(videourl,videourl2, 文件名ID, 文案, 用户名, 用户id, 文件保存目录, 文件名.replace(/[\\/:*?"<>|,;#%]/g, ''), 文件类型, 小红书ID, '', '');
  1334. mediajs.push({ 用户名: 用户名, 文章ID: 小红书ID, 文件ID: 文件名ID, url: mediaurl, url2: mediaurl2, url3: mediaurl3, 文件名: 新文件名.replace(/[\\/:*?"<>|,;#%]/g, ''), 文件类型: 文件类型 });
  1335. // 视频下载(json, 0, 按钮)
  1336. } else {
  1337. const obj = {
  1338. filename: 文件名,
  1339. url: mediaurl,
  1340. 小红书ID: 小红书ID,
  1341. dfturl: dfturl
  1342. };
  1343. resultList.push(obj);
  1344. 创建列表框(resultList, 按钮);
  1345. showToast("读取页面内小红书媒体信息完成,获取视频数:" + document.querySelectorAll(".optionWrapper").length, true);
  1346. }
  1347. } else {
  1348. var arrayLength = initialStateJSON.note?.noteDetailMap[小红书ID]?.note?.imageList.length;
  1349. if (arrayLength != 0) {
  1350. for (var i = 0; i < arrayLength; i++) {
  1351. var image = initialStateJSON.note?.noteDetailMap[小红书ID]?.note?.imageList[i];
  1352. 文件名ID = image.fileId;
  1353. if (文件名ID === "") {
  1354. dfturl = image.urlDefault;
  1355. 文件名ID = dfturl.substring(dfturl.lastIndexOf("/") + 1, dfturl.lastIndexOf("!"));
  1356. }
  1357. height = image.height;
  1358. 新文件名 = `${用户名}----${用户id}----${文案}----${小红书ID}----${文件名ID}----${时间}_${IP地址}.png`;
  1359. if (新文件名.length > 360) {
  1360. const maxLength = 360 - (新文件名.length - text.length);
  1361. const 文案 = text.substring(0, maxLength);
  1362. 新文件名 = `${用户名}----${用户id}----${文案}----${小红书ID}----${文件名ID}----${时间}_${IP地址}.mp4`;
  1363. }
  1364. mediaurl = `http://ci.xiaohongshu.com/${文件名ID}?imageView2/2/w/format/png`;
  1365. mediaurl2 = `https://ci.xiaohongshu.com/spectrum/${文件名ID}?imageView2/2/w/0/format/jpg/v3`;
  1366. mediaurl3 = `https://sns-img-hw.xhscdn.net/${文件名ID}`;
  1367. if (下载方式 === 1) {
  1368. 文件类型 = "webp"
  1369. mediajs.push({ 用户名: 用户名, 文章ID: 小红书ID, 文件ID: 文件名ID, url: mediaurl, url2: mediaurl2, url3: mediaurl3, 文件名: 新文件名.replace(/[\\/:*?"<>|,;#%]/g, ' '), 文件类型: 文件类型 });
  1370. } else {
  1371. const obj = {
  1372. filename: 文件名,
  1373. url: mediaurl,
  1374. 小红书ID: 小红书ID,
  1375. dfturl
  1376. };
  1377. resultList.push(obj);
  1378. }
  1379. }
  1380.  
  1381.  
  1382. }
  1383. }
  1384. if (下载方式 === 1) {
  1385. console.log('开始准备发送信息');
  1386. downjs.media = mediajs;
  1387. downjs.下载名称 = `《${用户名}》 \n ${文案}`;
  1388. downjs.小红书ID = 小红书ID;
  1389. 按钮.setAttribute('down_status', 'wait');
  1390. downjs.目录 = savedDirectoryPath;//+ 用户名.replace(/[<>:"/\\|?*\x00-\x1F\ud800-\udfff]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u2600-\u27FF]/g, '')
  1391. 媒体发送下载(JSON.stringify(downjs), 按钮);
  1392. // console.log(downjs, JSON.stringify(downjs));
  1393. } else {
  1394. 创建列表框(resultList, 按钮);
  1395. showToast("读取页面内小红书媒体信息完成,获取图片数:" + document.querySelectorAll(".optionWrapper").length, true);
  1396. }
  1397. }
  1398.  
  1399.  
  1400. function 媒体发送下载(post, 按钮) {
  1401. let 下载情况 = '';
  1402. let 正常 = false;
  1403. let 异常 = false;
  1404. var xhr = new XMLHttpRequest();
  1405. xhr.open('POST', 服务器地址, true);
  1406. xhr.withCredentials = true;
  1407. xhr.onreadystatechange = function () {
  1408. if (xhr.readyState === 4) {
  1409. if (xhr.status === 200) {
  1410. try {
  1411. let returnjson = JSON.parse(xhr.responseText)
  1412. if (returnjson.msg.下载.下载状态 === 'true') {
  1413. 按钮.setAttribute('down_status', 'success');
  1414. 异常 = false;
  1415. } else {
  1416. 按钮.setAttribute('down_status', 'fail');
  1417. 异常 = true;
  1418. }
  1419. 下载情况 = returnjson.msg.下载.下载状况;
  1420. // console.log(xhr.responseText)
  1421. 正常 = true;
  1422. 创建下载记录列表框(下载情况, true, 异常);
  1423. 添加成功下载文件2(returnjson.小红书ID);
  1424. } catch {
  1425. showToast('响应内容错误', xhr.responseText);
  1426. }
  1427.  
  1428. } else if (xhr.status === 404) {
  1429. 下载情况 = '视频:网络状态{' + xhr.status + ",资源未找到";
  1430. } else if (xhr.status === 500) {
  1431. 下载情况 = '视频:网络状态{' + xhr.status + ",服务器内部错误";
  1432. } else {
  1433. 下载情况 = '视频:网络状态{' + xhr.status + ",网络错误或其他错误";
  1434. }
  1435. if (!正常) {
  1436. 按钮.setAttribute('down_status', 'fail');
  1437. 判断服务器();
  1438. }
  1439. showToast(下载情况, 正常);
  1440. }
  1441. };
  1442. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  1443. xhr.send(post);
  1444. }
  1445. function 图片下载(post, index, 按钮) {
  1446. var 下载情况 = '';
  1447. let 正常 = false;
  1448. let 异常 = false;
  1449. var xhr = new XMLHttpRequest();
  1450. xhr.open('POST', 服务器地址, true);
  1451. xhr.withCredentials = true;
  1452. xhr.onreadystatechange = function () {
  1453. if (xhr.readyState === 4) {
  1454. if (xhr.status === 200) {
  1455. let returnjson = JSON.parse(xhr.responseText);
  1456. if (returnjson.下载.下载状态 === "下载完毕") {
  1457. 按钮.setAttribute('down_status', 'success');
  1458. 异常 = false;
  1459. } else {
  1460. 按钮.setAttribute('down_status', 'fail');
  1461. 异常 = true;
  1462. }
  1463. 正常 = true;
  1464. 下载情况 = '图片(' + (index + 1) + ')' + returnjson.用户名 + '--' + returnjson.文案.substring(0, 10) + '--' + returnjson.小红书ID + returnjson.下载.下载状态;
  1465. // console.log(xhr.responseText)
  1466. 创建下载记录列表框(下载情况, true, 异常);
  1467. } else {
  1468. if (xhr.status === 404) {
  1469. 下载情况 = '图片(' + (index + 1) + ')--网络状态{' + xhr.status + '},资源未找到\n';
  1470. } else {
  1471. if (xhr.status === 500) {
  1472. 下载情况 = '图片(' + (index + 1) + ')--网络状态{' + xhr.status + ',服务器内部错误\n';
  1473. } else {
  1474. 下载情况 = '图片(' + (index + 1) + ')--网络状态{' + xhr.status + ',网络错误或其他错误\n';
  1475. }
  1476. }
  1477. }
  1478. if (!正常) {
  1479. 按钮.setAttribute('down_status', 'fail');
  1480. 判断服务器();
  1481. }
  1482. showToast(下载情况, true);
  1483.  
  1484. }
  1485. };
  1486. xhr.onerror = function () {
  1487. showToast('发生网络错误');
  1488. };
  1489. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  1490. xhr.send(post);
  1491. }
  1492. function 评论图片下载(post, index, 按钮, 评论ID) {
  1493. let 下载情况 = '';
  1494. let 正常 = false;
  1495. let 异常 = false;
  1496. var xhr = new XMLHttpRequest();
  1497. xhr.open('POST', 服务器地址, true);
  1498. xhr.withCredentials = true;
  1499. xhr.onreadystatechange = function () {
  1500. if (xhr.readyState === 4) {
  1501. if (xhr.status === 200) {
  1502. let returnjson = JSON.parse(xhr.responseText)
  1503. if (returnjson.下载.下载状态 === "下载完毕") {
  1504. 按钮.setAttribute('down_status', 'success');
  1505. 添加成功下载文件(评论ID)
  1506. 异常 = false;
  1507. 正常 = true;
  1508. } else {
  1509. 异常 = true;
  1510. }
  1511. 下载情况 = '图片(' + (index + 1) + ')' + returnjson.用户名 + '--' + returnjson.文案.substring(0, 10) + '--' + returnjson.小红书ID + returnjson.下载.下载状态;
  1512.  
  1513. // console.log(xhr.responseText)
  1514. 创建下载记录列表框(下载情况, true, 异常);
  1515. } else if (xhr.status === 404) {
  1516. 下载情况 = '评论图片(' + (index + 1) + ')--网络状态{' + xhr.status + '},资源未找到\n';
  1517. } else if (xhr.status === 500) {
  1518. 下载情况 = '评论图片(' + (index + 1) + ')--网络状态{' + xhr.status + ',服务器内部错误\n';
  1519. } else {
  1520. 下载情况 = '评论图片(' + (index + 1) + ')--网络状态{' + xhr.status + ',网络错误或其他错误\n';
  1521. }
  1522.  
  1523. if (!正常) {
  1524. 按钮.setAttribute('down_status', 'fail')
  1525. 判断服务器();
  1526. }
  1527. showToast(下载情况, 正常);
  1528. }
  1529. };
  1530. xhr.onerror = function () {
  1531. showToast('发生网络错误');
  1532. };
  1533. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  1534. xhr.send(post);
  1535. }
  1536. function 小红书媒体(authorWrapperElements) {
  1537. // 遍历匹配的元素列表
  1538. authorWrapperElements.forEach(function (element) {
  1539. if (!element.querySelector('.meiti_button')) {
  1540. var linkElement = element.parentNode?.parentNode?.querySelector('a');
  1541. if (linkElement) {
  1542. var link = linkElement.getAttribute('href');
  1543. var 小红书ID = link.substring(link.lastIndexOf('/') + 1);
  1544. if (小红书ID != "") {
  1545. var xiazai = 本地下载按钮元素创建();
  1546. 下载记录查询(小红书ID, xiazai);
  1547. xiazai.className = "loca_download";
  1548. // 添加鼠标点击事件
  1549. xiazai.addEventListener("click", function () {
  1550. xiazai.setAttribute('down_status', 'wait');
  1551. 访问获取笔记源码(小红书ID, 0, xiazai);
  1552. })
  1553. element.appendChild(xiazai);
  1554. var meiti_send_button = document.createElement('div');
  1555. meiti_send_button.className = "meiti_send_button";
  1556. // 添加鼠标点击事件
  1557. 下载记录查询2(小红书ID, meiti_send_button);
  1558. meiti_send_button.addEventListener("click", function () {
  1559. meiti_send_button.setAttribute('down_status', 'wait');
  1560. 访问获取笔记源码(小红书ID, 1, meiti_send_button);
  1561. })
  1562. element.appendChild(meiti_send_button);
  1563. }
  1564. }
  1565. }
  1566. });
  1567. }
  1568.  
  1569. function 评论媒体(authorWrapperElements) {
  1570. // 遍历匹配的元素列表
  1571. authorWrapperElements.forEach(function (element) {
  1572. if (!element.querySelector('.loca_download')) {
  1573. // 添加鼠标点击事件
  1574. var linkElement = element.parentNode?.parentNode?.parentElement?.id;
  1575. if (linkElement) {
  1576. var 小红书ID = linkElement.substring(linkElement.lastIndexOf('-') + 1);
  1577. if (小红书ID != "") {
  1578. var xiazai = 本地下载按钮元素创建();
  1579. 下载记录查询(小红书ID, xiazai);
  1580. xiazai.className = "loca_download";
  1581. element.appendChild(xiazai);
  1582. xiazai.addEventListener("click", function () {
  1583. xiazai.setAttribute('down_status', 'wait');
  1584. outerLoop: for (var i = 0; i < globalJSON.length; i++) {
  1585. const comments = globalJSON[i]?.data?.comments;
  1586. if (comments) {
  1587. for (var j = 0; j < comments.length; j++) {
  1588. if (comments[j].id === 小红书ID) {
  1589. let url, 文件名, 文件名ID, 用户名, 用户id, 文件类型, 文案, json;
  1590. url = comments[j].pictures?.[0]?.url_default;
  1591. if (url) {
  1592. 文件名ID = url.match(/([^/]+)$/)[0].split('!')[0]
  1593. 文案 = comments[j].content;
  1594. 用户名 = comments[j].user_info?.nickname;
  1595. 用户id = comments[j].user_info?.user_id;
  1596. 文件名 = 用户名 + '----' + comments[j].note_id + '----{评论}' + 文案 + '----' + 用户id + '----' + 文件名ID + '----' + 时间转换(comments[j].create_time) + '(1).png'
  1597. 文件名 = 文件名.replace(/[\\/:*?"<>|,;#%]/g, '');
  1598. 文件类型 = 'png';
  1599. mediajs.push({ 用户名: 用户名, 文章ID: 小红书ID, 文件ID: 文件名ID, url: mediaurl, url2: mediaurl2, url3: mediaurl3, 文件名: 文件名.replace(/[\\/:*?"<>|,;#%]/g, ''), 文件类型: 文件类型 });
  1600.  
  1601. json = JSON数据(url, 文件名ID, 文案, 用户名, 用户id, 文件保存目录, 文件名, 文件类型, 小红书ID)
  1602. 评论图片下载(json, 0, xiazai);
  1603. break outerLoop; // 退出外部循环
  1604. }
  1605. }
  1606. const sub_comments = comments[j].sub_comments;
  1607. for (var k = 0; k < sub_comments.length; k++) {
  1608. let url, 文件名, 文件名ID, 用户名, 用户id, 文件类型, 文案, json;
  1609. if (sub_comments[k].id === 小红书ID) {
  1610. url = sub_comments[k]?.pictures?.[0]?.url_default
  1611. if (url) {
  1612. url = sub_comments[k].pictures?.[0]?.url_default;
  1613. 文件名ID = url.match(/([^/]+)$/)[0]?.split('!')[0];
  1614. 文案 = sub_comments[k].content;
  1615. 用户名 = sub_comments[k].user_info?.nickname;
  1616. 用户id = sub_comments[k].user_info?.user_id;
  1617. 文件名 = 用户名 + '----' + sub_comments[k].note_id + '----{评论}' + 文案 + '----' + 用户id + '----' + 文件名ID + '----' + 时间转换(sub_comments[k].create_time + '(1).png')
  1618. 文件名 = 文件名.replace(/[\\/:*?"<>|,;#%]/g, '');
  1619. 文件类型 = 'png';
  1620. json = JSON数据(url, 文件名ID, 文案, 用户名, 用户id, 文件保存目录, 文件名, 文件类型, 小红书ID);
  1621. 评论图片下载(json, 0, xiazai);
  1622. break outerLoop; // 退出外部循环
  1623. }
  1624. }
  1625. }
  1626. }
  1627. }
  1628. }
  1629.  
  1630. })
  1631. }
  1632. }
  1633.  
  1634.  
  1635. }
  1636. // 在这里执行你的逻辑操作
  1637. });
  1638. }
  1639.  
  1640. function 评论框(authorWrapperElements) {
  1641. // 遍历匹配的元素列表
  1642. authorWrapperElements.forEach(function (element) {
  1643. element.querySelector('.chat-wrapper')?.remove();
  1644. //创建本地解析笔记按钮
  1645. if (!element.querySelector('.meiti_button')) {
  1646. let 小红书ID = window.location.href.match(/\b[A-Fa-f0-9]{16,}\b/g)[0];
  1647. var xiazai = 本地下载按钮元素创建();
  1648. xiazai.className = "meiti_button loca_download";
  1649. 下载记录查询(小红书ID, xiazai);
  1650. // 添加鼠标点击事件
  1651. xiazai.addEventListener("click", function () {
  1652. xiazai.setAttribute('down_status', 'wait');
  1653. 访问获取笔记源码(小红书ID, 0, xiazai);
  1654. })
  1655. element.querySelector('.share-wrapper').appendChild(xiazai);
  1656. //创建发送笔记URL至网页文件下载按钮
  1657. var meiti_send_button = document.createElement('div');
  1658. meiti_send_button.className = "meiti_send_button";
  1659. meiti_send_button.style.margin = '5px';
  1660. meiti_send_button.style.display = 'inline-block';
  1661. meiti_send_button.style.verticalAlign = 'middle';
  1662. 下载记录查询2(小红书ID, meiti_send_button);
  1663. // 添加鼠标点击事件
  1664. meiti_send_button.addEventListener("click", function () {
  1665. let initialStateText = JSON.stringify(unsafeWindow.__INITIAL_STATE__);
  1666. 笔记源码分析(initialStateText, 1, meiti_send_button);
  1667. })
  1668. element.querySelector('.share-wrapper').appendChild(meiti_send_button);
  1669. }
  1670. });
  1671. }
  1672.  
  1673. function JSON数据(url, url2, url3, 文件名ID, 文案, 用户名, 用户id, 目录, 文件名, 文件类型, 小红书ID, prvurl, dfturl) {
  1674. let json = {
  1675. url: url,
  1676. url2: url2,
  1677. url3: url3,
  1678. 文件名ID: 文件名ID,
  1679. 文案: 文案,
  1680. 用户名: 用户名,
  1681. 用户id: 用户id,
  1682. 目录: 目录,
  1683. 文件名: 文件名,
  1684. 文件类型: 文件类型,
  1685. 小红书ID: 小红书ID,
  1686. prvurl: prvurl,
  1687. dfturl: dfturl
  1688. };
  1689. return JSON.stringify(json);
  1690. }
  1691.  
  1692. function 下载记录查询(小红书ID, 按钮_参数) {
  1693. var mediajson = localStorage.getItem('mediacompleted');
  1694. if (!mediajson) {
  1695. mediajson = [];
  1696. } else {
  1697. mediajson = JSON.parse(mediajson);
  1698. }
  1699. if (mediajson.includes(小红书ID)) {
  1700. 按钮_参数.setAttribute('down_status', 'success');
  1701. } else {
  1702. 按钮_参数.setAttribute('down_status', 'download');
  1703. }
  1704. }
  1705. function 下载记录查询2(小红书ID, 按钮_参数) {
  1706. var mediajson = localStorage.getItem('mediacompleted2');
  1707. if (!mediajson) {
  1708. mediajson = [];
  1709. } else {
  1710. mediajson = JSON.parse(mediajson);
  1711. }
  1712. if (mediajson.includes(小红书ID)) {
  1713. 按钮_参数.setAttribute('down_status', 'success');
  1714. } else {
  1715. 按钮_参数.setAttribute('down_status', 'download');
  1716. }
  1717. }
  1718. function 添加成功下载文件(推文ID) {
  1719. var mediajson = localStorage.getItem('mediacompleted');
  1720. if (!mediajson) {
  1721. mediajson = [];
  1722. } else {
  1723. mediajson = JSON.parse(mediajson);
  1724. }
  1725. mediajson.push(推文ID);
  1726. localStorage.setItem('mediacompleted', JSON.stringify(mediajson));
  1727. }
  1728. function 添加成功下载文件2(推文ID) {
  1729. var mediajson = localStorage.getItem('mediacompleted2');
  1730. if (!mediajson) {
  1731. mediajson = [];
  1732. } else {
  1733. mediajson = JSON.parse(mediajson);
  1734. }
  1735. mediajson.push(推文ID);
  1736. localStorage.setItem('mediacompleted2', JSON.stringify(mediajson));
  1737. }
  1738. // function 发送数据置下载服务端(post){
  1739. // var xhr = new XMLHttpRequest();
  1740. // xhr.open('POST', 服务器地址, false); // 第三个参数设置为false表示同步请求
  1741. // xhr.send(post);
  1742.  
  1743. // if (xhr.status === 200) {
  1744. // console.log(xhr.responseText);
  1745. // } else {
  1746. // console.log('请求失败:' + xhr.status);
  1747. // }
  1748. // }
  1749. function 本地下载按钮元素创建() {
  1750. var 下载 = document.createElement('div');
  1751. var 下载按钮SVG = `<div class='背景'><svg viewBox="0 0 24 24" style="width: 18px; height: 18px;">
  1752. <g class="download"><path d="M3,14 v5 q0,2 2,2 h14 q2,0 2,-2 v-5 M7,10 l4,4 q1,1 2,0 l4,-4 M12,3 v11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path></g>
  1753. <g class="completed"><path d="M3,14 v5 q0,2 2,2 h14 q2,0 2,-2 v-5 M7,10 l3,4 q1,1 2,0 l8,-11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path></g>
  1754. <g class="loading"><circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="4" opacity="0.4"></circle><path d="M12,2 a10,10 0 0 1 10,10" fill="none" stroke="#1DA1F2" stroke-width="4" stroke-linecap="round"></path></g>
  1755. <g class="failed"><circle cx="12" cy="12" r="11" fill="#f33" stroke="currentColor" stroke-width="2" opacity="0.8"></circle><path d="M14,5 a1,1 0 0 0 -4,0 l0.5,9.5 a1.5,1.5 0 0 0 3,0 z M12,17 a2,2 0 0 0 0,4 a2,2 0 0 0 0,-4" fill="#fff" stroke="none"></path></g>
  1756. </svg></div>`
  1757. 下载.innerHTML = 下载按钮SVG;
  1758. return 下载;
  1759. }
  1760.  
  1761. function 时间转换(timestamp) {
  1762. timestamp.toString().padEnd(13, '0');
  1763. // 创建一个新的 Date 对象并使用时间戳初始化它
  1764. var date = new Date(timestamp);
  1765. // 使用 Date 对象的方法获取年、月、日、时、分和秒
  1766. var year = date.getFullYear();
  1767. var month = ('0' + (date.getMonth() + 1)).slice(-2);
  1768. var day = ('0' + date.getDate()).slice(-2);
  1769. var hours = ('0' + date.getHours()).slice(-2);
  1770. var minutes = ('0' + date.getMinutes()).slice(-2);
  1771. var seconds = ('0' + date.getSeconds()).slice(-2);
  1772. // 组合成所需的日期时间格式
  1773. return year + '-' + month + '-' + day + ' ' + hours + ' ' + minutes + ' ' + seconds;
  1774. }
  1775. //复制内容到剪辑版
  1776. function copyToClipboard(text) {
  1777. var textarea = document.createElement('textarea');
  1778. textarea.value = text;
  1779. document.body.appendChild(textarea);
  1780. textarea.select();
  1781. document.execCommand('copy');
  1782. document.body.removeChild(textarea);
  1783. }
  1784. function 请求服务端下载(jsonData, 按钮) {
  1785. get(服务器地址, jsonData, function (response) {
  1786. console.log(response);
  1787. let retrunjson = JSON.parse(response);
  1788. if (retrunjson.下载.网络访问) {
  1789. showToast("评论图片:" + retrunjson.用户名 + '--' + retrunjson.小红书ID + '--' + retrunjson.下载.网络访问)
  1790. 按钮.setAttribute('down_status', 'fail')
  1791. } else {
  1792. showToast("评论图片:" + retrunjson.用户名 + '--' + retrunjson.小红书ID + '--' + retrunjson.下载.下载状态)
  1793. 按钮.setAttribute('down_status', 'fail')
  1794. }
  1795.  
  1796. });
  1797. }
  1798.  
  1799.  
  1800. function showToast(message, isError) {
  1801. // 创建新的提示框
  1802. const toastContainer = document.createElement('div');
  1803. // 设置样式属性
  1804. toastContainer.style.position = 'fixed';
  1805. toastContainer.style.justifyContent = 'center';
  1806. toastContainer.style.top = '30%';
  1807. toastContainer.style.left = '50%';
  1808. toastContainer.style.width = '65vw';
  1809. toastContainer.style.margin = '10px 0 0 0';
  1810. toastContainer.style.transform = 'translate(-50%, -50%)';
  1811. toastContainer.style.display = 'flex';
  1812. toastContainer.style.padding = '5px';
  1813. toastContainer.style.fontSize = '20px';
  1814. toastContainer.style.background = '#e7f4ff';
  1815. toastContainer.style.zIndex = '999';
  1816. toastContainer.style.borderRadius = '15px';
  1817. toastContainer.classList.add('PopupMessage'); // 设置 class 名称为 PopupMessage
  1818. // 根据是否为错误提示框添加不同的样式
  1819. if (isError) {
  1820. toastContainer.classList.add('success');
  1821. toastContainer.style.color = '#3fc91d';
  1822. } else {
  1823. toastContainer.classList.add('error');
  1824. toastContainer.style.color = '#CC5500';
  1825. }
  1826. // 将提示框添加到页面中
  1827. document.body.appendChild(toastContainer);
  1828. // 获取页面高度的 20vh
  1829. const windowHeight = window.innerHeight;
  1830. //设置最低的高度。
  1831. const height = windowHeight * 0.2;
  1832. // 设置当前提示框的位置
  1833. toastContainer.style.top = `${height}px`;
  1834. // 在页面中插入新的信息
  1835. const toast = document.createElement('div');
  1836. // 使用 <br> 实现换行
  1837. toast.innerHTML = message.replace(/\n/g, '<br>');
  1838. toastContainer.appendChild(toast);
  1839. // 获取所有的弹出信息元素,包括新添加的元素
  1840. const popupMessages = document.querySelectorAll('.PopupMessage');
  1841. // 调整所有提示框的位置
  1842. let offset = 0;
  1843. popupMessages.forEach(popup => {
  1844. if (popup !== toastContainer) {
  1845. popup.style.top = `${parseInt(popup.style.top) - toast.offsetHeight - 5}px`;
  1846. }
  1847. offset += popup.offsetHeight;
  1848. });
  1849. // 在 3 秒后隐藏提示框
  1850. setTimeout(() => {
  1851. toastContainer.classList.add('hide');
  1852. // 过渡动画结束后移除提示框
  1853. setTimeout(() => {
  1854. toastContainer.parentNode.removeChild(toastContainer);
  1855. }, 300);
  1856. }, 3000);
  1857. }
  1858.  
  1859.  
  1860. 监测页面请求();
  1861. function 监测页面请求() {
  1862. // 保存原始的 XMLHttpRequest 对象
  1863. var originalXhrOpen = XMLHttpRequest.prototype.open;
  1864. var originalXhrSend = XMLHttpRequest.prototype.send;
  1865. // 重写 XMLHttpRequest 的 open 方法
  1866. XMLHttpRequest.prototype.open = function (method, url) {
  1867. //console.log('发起网络请求:', method, url);
  1868. // 保存请求URL
  1869. this.__url = url;
  1870. if (阻止更新页面内容 === true) {
  1871. if (url.includes('api/sns/web/v1/homefeed') || url.includes('api/sns/web/v1/search') || url.includes('api/sns/web/v1/note/like/page') || url.includes('/api/sns/web/v2/note/collect/page')) {
  1872. return;
  1873. }
  1874.  
  1875. }
  1876. // 调用原始的 open 方法
  1877. originalXhrOpen.apply(this, arguments);
  1878. };
  1879. // 重写 XMLHttpRequest 的 send 方法
  1880. XMLHttpRequest.prototype.send = function (data) {
  1881. var xhr = this;
  1882. // 监听请求完成事件
  1883. xhr.addEventListener('load', function () {
  1884. console.log('load', xhr.__url)
  1885. // console.log('请求URL:', xhr.__url);
  1886. // console.log('请求头:', xhr.getAllResponseHeaders());
  1887. // console.log('响应内容:', xhr.responseText);
  1888. try {
  1889. 数据判断(xhr.__url, xhr.responseText);
  1890. } catch {
  1891.  
  1892. }
  1893.  
  1894. });
  1895.  
  1896. // 调用原始的 send 方法
  1897. originalXhrSend.apply(this, arguments);
  1898. };
  1899. // 监听 fetch 请求
  1900. if (window.fetch) {
  1901. var originalFetch = window.fetch;
  1902.  
  1903. window.fetch = function (url, options) {
  1904. //console.log('发起网络请求:', url, options);
  1905.  
  1906. // 调用原始的 fetch 方法
  1907. return originalFetch.apply(this, arguments)
  1908. .then(function (response) {
  1909. //console.log('响应URL:', response.url);
  1910. //console.log('响应头:', response.headers);
  1911. return response.text().then(function (text) {
  1912. //console.log('响应内容:', text);
  1913. 数据判断(response.url, text);
  1914. return new Response(text, response);
  1915. });
  1916. });
  1917. };
  1918. }
  1919. }
  1920. function 数据判断(url, text) {
  1921. if (url.includes("comment/page") && url.includes("cursor=")) {
  1922. if (url.includes("cursor=&")) {
  1923. globalJSON = [];
  1924. }
  1925. try {
  1926. // 将响应内容解析为 JSON
  1927. const jsonResponse = JSON.parse(text);
  1928. // 将 JSON 添加到全局变量
  1929. globalJSON.push(jsonResponse);
  1930. } catch (error) {
  1931. console.log("解析响应内容为 JSON 时出错:", error);
  1932. }
  1933. } else {
  1934. if (url.includes('comment/sub/page')) {
  1935. try {
  1936. // 将响应内容解析为 JSON
  1937. const jsonResponse = JSON.parse(text);
  1938. // 将 JSON 添加到全局变量
  1939. subJSON.push(jsonResponse);
  1940. console.log("评论包", subJSON);
  1941. } catch (error) {
  1942. console.log("解析响应内容为 JSON 时出错:", error);
  1943. }
  1944. } else {
  1945. if (url.includes('explore/')) {
  1946. try {
  1947. 页面源码 = text;
  1948. console.log("监测页面源码", subJSON);
  1949. } catch (error) {
  1950. console.log("解析响应内容为 JSON 时出错:", error);
  1951. }
  1952. }
  1953. }
  1954. }
  1955. }
  1956.  
  1957. // //网络请求监测
  1958. // (function () {
  1959. // // 重写 XMLHttpRequest 对象的 open 方法
  1960. // var realOpen = XMLHttpRequest.prototype.open;
  1961. // XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
  1962. // realOpen.apply(this, arguments);
  1963. // };
  1964.  
  1965. // // 重写 XMLHttpRequest 对象的 send 方法
  1966. // var realSend = XMLHttpRequest.prototype.send;
  1967. // XMLHttpRequest.prototype.send = function (data) {
  1968. // var self = this;
  1969. // var onload = this.onload;
  1970. // this.onload = function () {
  1971. // var url = self.responseURL;
  1972. // console.log('链接',url)
  1973. // // 判断 URL 是否包含 "comment/page" 和 "cursor="
  1974. // if (url.includes("comment/page") && url.includes("cursor=")) {
  1975. // if (url.includes("cursor=&")) {
  1976. // globalJSON = [];
  1977. // }
  1978. // try {
  1979. // // 将响应内容解析为 JSON
  1980. // const jsonResponse = JSON.parse(self.responseText);
  1981. // // 将 JSON 添加到全局变量
  1982. // globalJSON.push(jsonResponse);
  1983. // } catch (error) {
  1984. // console.log("解析响应内容为 JSON 时出错:", error);
  1985. // }
  1986. // } else {
  1987. // if (url.includes('comment/sub/page')) {
  1988. // try {
  1989. // // 将响应内容解析为 JSON
  1990. // const jsonResponse = JSON.parse(self.responseText);
  1991. // // 将 JSON 添加到全局变量
  1992. // subJSON.push(jsonResponse);
  1993. // console.log("评论包", subJSON);
  1994. // } catch (error) {
  1995. // console.log("解析响应内容为 JSON 时出错:", error);
  1996. // }
  1997. // }else{
  1998. // if (url.includes('explore/')) {
  1999. // try {
  2000. // 页面源码=self.responseText;
  2001. // console.log("页面源码", subJSON);
  2002. // } catch (error) {
  2003. // console.log("解析响应内容为 JSON 时出错:", error);
  2004. // }
  2005. // }
  2006. // }
  2007. // }
  2008.  
  2009. // if (typeof onload === 'function') {
  2010. // onload.apply(self, arguments);
  2011. // }
  2012. // };
  2013. // realSend.apply(this, arguments);
  2014. // };
  2015.  
  2016. // })()
  2017.  
  2018. //元素移动函数
  2019. function dragMenu(menuObj, e) {
  2020. e = e ? e : window.event;
  2021. if (e.target !== menuObj) {
  2022. return; // 如果点击的不是父元素本身,则不执行拖动操作
  2023. }
  2024. // || e.target.tagName === 'BUTTON' 判断是否为按钮元素
  2025. if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT' || e.target.tagName === 'SELECT') {
  2026. return;
  2027. }
  2028. let dragData = {
  2029. startX: e.clientX,
  2030. startY: e.clientY,
  2031. menuLeft: menuObj.offsetLeft,
  2032. menuTop: menuObj.offsetTop
  2033. };
  2034. document.onmousemove = function (e) { try { dragMenu(menuObj, e); } catch (err) { } };
  2035. document.onmouseup = function (e) { try { stopDrag(menuObj); } catch (err) { } };
  2036. doane(e);
  2037. function stopDrag(menuObj) {
  2038. document.onmousemove = null;
  2039. document.onmouseup = null;
  2040. }
  2041. function doane(e) {
  2042. if (e.stopPropagation) {
  2043. e.stopPropagation();
  2044. } else {
  2045. e.cancelBubble = true;
  2046. }
  2047. if (e.preventDefault) {
  2048. e.preventDefault();
  2049. } else {
  2050. e.returnValue = false;
  2051. }
  2052. }
  2053. document.onmousemove = function (e) {
  2054. let mouseX = e.clientX;
  2055. let mouseY = e.clientY;
  2056. let menuLeft = dragData.menuLeft + mouseX - dragData.startX;
  2057. let menuTop = dragData.menuTop + mouseY - dragData.startY;
  2058. menuObj.style.left = menuLeft + 'px';
  2059. menuObj.style.top = menuTop + 'px';
  2060. doane(e);
  2061. }
  2062. }
  2063.  
  2064. // 监测页面请求()
  2065. // function 监测页面请求(){
  2066. // // 拦截 XMLHttpRequest 请求
  2067. // var realXhr = window.XMLHttpRequest;
  2068. // window.XMLHttpRequest = function () {
  2069. // var xhr = new realXhr();
  2070. // var currentUrl; // 声明 currentUrl 变量
  2071. // // 重写 xhr.open 方法
  2072. // console.log('请求链接',currentUrl);
  2073. // var realOpen = xhr.open;
  2074. // xhr.open = function (method, url, async, user, password) {
  2075. // currentUrl = url; // 将当前请求的 URL 赋值给 currentUrl
  2076. // realOpen.apply(this, arguments);
  2077. // };
  2078. // // 重写 xhr.send 方法
  2079. // var realSend = xhr.send;
  2080. // xhr.send = function (data) {
  2081. // this.addEventListener('load', function () {
  2082. // // 判断是否包含有 "comment/page" 和 "cursor=" 的 URL 请求
  2083. // console.log('响应链接',currentUrl);
  2084. // if (currentUrl.includes("comment/page") && currentUrl.includes("cursor=")) {
  2085. // if(currentUrl.includes("cursor=&")){
  2086. // globalJSON = [];
  2087. // }
  2088. // // console.log("响应状态码:", xhr.status);
  2089. // // console.log("响应头:", xhr.getAllResponseHeaders());
  2090. // // console.log("响应体:", xhr.responseText);
  2091. // try {
  2092. // // 将响应内容解析为 JSON
  2093. // const jsonResponse = JSON.parse(xhr.responseText);
  2094. // // 将 JSON 添加到全局变量
  2095. // globalJSON.push(jsonResponse);
  2096. // // var secondMember = globalJSON[1]?.data?.comments[0]?.content;
  2097. // console.log("评论包", jsonResponse);
  2098. // } catch (error) {
  2099. // console.log("解析响应内容为 JSON 时出错:", error);
  2100. // }
  2101. // }
  2102. // //更多回复
  2103. // if (currentUrl.includes("comment/sub/page")) {
  2104. // // console.log("响应状态码:", xhr.status);
  2105. // // console.log("响应头:", xhr.getAllResponseHeaders());
  2106. // // console.log("响应体:", xhr.responseText);
  2107. // try {
  2108. // // 将响应内容解析为 JSON
  2109. // const jsonResponse = JSON.parse(xhr.responseText);
  2110. // // 将 JSON 添加到全局变量
  2111. // subJSON.push(jsonResponse);
  2112. // // var secondMember = globalJSON[1]?.data?.comments[0]?.content;
  2113. // console.log("评论包",jsonResponse);
  2114. // } catch (error) {
  2115. // console.log("解析响应内容为 JSON 时出错:", error);
  2116. // }
  2117. // }
  2118. // });
  2119. // realSend.apply(this, arguments);
  2120. // };
  2121. // // 重写 xhr.setRequestHeader 方法
  2122. // var realSetRequestHeader = xhr.setRequestHeader;
  2123. // xhr.setRequestHeader = function (header, value) {
  2124. // realSetRequestHeader.apply(this, arguments);
  2125. // };
  2126.  
  2127. // // 重写 xhr.getRequestHeader 方法
  2128. // var realGetRequestHeader = xhr.getRequestHeader;
  2129. // xhr.getRequestHeader = function (header) {
  2130. // return realGetRequestHeader.apply(this, arguments);
  2131. // };
  2132.  
  2133. // return xhr;
  2134. // };
  2135. // }
  2136.  
  2137. function 当前页全赞() {
  2138. var collection = document.querySelectorAll('[class="reds-icon like-icon"] use');
  2139. for (let name of collection) {
  2140. if (name.getAttribute('xlink:href') === '#like') {
  2141. name.parentElement.parentElement.click();
  2142. }
  2143. console.log();
  2144. }
  2145. }

QingJ © 2025

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