91-plus

多线程下载91porn视频,跳过广告,清除页面广告。

اعتبارا من 07-09-2023. شاهد أحدث إصدار.

  1. // ==UserScript==
  2. // @name 91-plus
  3. // @namespace 91porn
  4. // @version 7
  5. // @author uh58fg
  6. // @description 多线程下载91porn视频,跳过广告,清除页面广告。
  7. // @license WTFPL
  8. // @icon https://img-blog.csdnimg.cn/20181221195058594.gif
  9. // @match *91*.com/view_video.php?*
  10. // @require https://cdn.bootcdn.net/ajax/libs/hls.js/8.0.0-beta.3/hls.min.js
  11. // @require https://cdn.bootcdn.net/ajax/libs/dplayer/1.26.0/DPlayer.min.js
  12. // @require https://greasyfork.org/scripts/466106-91-plus-mux-mp4/code/91-plus-mux-mp4.js?version=1189391
  13. // @require https://cdn.bootcdn.net/ajax/libs/vue/3.2.47/vue.runtime.global.prod.min.js
  14. // @grant window.close
  15. // ==/UserScript==
  16.  
  17. (t=>{const e=document.createElement("style");e.dataset.source="vite-plugin-monkey",e.textContent=t,document.head.append(e)})(' .row2[data-v-4785016e]{display:flex;justify-content:flex-end}.big-btn[data-v-4785016e]{position:relative;display:inline-flex;justify-content:center;align-items:center;margin-left:1rem;font-size:1.6rem;min-width:10rem;color:#fff;cursor:pointer;border-radius:4px;border:1px solid #eeeeee;background-color:#3d8ac7;opacity:1;transition:.3s all;padding:.5rem 1rem}.big-btn[data-v-4785016e]:hover{opacity:.9}.big-btn.disable[data-v-4785016e]{cursor:not-allowed;background-color:#ddd;color:#000}.m-p-input-container[data-v-4785016e]{display:flex}.m-p-input-container input[data-v-4785016e]{flex:1;display:block;padding:0 1rem;font-size:1.8rem;border-radius:4px;box-shadow:none;color:#fff;background:rgba(239,239,239,.3);border:1px solid #cccccc}.m-p-tips[data-v-4785016e]{width:100%;color:#999;text-align:left;font-style:italic;word-break:break-all}.m-p-tips p[data-v-4785016e]{width:100px;display:inline-block}.m-p-tips.error-tips[data-v-4785016e]{color:#dc5350}.m-p-segment[data-v-4785016e]{text-align:left}.m-p-segment .item[data-v-4785016e]{display:inline-block;margin:10px 6px;width:50px;height:40px;color:#fff;line-height:40px;text-align:center;border-radius:4px;cursor:help;border:solid 1px #eeeeee;background-color:#ddd;transition:.3s all}.m-p-segment .finish[data-v-4785016e]{background-color:#0acd76}.m-p-segment .error[data-v-4785016e]{cursor:pointer;background-color:#dc5350}.m-p-segment .error[data-v-4785016e]:hover{opacity:.9}.error-btns[data-v-4785016e]{display:flex;justify-content:flex-end;gap:1rem}.m-p-force[data-v-4785016e],.m-p-retry[data-v-4785016e]{margin-top:1rem;right:50px;display:inline-block;padding:6px 12px;font-size:18px;color:#fff;cursor:pointer;border-radius:4px;border:1px solid #eeeeee;background-color:#3d8ac7;opacity:1;transition:.3s all}.m-p-retry[data-v-4785016e]{right:250px}.m-p-force[data-v-4785016e]:hover,.m-p-retry[data-v-4785016e]:hover{opacity:.9}.info[data-v-4785016e]{display:flex;justify-content:center;align-items:center;gap:1rem;margin-bottom:1rem}.info .options[data-v-4785016e]{margin-right:1rem;display:flex;flex-direction:column;align-items:flex-end}.info .option[data-v-4785016e]{cursor:pointer;display:flex;justify-content:center;align-items:center;color:#fff;margin-bottom:.3rem}.info .option label[data-v-4785016e]{margin:0}.info .option input[data-v-4785016e]{margin:0;margin-left:1rem;width:1.4rem;height:1.4rem}.progress[data-v-4785016e]{flex:1;border-radius:.8rem;opacity:1;height:1.5rem;background:#FCFDFF;box-sizing:border-box;border:.1rem solid #FFFFFF;box-shadow:inset 0 -1rem 4rem #fffc,inset 0 1rem 4rem #2d4c7266;display:flex;align-items:center;justify-content:space-between;position:relative}.progress .bar[data-v-4785016e]{height:100%;width:50%;left:0;top:0;position:absolute;background:linear-gradient(90deg,#9CDCFF 0%,#0ACD76 100%);border-radius:.8rem}[data-v-12ef713a]::-webkit-scrollbar{width:10px}[data-v-12ef713a]::-webkit-scrollbar-thumb{background:#4e4e4e;border-radius:25px}.showBtn[data-v-12ef713a]{cursor:pointer;position:fixed;top:1rem;right:1rem;color:#fff;z-index:99999;background:gray;padding:.6rem 1rem;border-radius:.3rem}.content[data-v-12ef713a]{z-index:9999;background:#0f0f0f;position:fixed;left:0;top:0;width:100vw;min-width:100vw;height:100vh;box-sizing:border-box;padding:1rem;display:flex;gap:2rem;text-align:start}.content .close[data-v-12ef713a]{cursor:pointer;position:absolute;top:1rem;right:2rem;width:2rem;height:2rem;line-height:4rem;text-align:center;color:#fff}.content .close[data-v-12ef713a]:before{position:absolute;content:"";width:.2rem;height:2rem;background:white;transform:rotate(45deg);top:calc(50% - .45rem);left:50%}.content .close[data-v-12ef713a]:after{content:"";position:absolute;width:.2rem;height:2rem;background:white;transform:rotate(-45deg);top:calc(50% - .45rem);left:50%}.content .home-icon[data-v-12ef713a]{cursor:pointer;right:2rem;position:absolute}.content .big-title[data-v-12ef713a]{position:relative;display:flex;align-items:center;justify-content:center;height:5rem;font-weight:700;color:#fff;letter-spacing:2px;background:#212121;font-size:2rem}.content .video[data-v-12ef713a]{width:60%;overflow:auto;padding-bottom:2rem}.content .video #dplayer[data-v-12ef713a]{width:100%}.content .video .title[data-v-12ef713a]{margin-top:1rem;font-weight:700;font-size:2rem;color:#fff;margin-bottom:1rem}.content .video .author[data-v-12ef713a]{margin-right:2rem;font-weight:700;font-size:1.4rem;margin-bottom:2rem;display:inline-block}.content .left[data-v-12ef713a]{flex:1;border:1px solid gray;border-radius:.5rem;overflow:hidden;position:relative}.content .left .comments[data-v-12ef713a]{color:#fff;height:calc(100% - 6rem);padding-bottom:4rem;overflow:auto}.content .left .comments .item[data-v-12ef713a]{margin-bottom:5px;padding:10px;border-bottom:1px solid #3f3f3f;text-align:start}.content .left .comments .item .title[data-v-12ef713a]{font-size:1rem}.content .left .comments .item .title span[data-v-12ef713a]{margin-right:10px}.content .left .comments .item .quote[data-v-12ef713a]{margin-top:.5rem;font-size:1rem;padding:.5rem .5rem .5rem 1rem;border:1px dashed gray}.content .left .comments .item .replay[data-v-12ef713a]{margin-top:1rem}.content .right[data-v-12ef713a]{flex:1;border:1px solid gray;border-radius:.5rem;overflow:hidden}.content .right .list[data-v-12ef713a]{color:#fff;height:calc(100% - 6rem);padding:1rem 1rem 4rem;overflow:auto}.ml20[data-v-12ef713a]{margin-left:20px} ');
  18.  
  19. (function (vue) {
  20. 'use strict';
  21.  
  22. var _monkeyWindow = /* @__PURE__ */ (() => window)();
  23. const _export_sfc = (sfc, props) => {
  24. const target = sfc.__vccOpts || sfc;
  25. for (const [key, val] of props) {
  26. target[key] = val;
  27. }
  28. return target;
  29. };
  30. function removePadding(buffer) {
  31. const outputBytes = buffer.byteLength;
  32. const paddingBytes = outputBytes && new DataView(buffer).getUint8(outputBytes - 1);
  33. if (paddingBytes) {
  34. return buffer.slice(0, outputBytes - paddingBytes);
  35. } else {
  36. return buffer;
  37. }
  38. }
  39. function AESDecryptor() {
  40. return {
  41. constructor() {
  42. this.rcon = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54];
  43. this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)];
  44. this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)];
  45. this.sBox = new Uint32Array(256);
  46. this.invSBox = new Uint32Array(256);
  47. this.key = new Uint32Array(0);
  48. this.initTable();
  49. },
  50. // Using view.getUint32() also swaps the byte order.
  51. uint8ArrayToUint32Array_(arrayBuffer) {
  52. let view = new DataView(arrayBuffer);
  53. let newArray = new Uint32Array(4);
  54. for (let i = 0; i < 4; i++) {
  55. newArray[i] = view.getUint32(i * 4);
  56. }
  57. return newArray;
  58. },
  59. initTable() {
  60. let sBox = this.sBox;
  61. let invSBox = this.invSBox;
  62. let subMix = this.subMix;
  63. let subMix0 = subMix[0];
  64. let subMix1 = subMix[1];
  65. let subMix2 = subMix[2];
  66. let subMix3 = subMix[3];
  67. let invSubMix = this.invSubMix;
  68. let invSubMix0 = invSubMix[0];
  69. let invSubMix1 = invSubMix[1];
  70. let invSubMix2 = invSubMix[2];
  71. let invSubMix3 = invSubMix[3];
  72. let d = new Uint32Array(256);
  73. let x = 0;
  74. let xi = 0;
  75. let i = 0;
  76. for (i = 0; i < 256; i++) {
  77. if (i < 128) {
  78. d[i] = i << 1;
  79. } else {
  80. d[i] = i << 1 ^ 283;
  81. }
  82. }
  83. for (i = 0; i < 256; i++) {
  84. let sx = xi ^ xi << 1 ^ xi << 2 ^ xi << 3 ^ xi << 4;
  85. sx = sx >>> 8 ^ sx & 255 ^ 99;
  86. sBox[x] = sx;
  87. invSBox[sx] = x;
  88. let x2 = d[x];
  89. let x4 = d[x2];
  90. let x8 = d[x4];
  91. let t = d[sx] * 257 ^ sx * 16843008;
  92. subMix0[x] = t << 24 | t >>> 8;
  93. subMix1[x] = t << 16 | t >>> 16;
  94. subMix2[x] = t << 8 | t >>> 24;
  95. subMix3[x] = t;
  96. t = x8 * 16843009 ^ x4 * 65537 ^ x2 * 257 ^ x * 16843008;
  97. invSubMix0[sx] = t << 24 | t >>> 8;
  98. invSubMix1[sx] = t << 16 | t >>> 16;
  99. invSubMix2[sx] = t << 8 | t >>> 24;
  100. invSubMix3[sx] = t;
  101. if (!x) {
  102. x = xi = 1;
  103. } else {
  104. x = x2 ^ d[d[d[x8 ^ x2]]];
  105. xi ^= d[d[xi]];
  106. }
  107. }
  108. },
  109. expandKey(keyBuffer) {
  110. let key = this.uint8ArrayToUint32Array_(keyBuffer);
  111. let sameKey = true;
  112. let offset = 0;
  113. while (offset < key.length && sameKey) {
  114. sameKey = key[offset] === this.key[offset];
  115. offset++;
  116. }
  117. if (sameKey) {
  118. return;
  119. }
  120. this.key = key;
  121. let keySize = this.keySize = key.length;
  122. if (keySize !== 4 && keySize !== 6 && keySize !== 8) {
  123. throw new Error("Invalid aes key size=" + keySize);
  124. }
  125. let ksRows = this.ksRows = (keySize + 6 + 1) * 4;
  126. let ksRow;
  127. let invKsRow;
  128. let keySchedule = this.keySchedule = new Uint32Array(ksRows);
  129. let invKeySchedule = this.invKeySchedule = new Uint32Array(ksRows);
  130. let sbox = this.sBox;
  131. let rcon = this.rcon;
  132. let invSubMix = this.invSubMix;
  133. let invSubMix0 = invSubMix[0];
  134. let invSubMix1 = invSubMix[1];
  135. let invSubMix2 = invSubMix[2];
  136. let invSubMix3 = invSubMix[3];
  137. let prev;
  138. let t;
  139. for (ksRow = 0; ksRow < ksRows; ksRow++) {
  140. if (ksRow < keySize) {
  141. prev = keySchedule[ksRow] = key[ksRow];
  142. continue;
  143. }
  144. t = prev;
  145. if (ksRow % keySize === 0) {
  146. t = t << 8 | t >>> 24;
  147. t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 255] << 16 | sbox[t >>> 8 & 255] << 8 | sbox[t & 255];
  148. t ^= rcon[ksRow / keySize | 0] << 24;
  149. } else if (keySize > 6 && ksRow % keySize === 4) {
  150. t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 255] << 16 | sbox[t >>> 8 & 255] << 8 | sbox[t & 255];
  151. }
  152. keySchedule[ksRow] = prev = (keySchedule[ksRow - keySize] ^ t) >>> 0;
  153. }
  154. for (invKsRow = 0; invKsRow < ksRows; invKsRow++) {
  155. ksRow = ksRows - invKsRow;
  156. if (invKsRow & 3) {
  157. t = keySchedule[ksRow];
  158. } else {
  159. t = keySchedule[ksRow - 4];
  160. }
  161. if (invKsRow < 4 || ksRow <= 4) {
  162. invKeySchedule[invKsRow] = t;
  163. } else {
  164. invKeySchedule[invKsRow] = invSubMix0[sbox[t >>> 24]] ^ invSubMix1[sbox[t >>> 16 & 255]] ^ invSubMix2[sbox[t >>> 8 & 255]] ^ invSubMix3[sbox[t & 255]];
  165. }
  166. invKeySchedule[invKsRow] = invKeySchedule[invKsRow] >>> 0;
  167. }
  168. },
  169. // Adding this as a method greatly improves performance.
  170. networkToHostOrderSwap(word) {
  171. return word << 24 | (word & 65280) << 8 | (word & 16711680) >> 8 | word >>> 24;
  172. },
  173. decrypt(inputArrayBuffer, offset, aesIV, removePKCS7Padding) {
  174. let nRounds = this.keySize + 6;
  175. let invKeySchedule = this.invKeySchedule;
  176. let invSBOX = this.invSBox;
  177. let invSubMix = this.invSubMix;
  178. let invSubMix0 = invSubMix[0];
  179. let invSubMix1 = invSubMix[1];
  180. let invSubMix2 = invSubMix[2];
  181. let invSubMix3 = invSubMix[3];
  182. let initVector = this.uint8ArrayToUint32Array_(aesIV);
  183. let initVector0 = initVector[0];
  184. let initVector1 = initVector[1];
  185. let initVector2 = initVector[2];
  186. let initVector3 = initVector[3];
  187. let inputInt32 = new Int32Array(inputArrayBuffer);
  188. let outputInt32 = new Int32Array(inputInt32.length);
  189. let t0, t1, t2, t3;
  190. let s0, s1, s2, s3;
  191. let inputWords0, inputWords1, inputWords2, inputWords3;
  192. let ksRow, i;
  193. let swapWord = this.networkToHostOrderSwap;
  194. while (offset < inputInt32.length) {
  195. inputWords0 = swapWord(inputInt32[offset]);
  196. inputWords1 = swapWord(inputInt32[offset + 1]);
  197. inputWords2 = swapWord(inputInt32[offset + 2]);
  198. inputWords3 = swapWord(inputInt32[offset + 3]);
  199. s0 = inputWords0 ^ invKeySchedule[0];
  200. s1 = inputWords3 ^ invKeySchedule[1];
  201. s2 = inputWords2 ^ invKeySchedule[2];
  202. s3 = inputWords1 ^ invKeySchedule[3];
  203. ksRow = 4;
  204. for (i = 1; i < nRounds; i++) {
  205. t0 = invSubMix0[s0 >>> 24] ^ invSubMix1[s1 >> 16 & 255] ^ invSubMix2[s2 >> 8 & 255] ^ invSubMix3[s3 & 255] ^ invKeySchedule[ksRow];
  206. t1 = invSubMix0[s1 >>> 24] ^ invSubMix1[s2 >> 16 & 255] ^ invSubMix2[s3 >> 8 & 255] ^ invSubMix3[s0 & 255] ^ invKeySchedule[ksRow + 1];
  207. t2 = invSubMix0[s2 >>> 24] ^ invSubMix1[s3 >> 16 & 255] ^ invSubMix2[s0 >> 8 & 255] ^ invSubMix3[s1 & 255] ^ invKeySchedule[ksRow + 2];
  208. t3 = invSubMix0[s3 >>> 24] ^ invSubMix1[s0 >> 16 & 255] ^ invSubMix2[s1 >> 8 & 255] ^ invSubMix3[s2 & 255] ^ invKeySchedule[ksRow + 3];
  209. s0 = t0;
  210. s1 = t1;
  211. s2 = t2;
  212. s3 = t3;
  213. ksRow = ksRow + 4;
  214. }
  215. t0 = invSBOX[s0 >>> 24] << 24 ^ invSBOX[s1 >> 16 & 255] << 16 ^ invSBOX[s2 >> 8 & 255] << 8 ^ invSBOX[s3 & 255] ^ invKeySchedule[ksRow];
  216. t1 = invSBOX[s1 >>> 24] << 24 ^ invSBOX[s2 >> 16 & 255] << 16 ^ invSBOX[s3 >> 8 & 255] << 8 ^ invSBOX[s0 & 255] ^ invKeySchedule[ksRow + 1];
  217. t2 = invSBOX[s2 >>> 24] << 24 ^ invSBOX[s3 >> 16 & 255] << 16 ^ invSBOX[s0 >> 8 & 255] << 8 ^ invSBOX[s1 & 255] ^ invKeySchedule[ksRow + 2];
  218. t3 = invSBOX[s3 >>> 24] << 24 ^ invSBOX[s0 >> 16 & 255] << 16 ^ invSBOX[s1 >> 8 & 255] << 8 ^ invSBOX[s2 & 255] ^ invKeySchedule[ksRow + 3];
  219. ksRow = ksRow + 3;
  220. outputInt32[offset] = swapWord(t0 ^ initVector0);
  221. outputInt32[offset + 1] = swapWord(t3 ^ initVector1);
  222. outputInt32[offset + 2] = swapWord(t2 ^ initVector2);
  223. outputInt32[offset + 3] = swapWord(t1 ^ initVector3);
  224. initVector0 = inputWords0;
  225. initVector1 = inputWords1;
  226. initVector2 = inputWords2;
  227. initVector3 = inputWords3;
  228. offset = offset + 4;
  229. }
  230. return removePKCS7Padding ? removePadding(outputInt32.buffer) : outputInt32.buffer;
  231. },
  232. destroy() {
  233. this.key = void 0;
  234. this.keySize = void 0;
  235. this.ksRows = void 0;
  236. this.sBox = void 0;
  237. this.invSBox = void 0;
  238. this.subMix = void 0;
  239. this.invSubMix = void 0;
  240. this.keySchedule = void 0;
  241. this.invKeySchedule = void 0;
  242. this.rcon = void 0;
  243. }
  244. };
  245. }
  246. const _sfc_main$1 = {
  247. name: "downloader",
  248. props: {
  249. url: "",
  250. title: ""
  251. },
  252. data() {
  253. return {
  254. conf: {
  255. autoDownload: true,
  256. autoSave: true,
  257. autoClose: false
  258. },
  259. show: false,
  260. tips: "m3u8 视频在线提取工具",
  261. // 顶部提示
  262. isPause: false,
  263. // 是否暂停下载
  264. isGetMP4: false,
  265. // 是否转码为 MP4 下载
  266. durationSecond: 0,
  267. // 视频持续时长
  268. isShowRefer: false,
  269. // 是否显示推送
  270. downloading: false,
  271. // 是否下载中
  272. beginTime: "",
  273. // 开始下载的时间
  274. errorNum: 0,
  275. // 错误数
  276. finishNum: 0,
  277. // 已下载数
  278. downloadIndex: 0,
  279. // 当前下载片段
  280. finishList: [],
  281. // 下载完成项目
  282. tsUrlList: [],
  283. // ts URL数组
  284. mediaFileList: [],
  285. // 下载的媒体数组
  286. isSupperStreamWrite: _monkeyWindow.streamSaver && !_monkeyWindow.streamSaver.useBlobFallback,
  287. // 当前浏览器是否支持流式下载
  288. streamWriter: null,
  289. // 文件流写入器
  290. streamDownloadIndex: 0,
  291. // 文件流写入器,正准备写入第几个视频片段
  292. rangeDownload: {
  293. // 特定范围下载
  294. isShowRange: false,
  295. // 是否显示范围下载
  296. startSegment: "",
  297. // 起始片段
  298. endSegment: "",
  299. // 截止片段
  300. targetSegment: 1
  301. // 待下载片段
  302. },
  303. aesConf: {
  304. // AES 视频解密配置
  305. method: "",
  306. // 加密算法
  307. uri: "",
  308. // key 所在文件路径
  309. iv: "",
  310. // 偏移值
  311. key: "",
  312. // 秘钥
  313. decryptor: null,
  314. // 解码器对象
  315. stringToBuffer: function(str) {
  316. return new TextEncoder().encode(str);
  317. }
  318. }
  319. };
  320. },
  321. computed: {
  322. progress() {
  323. return (this.finishNum / this.rangeDownload.targetSegment * 100).toFixed(2);
  324. }
  325. },
  326. watch: {
  327. conf: {
  328. handler(n, o) {
  329. localStorage.setItem("porn-plus", JSON.stringify(n));
  330. },
  331. deep: true
  332. },
  333. progress(n, o) {
  334. document.title = Number(n).toFixed(0) + "% " + this.title;
  335. }
  336. },
  337. created() {
  338. this.getConf();
  339. if (this.conf.autoDownload) {
  340. this.getMP4();
  341. }
  342. setInterval(this.retryAll.bind(this), 2e3);
  343. },
  344. methods: {
  345. // ajax 请求
  346. ajax(options) {
  347. options = options || {};
  348. let xhr = new XMLHttpRequest();
  349. if (options.type === "file") {
  350. xhr.responseType = "arraybuffer";
  351. }
  352. xhr.onreadystatechange = function() {
  353. if (xhr.readyState === 4) {
  354. let status = xhr.status;
  355. if (status >= 200 && status < 300) {
  356. options.success && options.success(xhr.response);
  357. } else {
  358. options.fail && options.fail(status);
  359. }
  360. }
  361. };
  362. xhr.open("GET", options.url, true);
  363. xhr.send(null);
  364. },
  365. // 合成URL
  366. applyURL(targetURL, baseURL) {
  367. baseURL = baseURL || location.href;
  368. if (targetURL.indexOf("http") === 0) {
  369. if (location.href.indexOf("https") === 0) {
  370. return targetURL.replace("http://", "https://");
  371. }
  372. return targetURL;
  373. } else if (targetURL[0] === "/") {
  374. let domain = baseURL.split("/");
  375. return domain[0] + "//" + domain[2] + targetURL;
  376. } else {
  377. let domain = baseURL.split("/");
  378. domain.pop();
  379. return domain.join("/") + "/" + targetURL;
  380. }
  381. },
  382. // 使用流式下载,边下载边保存,解决大视频文件内存不足的难题
  383. streamDownload(isMp4) {
  384. this.isGetMP4 = isMp4;
  385. let fileName = this.title || this.formatTime(/* @__PURE__ */ new Date(), "YYYY_MM_DD hh_mm_ss");
  386. this.streamWriter = _monkeyWindow.streamSaver.createWriteStream(`${fileName}.${isMp4 ? "mp4" : "ts"}`).getWriter();
  387. this.getM3U8();
  388. },
  389. // 解析为 mp4 下载
  390. getMP4() {
  391. this.isGetMP4 = true;
  392. this.getM3U8();
  393. },
  394. // 获取在线文件
  395. getM3U8(onlyGetRange) {
  396. if (!this.url) {
  397. alert("请输入链接");
  398. return;
  399. }
  400. if (this.url.toLowerCase().indexOf("m3u8") === -1) {
  401. alert("链接有误,请重新输入");
  402. return;
  403. }
  404. if (this.downloading) {
  405. alert("资源下载中,请稍后");
  406. return;
  407. }
  408. this.tips = "m3u8 文件下载中,请稍后";
  409. this.beginTime = /* @__PURE__ */ new Date();
  410. this.ajax({
  411. url: this.url,
  412. success: (m3u8Str) => {
  413. this.tsUrlList = [];
  414. this.finishList = [];
  415. m3u8Str.split("\n").forEach((item) => {
  416. if (/^[^#]/.test(item)) {
  417. this.tsUrlList.push(this.applyURL(item, this.url));
  418. this.finishList.push({
  419. title: item,
  420. status: ""
  421. });
  422. }
  423. });
  424. if (onlyGetRange) {
  425. this.rangeDownload.isShowRange = true;
  426. this.rangeDownload.endSegment = this.tsUrlList.length;
  427. this.rangeDownload.targetSegment = this.tsUrlList.length;
  428. return;
  429. } else {
  430. let startSegment = Math.max(this.rangeDownload.startSegment || 1, 1);
  431. let endSegment = Math.max(this.rangeDownload.endSegment || this.tsUrlList.length, 1);
  432. startSegment = Math.min(startSegment, this.tsUrlList.length);
  433. endSegment = Math.min(endSegment, this.tsUrlList.length);
  434. this.rangeDownload.startSegment = Math.min(startSegment, endSegment);
  435. this.rangeDownload.endSegment = Math.max(startSegment, endSegment);
  436. this.rangeDownload.targetSegment = this.rangeDownload.endSegment - this.rangeDownload.startSegment + 1;
  437. this.downloadIndex = this.rangeDownload.startSegment - 1;
  438. this.downloading = true;
  439. }
  440. if (this.isGetMP4) {
  441. let infoIndex = 0;
  442. m3u8Str.split("\n").forEach((item) => {
  443. if (item.toUpperCase().indexOf("#EXTINF:") > -1) {
  444. infoIndex++;
  445. if (this.rangeDownload.startSegment <= infoIndex && infoIndex <= this.rangeDownload.endSegment) {
  446. this.durationSecond += parseFloat(item.split("#EXTINF:")[1]);
  447. }
  448. }
  449. });
  450. }
  451. console.log("this.durationSecond", this.durationSecond);
  452. if (m3u8Str.indexOf("#EXT-X-KEY") > -1) {
  453. this.aesConf.method = (m3u8Str.match(/(.*METHOD=([^,\s]+))/) || ["", "", ""])[2];
  454. this.aesConf.uri = (m3u8Str.match(/(.*URI="([^"]+))"/) || ["", "", ""])[2];
  455. this.aesConf.iv = (m3u8Str.match(/(.*IV=([^,\s]+))/) || ["", "", ""])[2];
  456. this.aesConf.iv = this.aesConf.iv ? this.aesConf.stringToBuffer(this.aesConf.iv) : "";
  457. this.aesConf.uri = this.applyURL(this.aesConf.uri, this.url);
  458. this.getAES();
  459. } else if (this.tsUrlList.length > 0) {
  460. this.downloadTS();
  461. } else {
  462. this.alertError("资源为空,请查看链接是否有效");
  463. }
  464. },
  465. fail: () => {
  466. this.alertError("链接不正确,请查看链接是否有效");
  467. }
  468. });
  469. },
  470. // 获取AES配置
  471. getAES() {
  472. this.ajax({
  473. type: "file",
  474. url: this.aesConf.uri,
  475. success: (key) => {
  476. this.aesConf.key = key;
  477. this.aesConf.decryptor = new AESDecryptor();
  478. this.aesConf.decryptor.constructor();
  479. this.aesConf.decryptor.expandKey(this.aesConf.key);
  480. this.downloadTS();
  481. },
  482. fail: () => {
  483. this.alertError("视频已加密,可试用右下角入口的「无差别提取工具」");
  484. }
  485. });
  486. },
  487. // ts 片段的 AES 解码
  488. aesDecrypt(data, index) {
  489. let iv = this.aesConf.iv || new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, index]);
  490. return this.aesConf.decryptor.decrypt(data, 0, iv.buffer || iv, true);
  491. },
  492. // 下载分片
  493. downloadTS() {
  494. this.tips = "ts 视频碎片下载中,请稍后";
  495. let download = () => {
  496. let isPause = this.isPause;
  497. let index = this.downloadIndex;
  498. if (index >= this.rangeDownload.endSegment) {
  499. return;
  500. }
  501. this.downloadIndex++;
  502. if (this.finishList[index] && this.finishList[index].status === "") {
  503. this.finishList[index].status = "downloading";
  504. this.ajax({
  505. url: this.tsUrlList[index],
  506. type: "file",
  507. success: (file) => {
  508. this.dealTS(file, index, () => this.downloadIndex < this.rangeDownload.endSegment && !isPause && download());
  509. },
  510. fail: () => {
  511. this.errorNum++;
  512. this.finishList[index].status = "error";
  513. if (this.downloadIndex < this.rangeDownload.endSegment) {
  514. !isPause && download();
  515. }
  516. }
  517. });
  518. } else if (this.downloadIndex < this.rangeDownload.endSegment) {
  519. !isPause && download();
  520. }
  521. };
  522. for (let i = 0; i < Math.min(6, this.rangeDownload.targetSegment - this.finishNum); i++) {
  523. download();
  524. }
  525. },
  526. // 处理 ts 片段,AES 解密、mp4 转码
  527. dealTS(file, index, callback) {
  528. const data = this.aesConf.uri ? this.aesDecrypt(file, index) : file;
  529. this.conversionMp4(data, index, (afterData) => {
  530. this.mediaFileList[index - this.rangeDownload.startSegment + 1] = afterData;
  531. this.finishList[index].status = "finish";
  532. this.finishNum++;
  533. if (this.streamWriter) {
  534. for (let index2 = this.streamDownloadIndex; index2 < this.mediaFileList.length; index2++) {
  535. if (this.mediaFileList[index2]) {
  536. this.streamWriter.write(new Uint8Array(this.mediaFileList[index2]));
  537. this.mediaFileList[index2] = null;
  538. this.streamDownloadIndex = index2 + 1;
  539. } else {
  540. break;
  541. }
  542. }
  543. if (this.streamDownloadIndex >= this.rangeDownload.targetSegment) {
  544. this.streamWriter.close();
  545. }
  546. } else if (this.finishNum === this.rangeDownload.targetSegment) {
  547. let fileName = this.title || this.formatTime(this.beginTime, "YYYY_MM_DD hh_mm_ss");
  548. this.downloadFile(this.mediaFileList, fileName);
  549. }
  550. callback && callback();
  551. });
  552. },
  553. // 转码为 mp4
  554. conversionMp4(data, index, callback) {
  555. if (this.isGetMP4) {
  556. let transmuxer = new _monkeyWindow.muxjs.Transmuxer({
  557. keepOriginalTimestamps: true,
  558. duration: parseInt(this.durationSecond)
  559. });
  560. transmuxer.on("data", (segment) => {
  561. if (index === this.rangeDownload.startSegment - 1) {
  562. let data2 = new Uint8Array(segment.initSegment.byteLength + segment.data.byteLength);
  563. data2.set(segment.initSegment, 0);
  564. data2.set(segment.data, segment.initSegment.byteLength);
  565. callback(data2.buffer);
  566. } else {
  567. callback(segment.data);
  568. }
  569. });
  570. transmuxer.push(new Uint8Array(data));
  571. transmuxer.flush();
  572. } else {
  573. callback(data);
  574. }
  575. },
  576. // 暂停与恢复
  577. togglePause() {
  578. this.isPause = !this.isPause;
  579. !this.isPause && this.retryAll(true);
  580. },
  581. // 重新下载某个片段
  582. retry(index) {
  583. if (this.finishList[index].status === "error") {
  584. this.finishList[index].status = "";
  585. this.ajax({
  586. url: this.tsUrlList[index],
  587. type: "file",
  588. success: (file) => {
  589. this.errorNum--;
  590. this.dealTS(file, index);
  591. },
  592. fail: () => {
  593. this.finishList[index].status = "error";
  594. }
  595. });
  596. }
  597. },
  598. // 重新下载所有错误片段
  599. retryAll(forceRestart) {
  600. if (!this.finishList.length || this.isPause) {
  601. return;
  602. }
  603. let firstErrorIndex = this.downloadIndex;
  604. this.finishList.forEach((item, index) => {
  605. if (item.status === "error") {
  606. item.status = "";
  607. firstErrorIndex = Math.min(firstErrorIndex, index);
  608. }
  609. });
  610. this.errorNum = 0;
  611. if (this.downloadIndex >= this.rangeDownload.endSegment || forceRestart) {
  612. this.downloadIndex = firstErrorIndex;
  613. this.downloadTS();
  614. } else {
  615. this.downloadIndex = firstErrorIndex;
  616. }
  617. },
  618. // 下载整合后的TS文件
  619. downloadFile(fileDataList, fileName, forceSave = false) {
  620. this.getConf();
  621. setTimeout(() => {
  622. document.title = "下载完成 " + this.title;
  623. }, 1e3);
  624. if (!this.conf.autoSave && !forceSave) {
  625. return;
  626. }
  627. this.tips = "ts 碎片整合中,请留意浏览器下载";
  628. let fileBlob = null;
  629. let a = document.createElement("a");
  630. if (this.isGetMP4) {
  631. fileBlob = new Blob(fileDataList, { type: "video/mp4" });
  632. a.download = fileName + ".mp4";
  633. } else {
  634. fileBlob = new Blob(fileDataList, { type: "video/MP2T" });
  635. a.download = fileName + ".ts";
  636. }
  637. a.href = URL.createObjectURL(fileBlob);
  638. a.style.display = "none";
  639. document.body.appendChild(a);
  640. a.click();
  641. a.remove();
  642. setTimeout(() => {
  643. if (this.conf.autoClose) {
  644. window.opener = null;
  645. window.open("", "_self", "");
  646. window.close();
  647. }
  648. }, 1e3);
  649. },
  650. // 格式化时间
  651. formatTime(date, formatStr) {
  652. const formatType = {
  653. Y: date.getFullYear(),
  654. M: date.getMonth() + 1,
  655. D: date.getDate(),
  656. h: date.getHours(),
  657. m: date.getMinutes(),
  658. s: date.getSeconds()
  659. };
  660. return formatStr.replace(
  661. /Y+|M+|D+|h+|m+|s+/g,
  662. (target) => (new Array(target.length).join("0") + formatType[target[0]]).substr(-target.length)
  663. );
  664. },
  665. // 强制下载现有片段
  666. forceDownload() {
  667. if (this.mediaFileList.length) {
  668. let fileName = this.title || this.formatTime(this.beginTime, "YYYY_MM_DD hh_mm_ss");
  669. this.downloadFile(this.mediaFileList, fileName, true);
  670. } else {
  671. alert("当前无已下载片段");
  672. }
  673. },
  674. // 发生错误,进行提示
  675. alertError(tips) {
  676. alert(tips);
  677. this.downloading = false;
  678. this.tips = "m3u8 视频在线提取工具";
  679. },
  680. getConf() {
  681. let confStr = localStorage.getItem("porn-plus");
  682. if (confStr) {
  683. try {
  684. this.conf = JSON.parse(confStr);
  685. } catch (e) {
  686. }
  687. }
  688. }
  689. }
  690. };
  691. const _withScopeId$1 = (n) => (vue.pushScopeId("data-v-4785016e"), n = n(), vue.popScopeId(), n);
  692. const _hoisted_1$1 = { id: "app" };
  693. const _hoisted_2$1 = { class: "info" };
  694. const _hoisted_3$1 = { class: "options" };
  695. const _hoisted_4$1 = { class: "option" };
  696. const _hoisted_5$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("label", { for: "a1" }, "自动下载", -1));
  697. const _hoisted_6$1 = { class: "option" };
  698. const _hoisted_7$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("label", { for: "a2" }, "下载完成后自动保存", -1));
  699. const _hoisted_8$1 = { class: "option" };
  700. const _hoisted_9$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("label", { for: "a3" }, "保存后自动关闭", -1));
  701. const _hoisted_10$1 = {
  702. key: 0,
  703. class: "row2"
  704. };
  705. const _hoisted_11$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("span", null, "保存", -1));
  706. const _hoisted_12$1 = [
  707. _hoisted_11$1
  708. ];
  709. const _hoisted_13$1 = { class: "wrapper" };
  710. const _hoisted_14$1 = { class: "m-p-input-container" };
  711. const _hoisted_15$1 = ["value"];
  712. const _hoisted_16$1 = {
  713. key: 1,
  714. class: "disable big-btn"
  715. };
  716. const _hoisted_17$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("span", null, "保存", -1));
  717. const _hoisted_18$1 = [
  718. _hoisted_17$1
  719. ];
  720. const _hoisted_19 = { class: "error-btns" };
  721. const _hoisted_20 = { class: "m-p-tips" };
  722. const _hoisted_21 = { class: "m-p-segment" };
  723. const _hoisted_22 = ["title", "onClick"];
  724. function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  725. return vue.openBlock(), vue.createElementBlock("section", _hoisted_1$1, [
  726. vue.createElementVNode("div", _hoisted_2$1, [
  727. vue.createElementVNode("div", {
  728. class: "progress",
  729. onClick: _cache[0] || (_cache[0] = ($event) => $data.show = !$data.show)
  730. }, [
  731. vue.createElementVNode("div", {
  732. class: "bar",
  733. style: vue.normalizeStyle({ width: $options.progress + "%" })
  734. }, null, 4)
  735. ]),
  736. vue.createElementVNode("div", _hoisted_3$1, [
  737. vue.createElementVNode("div", _hoisted_4$1, [
  738. _hoisted_5$1,
  739. vue.withDirectives(vue.createElementVNode("input", {
  740. id: "a1",
  741. type: "checkbox",
  742. "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => $data.conf.autoDownload = $event)
  743. }, null, 512), [
  744. [vue.vModelCheckbox, $data.conf.autoDownload]
  745. ])
  746. ]),
  747. vue.createElementVNode("div", _hoisted_6$1, [
  748. _hoisted_7$1,
  749. vue.withDirectives(vue.createElementVNode("input", {
  750. id: "a2",
  751. type: "checkbox",
  752. "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => $data.conf.autoSave = $event)
  753. }, null, 512), [
  754. [vue.vModelCheckbox, $data.conf.autoSave]
  755. ])
  756. ]),
  757. vue.createElementVNode("div", _hoisted_8$1, [
  758. _hoisted_9$1,
  759. vue.withDirectives(vue.createElementVNode("input", {
  760. id: "a3",
  761. type: "checkbox",
  762. "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => $data.conf.autoClose = $event)
  763. }, null, 512), [
  764. [vue.vModelCheckbox, $data.conf.autoClose]
  765. ])
  766. ])
  767. ])
  768. ]),
  769. !$data.show ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_10$1, [
  770. $data.finishNum === $data.rangeDownload.targetSegment && $data.rangeDownload.targetSegment > 0 ? (vue.openBlock(), vue.createElementBlock("div", {
  771. key: 0,
  772. class: "big-btn",
  773. onClick: _cache[4] || (_cache[4] = (...args) => $options.forceDownload && $options.forceDownload(...args))
  774. }, _hoisted_12$1)) : vue.createCommentVNode("", true),
  775. vue.createElementVNode("div", {
  776. class: "big-btn",
  777. onClick: _cache[5] || (_cache[5] = ($event) => $data.show = !$data.show)
  778. }, [
  779. vue.createElementVNode("span", null, vue.toDisplayString(!$data.show ? "展开" : "收起") + "下载详情", 1)
  780. ])
  781. ])) : vue.createCommentVNode("", true),
  782. vue.withDirectives(vue.createElementVNode("div", _hoisted_13$1, [
  783. vue.createElementVNode("section", _hoisted_14$1, [
  784. vue.createElementVNode("input", {
  785. type: "text",
  786. value: $props.url,
  787. disabled: true,
  788. placeholder: "请输入 m3u8 链接"
  789. }, null, 8, _hoisted_15$1),
  790. !$data.downloading ? (vue.openBlock(), vue.createElementBlock("div", {
  791. key: 0,
  792. class: "big-btn",
  793. onClick: _cache[6] || (_cache[6] = (...args) => $options.getMP4 && $options.getMP4(...args))
  794. }, "下载")) : $data.finishNum === $data.rangeDownload.targetSegment && $data.rangeDownload.targetSegment > 0 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_16$1, " 下载完成 ")) : (vue.openBlock(), vue.createElementBlock("div", {
  795. key: 2,
  796. class: "big-btn",
  797. onClick: _cache[7] || (_cache[7] = (...args) => $options.togglePause && $options.togglePause(...args))
  798. }, vue.toDisplayString($data.isPause ? "恢复下载" : "暂停下载"), 1)),
  799. $data.finishNum === $data.rangeDownload.targetSegment && $data.rangeDownload.targetSegment > 0 && !$data.conf.autoSave ? (vue.openBlock(), vue.createElementBlock("div", {
  800. key: 3,
  801. class: "big-btn",
  802. onClick: _cache[8] || (_cache[8] = (...args) => $options.forceDownload && $options.forceDownload(...args))
  803. }, _hoisted_18$1)) : vue.createCommentVNode("", true),
  804. vue.createElementVNode("div", {
  805. class: "big-btn detail",
  806. onClick: _cache[9] || (_cache[9] = ($event) => $data.show = !$data.show)
  807. }, [
  808. vue.createElementVNode("span", null, vue.toDisplayString(!$data.show ? "展开" : "收起") + "下载详情", 1)
  809. ])
  810. ]),
  811. $data.finishList.length > 0 ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [
  812. vue.createElementVNode("div", _hoisted_19, [
  813. $data.errorNum && $data.downloadIndex >= $data.rangeDownload.targetSegment ? (vue.openBlock(), vue.createElementBlock("div", {
  814. key: 0,
  815. class: "m-p-retry",
  816. onClick: _cache[10] || (_cache[10] = (...args) => $options.retryAll && $options.retryAll(...args))
  817. }, " 重新下载错误片段 ")) : vue.createCommentVNode("", true),
  818. $data.mediaFileList.length && !$data.streamWriter ? (vue.openBlock(), vue.createElementBlock("div", {
  819. key: 1,
  820. class: "m-p-force",
  821. onClick: _cache[11] || (_cache[11] = (...args) => $options.forceDownload && $options.forceDownload(...args))
  822. }, "强制下载现有片段 ")) : vue.createCommentVNode("", true)
  823. ]),
  824. vue.createElementVNode("div", _hoisted_20, "待下载碎片总量:" + vue.toDisplayString($data.rangeDownload.targetSegment) + ",已下载:" + vue.toDisplayString($data.finishNum) + ",错误:" + vue.toDisplayString($data.errorNum) + ",进度:" + vue.toDisplayString($options.progress) + "% ", 1),
  825. vue.createElementVNode("div", {
  826. class: vue.normalizeClass(["m-p-tips", [$data.errorNum ? "error-tips" : ""]])
  827. }, " 若某视频碎片下载发生错误,将标记为红色,可点击相应图标进行重试 ", 2),
  828. vue.createElementVNode("section", _hoisted_21, [
  829. (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList($data.finishList, (item, index) => {
  830. return vue.openBlock(), vue.createElementBlock("div", {
  831. class: vue.normalizeClass(["item", [item.status]]),
  832. title: item.title,
  833. onClick: ($event) => $options.retry(index)
  834. }, vue.toDisplayString(index + 1), 11, _hoisted_22);
  835. }), 256))
  836. ])
  837. ], 64)) : vue.createCommentVNode("", true)
  838. ], 512), [
  839. [vue.vShow, $data.show]
  840. ])
  841. ]);
  842. }
  843. const Downloader = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__scopeId", "data-v-4785016e"]]);
  844. const _withScopeId = (n) => (vue.pushScopeId("data-v-12ef713a"), n = n(), vue.popScopeId(), n);
  845. const _hoisted_1 = { class: "content" };
  846. const _hoisted_2 = { class: "left" };
  847. const _hoisted_3 = { class: "big-title" };
  848. const _hoisted_4 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("path", {
  849. d: "M9 18V42H39V18L24 6L9 18Z",
  850. fill: "none",
  851. stroke: "#ffffff",
  852. "stroke-width": "4",
  853. "stroke-linecap": "round",
  854. "stroke-linejoin": "round"
  855. }, null, -1));
  856. const _hoisted_5 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("path", {
  857. d: "M19 29V42H29V29H19Z",
  858. fill: "none",
  859. stroke: "#ffffff",
  860. "stroke-width": "4",
  861. "stroke-linejoin": "round"
  862. }, null, -1));
  863. const _hoisted_6 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("path", {
  864. d: "M9 42H39",
  865. stroke: "#ffffff",
  866. "stroke-width": "4",
  867. "stroke-linecap": "round"
  868. }, null, -1));
  869. const _hoisted_7 = [
  870. _hoisted_4,
  871. _hoisted_5,
  872. _hoisted_6
  873. ];
  874. const _hoisted_8 = { class: "comments" };
  875. const _hoisted_9 = { class: "item" };
  876. const _hoisted_10 = { class: "title" };
  877. const _hoisted_11 = {
  878. key: 0,
  879. class: "quote"
  880. };
  881. const _hoisted_12 = { class: "replay" };
  882. const _hoisted_13 = { class: "video" };
  883. const _hoisted_14 = { class: "title" };
  884. const _hoisted_15 = {
  885. class: "author",
  886. target: "_blank"
  887. };
  888. const _hoisted_16 = ["href"];
  889. const _hoisted_17 = { class: "right" };
  890. const _hoisted_18 = { class: "big-title" };
  891. const _sfc_main = {
  892. __name: "App",
  893. setup(__props) {
  894. const dpRef = vue.ref(null);
  895. const dp = vue.ref(null);
  896. const recommendRef = vue.ref(null);
  897. const data = vue.reactive({
  898. comments: [],
  899. info: window.info,
  900. show: true
  901. });
  902. vue.watch(() => data.show, (n, o) => {
  903. if (n) {
  904. pauseOriginVideo();
  905. document.body.style.overflow = "hidden";
  906. } else {
  907. dp.value.pause();
  908. document.body.style.overflow = "unset";
  909. }
  910. }, { immediate: true });
  911. vue.onMounted(() => {
  912. recommendRef.value.append($("h3").next().next()[0]);
  913. $("br").remove();
  914. initComments();
  915. initVideo();
  916. checkOriginVideoIsPlay();
  917. });
  918. function pauseOriginVideo() {
  919. if ($(".vjs-playing").length) {
  920. $("#player_one_html5_api").click();
  921. }
  922. }
  923. function checkOriginVideoIsPlay() {
  924. setInterval(() => {
  925. if (data.show) {
  926. pauseOriginVideo();
  927. }
  928. }, 3e3);
  929. }
  930. function initComments() {
  931. $.ajax({
  932. url: `${location.origin}/show_comments2.php?VID=${data.info.video.vid}&start=1&comment_per_page=30`,
  933. success(r) {
  934. let h = $(r);
  935. h.each(function() {
  936. if (this.tagName === "TABLE") {
  937. let name = $(this).find("a:first").remove().text().trim();
  938. let time = $(this).find(".comment-info").contents()[4];
  939. let body = $(this).find(".comment-body");
  940. let quote = body.find(".comment_quote").text().replaceAll(" ", "").replaceAll("\n", "");
  941. body.find(".comment_quote").remove();
  942. let replay = body.find("a").remove().end().text().trim();
  943. if (replay.includes("请不要发广告或与视频无关的评论") || quote.includes("请不要发广告或与视频无关的评论"))
  944. ;
  945. else {
  946. data.comments.push({
  947. name,
  948. time: $(time).text(),
  949. quote,
  950. replay
  951. });
  952. }
  953. }
  954. });
  955. }
  956. });
  957. }
  958. function initVideo() {
  959. if (!dpRef.value)
  960. return;
  961. dp.value = new _monkeyWindow.DPlayer({
  962. container: dpRef.value,
  963. autoplay: true,
  964. video: {
  965. url: data.info.video.url,
  966. type: "customHls",
  967. customType: {
  968. customHls: function(video, player) {
  969. const hls = new _monkeyWindow.Hls();
  970. hls.loadSource(video.src);
  971. hls.attachMedia(video);
  972. }
  973. }
  974. }
  975. });
  976. dp.value.seek(10);
  977. }
  978. function goHome() {
  979. location.href = "https://91porn.com/index.php";
  980. }
  981. return (_ctx, _cache) => {
  982. return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
  983. vue.withDirectives(vue.createElementVNode("div", _hoisted_1, [
  984. vue.createElementVNode("div", _hoisted_2, [
  985. vue.createElementVNode("div", _hoisted_3, [
  986. vue.createTextVNode(" 评论 "),
  987. (vue.openBlock(), vue.createElementBlock("svg", {
  988. onClick: goHome,
  989. class: "home-icon",
  990. width: "24",
  991. height: "24",
  992. viewBox: "0 0 48 48",
  993. fill: "none",
  994. xmlns: "http://www.w3.org/2000/svg"
  995. }, _hoisted_7))
  996. ]),
  997. vue.createElementVNode("div", _hoisted_8, [
  998. (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(data.comments, (item) => {
  999. return vue.openBlock(), vue.createElementBlock("div", _hoisted_9, [
  1000. vue.createElementVNode("div", _hoisted_10, [
  1001. vue.createElementVNode("span", null, vue.toDisplayString(item.name), 1),
  1002. vue.createElementVNode("span", null, vue.toDisplayString(item.time), 1)
  1003. ]),
  1004. item.quote ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_11, vue.toDisplayString(item.quote), 1)) : vue.createCommentVNode("", true),
  1005. vue.createElementVNode("div", _hoisted_12, vue.toDisplayString(item.replay), 1)
  1006. ]);
  1007. }), 256))
  1008. ])
  1009. ]),
  1010. vue.createElementVNode("div", _hoisted_13, [
  1011. vue.createElementVNode("div", {
  1012. id: "dplayer",
  1013. ref_key: "dpRef",
  1014. ref: dpRef,
  1015. style: { "height": "70vh" }
  1016. }, null, 512),
  1017. vue.createElementVNode("div", _hoisted_14, vue.toDisplayString(data.info.video.name), 1),
  1018. vue.createElementVNode("div", _hoisted_15, " 添加时间: " + vue.toDisplayString(data.info.video.date), 1),
  1019. data.info.author.name ? (vue.openBlock(), vue.createElementBlock("a", {
  1020. key: 0,
  1021. href: data.info.author.url,
  1022. class: "author",
  1023. target: "_blank"
  1024. }, " 作者: " + vue.toDisplayString(data.info.author.name), 9, _hoisted_16)) : vue.createCommentVNode("", true),
  1025. vue.createVNode(Downloader, {
  1026. title: data.info.video.name,
  1027. url: data.info.video.url
  1028. }, null, 8, ["title", "url"])
  1029. ]),
  1030. vue.createElementVNode("div", _hoisted_17, [
  1031. vue.createElementVNode("div", _hoisted_18, [
  1032. vue.createTextVNode(" 本月热播 "),
  1033. vue.createElementVNode("div", {
  1034. class: "close",
  1035. onClick: _cache[0] || (_cache[0] = ($event) => data.show = !data.show)
  1036. })
  1037. ]),
  1038. vue.createElementVNode("div", {
  1039. class: "list",
  1040. ref_key: "recommendRef",
  1041. ref: recommendRef
  1042. }, null, 512)
  1043. ])
  1044. ], 512), [
  1045. [vue.vShow, data.show]
  1046. ]),
  1047. vue.withDirectives(vue.createElementVNode("div", {
  1048. class: "showBtn",
  1049. onClick: _cache[1] || (_cache[1] = ($event) => data.show = !data.show)
  1050. }, "展开脚本", 512), [
  1051. [vue.vShow, !data.show]
  1052. ])
  1053. ], 64);
  1054. };
  1055. }
  1056. };
  1057. const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-12ef713a"]]);
  1058. window.info = {
  1059. author: {
  1060. name: "",
  1061. url: "",
  1062. vid: ""
  1063. },
  1064. video: {
  1065. name: "",
  1066. url: "",
  1067. vid: ""
  1068. }
  1069. // videoUrl:'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8',
  1070. };
  1071. function init() {
  1072. if (window.info.videoUrl)
  1073. return;
  1074. const videoWrapper = $("#player_one_html5_api");
  1075. const userWrapper = $(".title-yakov");
  1076. if ((userWrapper == null ? void 0 : userWrapper.length) === 2) {
  1077. let href = userWrapper[1].firstElementChild.href;
  1078. let start = href.indexOf("UID");
  1079. let uid = href.substring(start + 4);
  1080. window.info.author = {
  1081. name: userWrapper.find(".title").text(),
  1082. url: `https://91porn.com/uvideos.php?UID=${uid}&type=public`,
  1083. uid
  1084. };
  1085. }
  1086. if (!videoWrapper[0]) {
  1087. return;
  1088. } else {
  1089. window.info.video = {
  1090. name: document.title.replace("Chinese homemade video", ""),
  1091. url: videoWrapper.find("source")[0].src,
  1092. vid: $("#favorite #VID").text(),
  1093. date: $(".title-yakov")[0].innerText
  1094. };
  1095. }
  1096. console.log(window.info);
  1097. }
  1098. try {
  1099. init();
  1100. vue.createApp(App).mount(
  1101. (() => {
  1102. const app = document.createElement("div");
  1103. document.body.append(app);
  1104. return app;
  1105. })()
  1106. );
  1107. } catch (e) {
  1108. console.log("init报错了", e);
  1109. }
  1110.  
  1111. })(Vue);