海角天涯-解锁海角社区资源

无限制解锁收费资源,包括钻石贴、金币贴,下载视频,复制视频链接,自动展开帖子

  1. // ==UserScript==
  2. // @name 海角天涯-解锁海角社区资源
  3. // @namespace tianya365.top
  4. // @homepage https://vip.tianya365.top
  5. // @version 1.4.0
  6. // @description 无限制解锁收费资源,包括钻石贴、金币贴,下载视频,复制视频链接,自动展开帖子
  7. // @icon data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAAEoAAABKCAYAAAAc0MJxAAAKSUlEQVR4Xu2cCZQU1RVA7x8G2VE2WWUbMSiCBDQqiAYVUAkIohIwyCaYnATZHAYwsgnICLLlIMQIRiISECTsgsMSRDGAsguMgkQWCWgiqzow/XPe/OlUdXdNdxXUBKH7nTMHqv5+6/3/33u/uhTRJFWnoGip4D4UtdBUUYoSUctcJolacwrFITSfaUUGAZYxVu3Lq/vKMSFVpyQlMULDLxUkXSZjv6huak22UswOBBjmBCwSVJp+XMHrCopeVMuXaWENZ3Q2nRinFtiHEApqgO6vYKxSOGvaZTp4r93WENABnmWsmhAsawEZoNsoxfx4mWqx4OXA0rTjJfU3yWtApeoUlcRWBcVjVRBP6RpO6SzqM0HtzwGVlKZnAR3jCYKHsc4MpKvOiv66mkpGiMXF7uYBUE5W2Q11FtcrUnXvpCQmeq0gnvIHAjyj1AC9VCkeiqeBex2r1ixSKk3vVlDba+F4yq9hj4A6oaBkPA3c61g1nFRJaVp7LRiP+ROgXD71BKgEKJcEXGZLaFQClEsCLrPlm0Yt6wqbDsGkD+DfZ1325kecLV9AtawNCzuDUnDie5i/A0athgP/+RGTiNE1T6DKFYM/PGwA2GXqBli737rzTid4uE5onknr4cU15l54+bz6KM0cPyOBoUsP2BOo6qVgX1pkp7vPgz9vNvd/Ug529oOkMJjZAbiQ8VZ7EY6eusxAVb0GvhgYHdSoFjCwqX8DqzQS/nXav/outCZPGpUXqKfmweuboVQRODAIil91od2JLFdxJBy70kB1vw1ebecfJKmpwgtmnbrU4ptG/eUTszbVKmsNSdzt9z6DwycjhylpTmtWuIvefwmczrrUmMATqNJFYVzL3E7bBirTLjkJMnqE7mgnv4ca6fDtd/4NtHAyjH7AtCNQZUfM+RdY8zm8m+lfW/aaPIGK1oVl3aDFDaE5vjsHe45HlnI6NAzfJRfshBGrIss+8VOY2T7yvgB7YDrsOgpnz0FWNpzLhuwL2WodBuoLqJQysKMvFEr272lO3wg934msb8Zj0Llh9HaCmiaQzgcMMAGXA++89f8+i2FNnm8bhLbhC6iyRWFia3isLiQX8AfW9E3Qc35oXTK9vxwE5X16TWTwu5C+1l1/PYESy7xvkzDSwNztsOUI3FoFpj8KN1dw13i0XDM2QY8wUM1qwfJu7i37WL148xPoPDdWLpPuCdSN15qdLVw6z4E3t5i7w5rB8/dZOUTtW0yHrPPWvZvKg/zN3ZbbCQVrn4aCNm10AjWyBQwKM2Z3HAXZcZ2kaEEYcn+klxDMu+4LaPrHfABVuxzs6u8NlKwLpYbC9+eh713Q43a4oaxxS6qMtuo6OzJ0jQsHJRtA5gCoWTq0/ZGrYOh7zoPteiu89qhz2spMENfriIPp4lTCk0aJH/dpDFBDm8GQMI0qNQxkB5zUGn7XyHRDdqkaY+DQCXMdC1SDSrCxV+i0kzrqvAyZXzvDmPsEtKsbmiaaPXqNWZvkIboVf0DNhfVfGAh3VoM7qlrNy67T8S3TqTZ1oMutVppEFP4uUQcFczqGTr09x2D1PliyG1ZkQu/GML5V6LAEcpOpzkMtUhA294KiNnfqTBaIASv1iYhWu4XlC6guc2HfN/D+b9w+H/f5hqyE0athax9/Ngl7y794HZbvddcXT6AqlYSJrYzrYXc1pmwACaPkFyjZnfY7RC3cDTHvXGnLYNw6d7V4AhWtykbV8g+UuEgyjfyyn4LjmPkxdH07H0F1rA/1KoIYhZ/lLqSyNq13mHqiebI2BCUvj0JCM06RT5l6EkaufS2s7gElCkORZCuvhGDsRuNd1aHtzVZ7AmPbV+a6ytWhduDGg3DnlHwE9d5TcO/1Zrp9cACmfWRCwdcWh1/fYf6CInZU+RfgbJQIgMA7OSLUPFi4C8RyPnbGOpxoUBnqV4KpbSwPQBb0mmOs9lrfBPM6Wdd9F8OUD8219O/gYAuylJWd102o2fPUK3YVHH4OShQKfRIjMmB4hjHwht4fCqr0MOOoRpNw8yDatPh2uNW+aKx95xKtvMpmuJ4Pc4wlLai5YrKUGQY/uDATPINqmgKiUfZpIk+k8SsgqhwNlGzZMm3vqQkNK0PdCdbT9ALq0GCo6MP7NwL55vHOEY7wh+oZ1Ctt4enbQ6uReNN1o43WiPsibox96pUZbtapEc3huXtNinRS4P7joLn2Amr3s8a690NkMRftjSWeQe3qZxZWu4hT3OEtcycclBicMvUE1OP1YLbtldrnVxgr2SuoTb1A1qugvL0dvrEdsjavBTXLWA9k1hYrSnpLRWMUB+XldTBgWSxMHp3i8sVNmCM8lCIeuNg6TqBkwZ+43qwjZYpCT5s2SsRBfC6R/nebKGlQtn8FUz+Cjw/B1iOhAbg1PeHumlbe5q/Bqs+ta4mApv3cAtVwsrXzhQf+5u2A9vJOdAzxpFGp98CYByNrrD3OMhPCNSpWB2KlyxQVbRF3ZudR4/IMvjc0mmqPXkh98jCmtrVqFm0XrRepVwG29LHSZOo3cmEieAK1qAvIcbld9h43jmnQPvIblBNIgWffTMT0sO9csrPJ7hwUe7qEnK8ubKWJv1d5VKzH5WHqSazoq9+bszu7vJABwzKsO/8PULGH5T6HLA3lRph3JKKJa41qdSMseDLSem7zBizebTUhGidGn12c3hINt9C73Ra6Rn3+NWw+DBJVrVjCmAMlC0GBfPjZQLM/mantCyg5pgoPA4tKy0mul3M3UXsnd2VvKshRVFBkp3pyTmjXBVSN0tCvCfyqgZW27QgsDzum6tIQKuTG1n84D+K4Bw3T+1Lgtuus8qlLYfz7PoHa0tv4d3ZZ+Ck8MtO9mkvOya3ht7nBu2glnUAF80vcS4KAQfnwADSZFlrbWx2g/S3mnrhR8rJHr8YgUdo65eGGclZ+2ZUlTuWLRontJNNPtldpTNas596FMS5PMYKd8AOUABAQQZEwc+mhoQu6PXYvU7/pq+aUyOkhSey+w2yfQAWrkV1D1L9TA/jrVnfmv70LfoASN0pOpe1SfyLIQUNQevwMpj1iXfdeZA4ZJoRFSSVHzgnSZJ9BeZtokbmDoGJ57LO3Rq5RwdpkcX/mLihW0JgBJQubNWbDP632ZHrJSyNBkeiGuFrPNI7sk7he4etheC7Xu97FArrcyydAuXyCCVAJUC4JuMyW0KgEKJcEXGZLaFQClEsCLrMlfirrAlTOT2VVmt6rIOztSxel4yiLhkwBtUJB8zgat+ehaliZ+ECEC2yBAH0UA3V1FWB/vH8KKS9eWqN1NjWCH7GZDnRzATces8wIpKvu5t34fvo6lcx2pbgmHknkqU1wQp+jLuPVQetHBGn6QQVLEl/9MdhyvvKjaEW6Wi7XkZ9uU7wU77Byv0bWl5fU/+KekT9LeVa3VQV4Q3FlfEbS61Ki4bSWj46lq8X2ss4f/UvTVZNglBSIF+3K/bzkrAA8T7r6MiIUHJW4fLA0iYeU5v4r+oOlsArN0mgfLP0vox/zX2L4nPIAAAAASUVORK5CYII=
  8. // @author tianya365.top
  9. // @exclude https://*.tianya365.top/*
  10. // @include https://hj*.*/*
  11. // @include https://h*j*.*/*
  12. // @include https://*.hj*.*/*
  13. // @include https://hai*.top/*
  14. // @match https://haijiao.com/*
  15. // @match https://www.haijiao.com/*
  16. // @match *://*/post/details*
  17. // @connect *
  18. // @run-at document-start
  19. // @grant none
  20. // @antifeature payment
  21. // @charset UTF-8
  22. // ==/UserScript==
  23.  
  24. (function () {
  25. 'use strict';
  26.  
  27. let version = '1.4.0';
  28. let newVersion = '';
  29. let m3u8_url = '';
  30. let play_url = '';
  31. let hasPic = false;
  32. let video = null;
  33. let topic = null;
  34. let shortVideoUrl = '';
  35. let server = window.localStorage.getItem('hjty_server');
  36. let freeToken = window.localStorage.getItem('hjty_free_token');
  37.  
  38. if (server == null) {
  39. server = 'https://vip.tianya365.top';
  40. window.localStorage.setItem('hjty_server', server);
  41. }
  42.  
  43. if (freeToken == null) {
  44. freeToken = '8b9591fcdb4e4340bc815cb183aa02f8'
  45. window.localStorage.setItem('hjty_free_token', freeToken);
  46. }
  47.  
  48. function importJs(src, onload) {
  49. let script = document.createElement("script");
  50. script.setAttribute("type", "text/javascript");
  51. script.src = src;
  52. script.onload = onload;
  53. document.getElementsByTagName('head')[0].appendChild(script);
  54. }
  55.  
  56. function loadStyle() {
  57. $('head').append(`
  58. <style>
  59. .hjty-dialog-mask{position: fixed;top: 0;left: 0;width: 100%;height: 100%;background-color: rgba(0, 0, 0, 0.5);z-index: 3000;}
  60. .hjty-dialog{position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);background-color: #ffffff;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);padding: 20px;font-size: 14px;
  61. border: 1px solid #f1f1f1;
  62. border-radius: 5px;
  63. width: 250px;}
  64. .hjty-dialog-title{font-size: 16px;font-weight: bold;margin-bottom: 10px;text-align: center;}
  65. .hjty-dialog-content p{color: #333333;margin-bottom: 10px;}
  66. .hjty-dialog-content ul{list-style: none;padding: 0;margin: 0;}
  67. .hjty-dialog-content ul li{
  68. margin-bottom: 5px;
  69. border: 2px solid #d5d5d5;
  70. padding: 3px 10px;
  71. border-radius: 4px;
  72. background: #f7f7f7;
  73. cursor: pointer;
  74. }
  75. .hjty-dialog-content ul li.active{border-color: #409eff;background: #ecf5ff;}
  76. .hjty-dialog-content ul li span.status{display: inline-block;width: 10px;height: 10px;border-radius: 50%;margin-right: 5px;background-color: #a9a9a9;}
  77. .hjty-dialog-content ul li span.status-success{background-color: #67c23a;}
  78. .hjty-dialog-content ul li span.status-fail{background-color: #f56c6c;}
  79. .hjty-dialog-content ul li span.timeout{color: #999999;font-size: 12px;float: right;margin-top: 1px;}
  80. .hjty-dialog-footer{margin-top: 20px;text-align: center;}
  81. .hjty-dialog-btn{padding: 5px 10px;background-color: #409eff;color: #ffffff;border: none;border-radius: 4px;cursor: pointer;}
  82. #play-box {height: 100%;display: flex;align-items: center;background-color: #000;}
  83. .van-popup.van-popup--center.play-box {width: 100%;height: 100%;max-width: 100%;}
  84. #bbs_float_menu{z-index:1!important;}
  85. #body.el-popup-parent--hidden{overflow: auto;}
  86. .van-list .van-list__loading,
  87. .sell-btn:before,
  88. .el-message-box,
  89. .v-modal,
  90. .containeradvertising,
  91. .ishide,
  92. .el-message-box__wrapper,
  93. #tidio-chat{display: none !important;}
  94. </style
  95. `);
  96. }
  97.  
  98. function importCss(href) {
  99. let link = document.createElement("link");
  100. link.setAttribute("rel", "stylesheet");
  101. link.href = href;
  102. document.getElementsByTagName('head')[0].appendChild(link);
  103. }
  104.  
  105. function checkUrl(url) {
  106. return new Promise((resolve, reject) => {
  107. const xhr = new XMLHttpRequest();
  108. const startTime = Date.now();
  109. const timeoutId = setTimeout(() => {
  110. resolve(null);
  111. }, 5000);
  112.  
  113. xhr.open('HEAD', url, true);
  114. xhr.onload = function() {
  115. clearTimeout(timeoutId);
  116. if (xhr.status >= 200 && xhr.status < 400) {
  117. const responseTime = Date.now() - startTime;
  118. resolve({ url: url, time: responseTime });
  119. } else {
  120. resolve(null);
  121. }
  122. };
  123.  
  124. xhr.onerror = function() {
  125. clearTimeout(timeoutId);
  126. resolve(null);
  127. };
  128.  
  129. xhr.send();
  130. });
  131. }
  132.  
  133. function switchServer() {
  134. $('body').append(`
  135. <div class="hjty-dialog-mask">
  136. <div class="hjty-dialog">
  137. <div class="hjty-dialog-title">海角天涯提示</div>
  138. <div class="hjty-dialog-content">
  139. <p>请选择服务器节点</p>
  140. <ul>
  141. <li data-url="https://vip.tianya365.top"><span class="status"></span> 服务器1 <span class="timeout">测速中</span></li>
  142. <li data-url="https://haijiao.tianya365.top"><span class="status"></span> 服务器2 <span class="timeout">测速中</span></li>
  143. <li data-url="https://new.tianya365.top"><span class="status"></span> 服务器3 <span class="timeout">测速中</span></li>
  144. <li data-url="https://hj.tianya365.top"><span class="status"></span> 服务器4 <span class="timeout">测速中</span></li>
  145. </ul>
  146. </div>
  147. <div class="hjty-dialog-footer">
  148. <button class="hjty-dialog-btn">确定</button>
  149. </div>
  150. </div>
  151. </div>
  152. `);
  153.  
  154. $('.hjty-dialog-content ul li').each(function() {
  155. const url = $(this).attr('data-url');
  156. if (server === url) {
  157. $(this).addClass('active');
  158. } else {
  159. $(this).removeClass('active');
  160. }
  161. checkUrl(url + '/login').then(result => {
  162. if (result) {
  163. $(this).find('.status').addClass('status-success');
  164. $(this).find('.timeout').html(`${result.time}ms`);
  165. } else {
  166. $(this).find('.status').addClass('status-fail');
  167. $(this).find('.timeout').html('超时');
  168. }
  169. })
  170. });
  171.  
  172. $('.hjty-dialog-content ul li').click(function() {
  173. $('.hjty-dialog-content ul li').not($(this)).removeClass('active');
  174. $(this).addClass('active');
  175. server = $(this).attr('data-url');
  176. window.localStorage.setItem('hjty_server', server);
  177. });
  178. $('.hjty-dialog-btn').click(function() {
  179. location.reload()
  180. });
  181. }
  182.  
  183. window.onload = function () {
  184. loadStyle();
  185. checkUrl(server).then(result => {
  186. if (!result) {
  187. switchServer();
  188. } else {
  189. importCss(server + '/static/libs/vant/lib/index.css');
  190. importJs(server + '/static/libs/axios/axios.min.js');
  191. importJs(server + '/js/base64.min.js');
  192. importJs(server + '/static/libs/vue/vue.global.prod.js', function () {
  193. importJs(server + '/static/libs/vant/lib/vant.js', function () {
  194. init()
  195. });
  196. });
  197. }
  198. })
  199. }
  200.  
  201. const decrypt = function (data) {
  202. return JSON.parse(window.Base64.decode(window.Base64.decode(window.Base64.decode(data))));
  203. }
  204.  
  205. const encrypt = function (data) {
  206. return window.Base64.encode(window.Base64.encode(window.Base64.encode(JSON.stringify(data))));
  207. }
  208.  
  209. function formatTitle(data) {
  210. data.results.forEach(item => {
  211. const tips = [];
  212. tips.push('money_type' in item ? ['', '💰', '💎'][item.money_type] : '');
  213. item.hasVideo && tips.push('🎥');
  214. item.hasAudio && tips.push('🎵');
  215. item.title = `${tips.join(' ')} ${item.title}`;
  216. });
  217. return data;
  218. }
  219.  
  220. function getShortVideoUrl(responseText) {
  221. try {
  222. let res = JSON.parse(responseText);
  223. if (res['isEncrypted']) {
  224. let data = decrypt(res.data);
  225. if (data['category'] && data['category'] === 'video' && data['remoteUrl']) {
  226. if (!data['remoteUrl'].startsWith('http')) {
  227. shortVideoUrl = window.location.origin + data['remoteUrl'];
  228. } else {
  229. shortVideoUrl = data['remoteUrl'];
  230. }
  231. }
  232. }
  233. } catch (e) {
  234. }
  235. }
  236.  
  237. function fixShortVideo(responseText) {
  238. try {
  239. responseText = JSON.parse(responseText);
  240. let video = decrypt(responseText.data);
  241. video.type = 1;
  242. video.amount = 0;
  243. video.money_type = 0;
  244. video.vip = 0;
  245. video.message = '';
  246. responseText.data = encrypt(video);
  247. return JSON.stringify(responseText);
  248. } catch (e) {
  249. return responseText;
  250. }
  251. }
  252.  
  253. function init() {
  254. $('body').append(`
  255. <div id="hjty-app">
  256. <van-dialog v-model:show="dialogLogin"
  257. :before-close="userLogin"
  258. title="登录海角天涯"
  259. confirm-button-text="登录"
  260. show-cancel-button="true"
  261. close-on-click-overlay="true"
  262. cancel-button-text="注册"
  263. theme="round-button">
  264. <van-form style="padding: 10px 15px">
  265. <van-field
  266. v-model="username"
  267. name="账号"
  268. label="账号"
  269. placeholder="请输入账号"
  270. :rules="[{ required: true, message: '请填写账号' }]"
  271. ></van-field>
  272. <van-field
  273. v-model="password"
  274. type="password"
  275. name="密码"
  276. label="密码"
  277. placeholder="请输入密码"
  278. :rules="[{ required: true, message: '请填写密码' }]"
  279. ></van-field>
  280. </van-form>
  281. </van-dialog>
  282. <div style="touch-action: none">
  283. <van-floating-bubble
  284. axis="xy"
  285. icon="lock"
  286. magnetic="x"
  287. @click="unlock"
  288. v-model:offset="offset"
  289. ></van-floating-bubble>
  290. </div>
  291. <div>
  292. <van-action-sheet
  293. v-model:show="actionShow"
  294. :actions="actions"
  295. @select="onSelect"
  296. cancel-text="关闭"
  297. description="帖子解锁成功,下滑查看更多操作"
  298. ></van-action-sheet>
  299. </div>
  300. <div>
  301. <van-popup v-model:show="showPlayer" :closeable="true" class="play-box" @click-close-icon="onPlayerClose">
  302. <div id="play-box">
  303. <iframe :src="play_url" style="display:block;border:0;width: 100%;height: 500px" allow="autoplay; encrypted-media" allowfullscreen></iframe>
  304. </div>
  305. </van-popup>
  306. </div>
  307. </div>`);
  308.  
  309. axios.defaults.timeout = 60000;
  310. Vue.createApp({
  311. data() {
  312. return {
  313. showPlayer: false,
  314. dialogLogin: false,
  315. dialogResult: false,
  316. actionShow: false,
  317. play_url: '',
  318. username: '',
  319. password: '',
  320. offset: {},
  321. actions: []
  322. };
  323. },
  324. mounted() {
  325. if (!getUserToken()) {
  326. this.dialogLogin = true;
  327. } else {
  328. this.checkUpdate();
  329. }
  330. this.offset = {x: window.innerWidth - 72, y: window.innerHeight / 2};
  331. },
  332. methods: {
  333. unlock() {
  334. if (newVersion !== '') {
  335. this.showUpdate();
  336. return;
  337. }
  338.  
  339. if (!getUserToken()) {
  340. this.dialogLogin = true;
  341. return;
  342. }
  343.  
  344. deleteCookie('NOTLOGIN');
  345. const pid = this.getPid();
  346. if (!pid) {
  347. if (/\/videoplay/.test(location.href)) {
  348. copyText(shortVideoUrl).then(() => {
  349. vant.showSuccessToast('短视频地址复制成功');
  350. }).catch(() => {
  351. vant.showFailToast('复制失败');
  352. });
  353. } else {
  354. vant.showDialog({
  355. title: '海角天涯',
  356. message: '当前账号:' + window.localStorage.getItem('hjty_user_name') + '\n如需解锁帖子,请进入帖子再操作',
  357. theme: 'round-button',
  358. confirmButtonText: '退出登录',
  359. cancelButtonText: '关闭',
  360. showCancelButton: true,
  361. }).then(action => {
  362. if (action === 'confirm') {
  363. window.localStorage.setItem('hjty_user_token', '');
  364. window.localStorage.setItem('hjty_user_name', '');
  365. vant.showSuccessToast('退出成功');
  366. location.reload();
  367. }
  368. }).catch(error => {});
  369. }
  370.  
  371. return;
  372. }
  373. if (m3u8_url !== '') {
  374. this.actionShow = true;
  375. return;
  376. }
  377. vant.showLoadingToast({
  378. message: '解锁中...',
  379. forbidClick: true,
  380. duration: 0,
  381. });
  382. this.getTopic(pid).then((data) => {
  383. this.showTopic(data, pid);
  384. }).catch((err) => {
  385. vant.closeToast();
  386. vant.showFailToast(err);
  387. });
  388. },
  389. showActions() {
  390. this.actions = [
  391. {
  392. name: '播放视频(站内)',
  393. key: 'play',
  394. subname: '如无法在线播放,可尝试其他在线播放器,或下载后再观看'
  395. },
  396. {
  397. name: '复制视频地址',
  398. key: 'copy',
  399. subname: '可复制视频地址到其他地方播放或下载'
  400. },
  401. {
  402. name: '在线播放器1',
  403. key: 'open',
  404. subname: '电脑端和手机端均可正常播放',
  405. url: 'http://tool.liumingye.cn/m3u8/#' + m3u8_url
  406. },
  407. {
  408. name: '在线播放器2',
  409. subname: '手机端可能无法播放',
  410. key: 'open',
  411. url: 'https://www.hlsplayer.net/embed?type=m3u8&src=' + encodeURIComponent(m3u8_url)
  412. },
  413. {
  414. name: '在线播放器3',
  415. key: 'open',
  416. subname: '无法播放免费视频,手机端可能无法播放',
  417. url: 'https://m3u8player.org/'
  418. },
  419. {
  420. name: '视频下载通道1',
  421. key: 'open',
  422. subname: '【推荐】支持自动下载和在线播放',
  423. url: 'https://getm3u8.com/?source=' + m3u8_url + (m3u8_url.includes('?') ? '&' : '?') + 'title=' + topic['title']
  424. },
  425. {
  426. name: '视频下载通道2',
  427. key: 'open',
  428. subname: '免费视频可能无法下载,推荐在电脑端操作',
  429. url: 'https://blog.luckly-mjw.cn/tool-show/m3u8-downloader/index.html?source=' + m3u8_url + (m3u8_url.includes('?') ? '&' : '?') + 'title=' + topic['title']
  430. },
  431. {
  432. name: '视频下载通道3',
  433. key: 'open',
  434. subname: '需要手动点击下载,推荐在电脑端操作',
  435. url: 'https://tools.thatwind.com/tool/m3u8downloader#m3u8=' + encodeURIComponent(m3u8_url)
  436. },
  437. {
  438. name: '海角天涯官网',
  439. key: 'open',
  440. subname: '点击访问',
  441. url: server + '/'
  442. },
  443. {
  444. name: '切换节点',
  445. key: 'switch',
  446. subname: '点击切换其他服务器节点'
  447. },
  448. {
  449. name: '退出登录',
  450. subname: '当前账号:'+ window.localStorage.getItem('hjty_user_name'),
  451. key: 'logout',
  452. },
  453. ];
  454. this.actionShow = true;
  455. },
  456. onSelect(item) {
  457. switch (item.key) {
  458. case 'play':
  459. this.play_url = play_url;
  460. this.showPlayer = true;
  461. vant.showToast('视频加载中,请稍等...');
  462. break;
  463. case 'open':
  464. window.open(item.url);
  465. break;
  466. case 'switch':
  467. switchServer();
  468. break;
  469. case 'logout':
  470. window.localStorage.setItem('hjty_user_token', '');
  471. window.localStorage.setItem('hjty_user_name', '');
  472. vant.showSuccessToast('退出成功');
  473. location.reload();
  474. break;
  475. case 'copy':
  476. copyText(m3u8_url).then(() => {
  477. vant.showSuccessToast('复制成功');
  478. }).catch(() => {
  479. vant.showFailToast('复制失败');
  480. });
  481. }
  482. },
  483. getAttachments(data, pid) {
  484. return new Promise((resolve, reject) => {
  485. let html = '';
  486. data['attachments'].forEach(item => {
  487. switch (item.category) {
  488. case 'video':
  489. video = item;
  490. break;
  491. case 'audio':
  492. html += '<p style="text-align: center"><audio src="' + item['remoteUrl'] + '" controls="true" id="showaudio"></audio></p>'
  493. break;
  494. case 'images':
  495. hasPic = true;
  496. if (!data['content'].includes(item['remoteUrl'])) {
  497. html += '<p><img data-url="'+item['remoteUrl']+'" src="/images/common/project/loading.gif" alt="" data-id="' + item.id + '" lazy="loaded"></p>'
  498. }
  499. break;
  500. }
  501. });
  502. resolve(html);
  503. });
  504. },
  505. showTopic(data, pid) {
  506. if (data['attachments']) {
  507. this.getAttachments(data, pid).then(html => {
  508. if (html) {
  509. if ($('.sell-btn').length) {
  510. $('.sell-btn').replaceWith('<div id="hjsell-container" class="hjsell-container"><div class="hssell-content">' + html + '</div></div>');
  511. } else {
  512. $('#hjsell-container .hssell-content').html(html);
  513. }
  514. if (!document.getElementById('hjsell-container')) {
  515. vant.showFailToast('请等待页面加载完成!');
  516. return;
  517. }
  518. window.scrollTo({
  519. top: document.getElementById('hjsell-container').getBoundingClientRect().top + window.scrollY - 100,
  520. behavior: 'smooth'
  521. });
  522. if (hasPic) {
  523. this.showImages();
  524. }
  525. }
  526. if (video !== null) {
  527. if (!data['sale'] || data['sale']['money_type'] === 0 || data['sale']['is_buy']) {
  528. this.getVideo(video, pid);
  529. } else {
  530. this.queryTopic(pid);
  531. }
  532. } else {
  533. vant.closeToast();
  534. vant.showSuccessToast('解锁成功');
  535. }
  536. });
  537. } else {
  538. vant.showFailToast('没有可解锁的内容');
  539. }
  540. },
  541. queryTopic(pid) {
  542. axios.get(server + '/api/topic/' + pid + '?v=' + version, {
  543. headers: {
  544. 'token': getUserToken()
  545. }
  546. }).then(res => {
  547. vant.closeToast();
  548. if (res.data.code) {
  549. if (res.data.msg === '有新版本') {
  550. newVersion = res.data.data;
  551. this.showUpdate();
  552. return ;
  553. }
  554.  
  555. if (res.data.data['m3u8_url'] !== '') {
  556. m3u8_url = server + res.data.data['m3u8_url'];
  557. play_url = server + '/play?url=' + window.Base64.encode(m3u8_url);
  558. this.showActions();
  559. } else {
  560. vant.showFailToast('没有可解锁的内容');
  561. }
  562. } else {
  563. vant.showDialog({
  564. title: '解锁失败',
  565. message: res.data.msg,
  566. theme: 'round-button',
  567. confirmButtonText: res.data.data['confirmButtonText'],
  568. cancelButtonText: res.data.data['cancelButtonText'],
  569. showCancelButton: !!res.data.data['cancelButtonText'],
  570. }).then(action => {
  571. if (action === 'confirm') {
  572. if (res.data.data['confirmButtonText'] === '重新登录') {
  573. window.localStorage.setItem('hjty_user_token', '');
  574. this.dialogLogin = true;
  575. } else if (res.data.data['confirmButtonText'] === '打开官网') {
  576. window.open(server + '/');
  577. }
  578. }
  579. }).catch(error => {});
  580. return false;
  581. }
  582. }).catch(() => {
  583. vant.closeToast();
  584. vant.showConfirmDialog({
  585. title: '解锁失败',
  586. message: '服务器连接出错,是否切换到其他节点?',
  587. theme: 'round-button',
  588. beforeClose: (action) => {
  589. if (action === 'confirm') {
  590. switchServer();
  591. }
  592. return true;
  593. }
  594. });
  595. });
  596. },
  597. showImages() {
  598. document.querySelectorAll('#hjsell-container img').forEach(img => {
  599. axios.get(img.dataset.url).then(res => {
  600. img.src = imageDecode(res.data);
  601. });
  602. });
  603. },
  604. getVideo(item, pid) {
  605. if (item.remoteUrl.includes('haijiao.live')) {
  606. m3u8_url = item.remoteUrl;
  607. play_url = server + '/play?url=' + window.Base64.encode(item.remoteUrl);
  608. vant.closeToast();
  609. this.showActions();
  610. } else {
  611. const uid = /uid=([^;]+)/.exec(document.cookie);
  612. const token = /token=([^;]+)/.exec(document.cookie);
  613. axios.post('/api/attachment', JSON.stringify({
  614. id: item.id,
  615. resource_id: pid,
  616. resource_type: 'topic',
  617. line: ''
  618. }), {
  619. headers: {
  620. 'X-User-Id': uid && token ? uid[1] : '172561377002',
  621. 'X-User-Token': uid && token ? token[1] : freeToken
  622. }
  623. }).then(res => {
  624. vant.closeToast();
  625. let data = {};
  626. if (res.data.isEncrypted) {
  627. data = JSON.parse(window.Base64.decode(window.Base64.decode(window.Base64.decode(res.data.data))));
  628. } else {
  629. data = res.data.data;
  630. }
  631.  
  632. if (!data['remoteUrl'].startsWith('http')) {
  633. m3u8_url = play_url = window.location.origin + data.remoteUrl;
  634. } else {
  635. m3u8_url = play_url = data.remoteUrl;
  636. }
  637. play_url = server + '/play?url=' + window.Base64.encode(play_url);
  638.  
  639. this.showActions();
  640. }).catch(() => {
  641. axios.get(server + '/acc').then(res => {
  642. if (res.data.code) {
  643. freeToken = res.data.data;
  644. window.localStorage.setItem('hjty_free_token', freeToken);
  645. vant.showFailToast('网络错误: 请重试')
  646. } else {
  647. vant.showFailToast('网络错误: 2')
  648. }
  649. }).catch(() => {
  650. vant.showFailToast('网络错误: 2')
  651. });
  652. });
  653. }
  654. },
  655. getTopic(pid) {
  656. return new Promise((resolve, reject) => {
  657. axios.get('/api/topic/' + pid).then(res => {
  658. if (res.data.isEncrypted) {
  659. topic = JSON.parse(window.Base64.decode(window.Base64.decode(window.Base64.decode(res.data.data))));
  660. } else {
  661. topic = res.data.data;
  662. }
  663. resolve(topic);
  664. }).catch(() => {
  665. reject('网络错误: 3')
  666. });
  667. })
  668. },
  669. onPlayerClose() {
  670. this.play_url = '';
  671. },
  672. userLogin(action) {
  673. if (action === 'confirm') {
  674. if (!this.username || !this.password) {
  675. vant.showFailToast('请填写账号和密码');
  676. return false;
  677. }
  678. return new Promise((resolve) => {
  679. axios.post(server + '/api/login', {
  680. username: this.username,
  681. password: this.password
  682. }).then(res => {
  683. if (res.data.code) {
  684. vant.showDialog({
  685. title: '登录失败',
  686. message: res.data.msg,
  687. theme: 'round-button',
  688. });
  689. resolve(false);
  690. } else {
  691. window.localStorage.setItem('hjty_user_token', res.data.token);
  692. window.localStorage.setItem('hjty_user_name', this.username);
  693. this.dialogLogin = false;
  694. vant.showDialog({
  695. title: '登录成功',
  696. message: res.data.msg,
  697. theme: 'round-button',
  698. }).then(() => {
  699. resolve(true);
  700. })
  701. }
  702. });
  703. });
  704. } else if(action === 'cancel') {
  705. window.open(server + '/reg');
  706. } else {
  707. this.username = '';
  708. this.password = '';
  709. return true;
  710. }
  711. },
  712. getPid() {
  713. if (/\/post\/details.*pid=\d+/.test(location.href)) {
  714. let match = location.href.match(/\/post\/details.*pid=(\d+)/i);
  715. return parseInt(match[1]);
  716. }
  717. return false;
  718. },
  719. showUpdate() {
  720. vant.showDialog({
  721. title: '新版本(' + newVersion + ')',
  722. message: '检测到新版本,请更新后再使用!',
  723. theme: 'round-button',
  724. confirmButtonText: '立即更新',
  725. }).then(() => {
  726. location.href = server + '/js/script.user.js?v=' + Math.random();
  727. });
  728. },
  729. checkUpdate() {
  730. axios.get(server + '/api/update?v=' + version, {
  731. headers: {
  732. 'token': getUserToken()
  733. }
  734. }).then(res => {
  735. if (res.data.msg === '有新版本') {
  736. newVersion = res.data.data;
  737. this.showUpdate();
  738. }
  739. }).catch(() => {
  740. vant.closeToast();
  741. vant.showConfirmDialog({
  742. title: '版本检查失败',
  743. message: '服务器连接出错,是否切换到其他节点?',
  744. theme: 'round-button',
  745. beforeClose: (action) => {
  746. if (action === 'confirm') {
  747. switchServer();
  748. }
  749. return true;
  750. }
  751. });
  752. });
  753. }
  754. },
  755. }).use(vant).mount('#hjty-app');
  756. }
  757.  
  758. function getUserToken() {
  759. return window.localStorage.getItem('hjty_user_token');
  760. }
  761.  
  762. function deleteCookie(name) {
  763. // 设置 cookie 过期时间为过去的时间
  764. document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
  765. }
  766.  
  767. function imageDecode(t) {
  768. const e = "ABCD*EFGHIJKLMNOPQRSTUVWX#YZabcdefghijklmnopqrstuvwxyz1234567890";
  769. let o, i, a, r, s, c, u, l = "", d = 0;
  770.  
  771. function n(e) {
  772. let t, n = "", o = 0, i = 0, a = 0;
  773. while (o < e.length)
  774. i = e.charCodeAt(o),
  775. i < 128 ? (n += String.fromCharCode(i),
  776. o++) : i > 191 && i < 224 ? (a = e.charCodeAt(o + 1),
  777. n += String.fromCharCode((31 & i) << 6 | 63 & a),
  778. o += 2) : (a = e.charCodeAt(o + 1),
  779. t = e.charCodeAt(o + 2),
  780. n += String.fromCharCode((15 & i) << 12 | (63 & a) << 6 | 63 & t),
  781. o += 3);
  782. return n
  783. }
  784.  
  785. t = t.replace(/[^A-Za-z0-9*#]/g, "");
  786. while (d < t.length)
  787. r = e.indexOf(t.charAt(d++)),
  788. s = e.indexOf(t.charAt(d++)),
  789. c = e.indexOf(t.charAt(d++)),
  790. u = e.indexOf(t.charAt(d++)),
  791. o = r << 2 | s >> 4,
  792. i = (15 & s) << 4 | c >> 2,
  793. a = (3 & c) << 6 | u,
  794. l += String.fromCharCode(o),
  795. 64 !== c && (l += String.fromCharCode(i)),
  796. 64 !== u && (l += String.fromCharCode(a));
  797.  
  798. let reg = new RegExp('', "g");
  799. l = l.replace(reg, '');
  800. return l = n(l),
  801. l
  802. }
  803.  
  804. function copyText(text) {
  805. return new Promise((resolve, reject) => {
  806. if (navigator.clipboard && window.isSecureContext) {
  807. navigator.clipboard.writeText(text)
  808. .then(() => {
  809. resolve();
  810. })
  811. .catch(err => {
  812. reject(err);
  813. });
  814. } else {
  815. const textarea = document.createElement('textarea');
  816. textarea.value = text;
  817. textarea.style.position = 'fixed';
  818. document.body.appendChild(textarea);
  819. textarea.focus();
  820. textarea.select();
  821.  
  822. try {
  823. const success = document.execCommand('copy');
  824. if (success) {
  825. resolve();
  826. } else {
  827. reject();
  828. }
  829. } catch (err) {
  830. reject(err);
  831. }
  832.  
  833. document.body.removeChild(textarea);
  834. }
  835. });
  836. }
  837.  
  838. (function () {
  839. const originOpen = XMLHttpRequest.prototype.open;
  840. XMLHttpRequest.prototype.open = function (method, url) {
  841. if (/\/api\/topic\/(hot\/topics\?|node\/(topics|news)|idol_list)/.test(url)) {
  842. const xhr = this;
  843. const getter = Object.getOwnPropertyDescriptor(
  844. XMLHttpRequest.prototype,
  845. "response"
  846. ).get;
  847. Object.defineProperty(xhr, "responseText", {
  848. get: () => {
  849. let result = getter.call(xhr);
  850. try {
  851. let res = JSON.parse(result);
  852. if (res['isEncrypted']) {
  853. res.data = encrypt(formatTitle(decrypt(res.data)));
  854. } else {
  855. res.data = formatTitle(res.data);
  856. }
  857. return JSON.stringify(res);
  858. } catch (e) {
  859. return result;
  860. }
  861. },
  862. });
  863. } else if (/\/api\/topic\/\d+/.test(url) && /tianya365/.test(url) === false) {
  864. m3u8_url = '';
  865. } else if (/\/api\/attachment/.test(url)) {
  866. const xhr = this;
  867. const getter = Object.getOwnPropertyDescriptor(
  868. XMLHttpRequest.prototype,
  869. "response"
  870. ).get;
  871. Object.defineProperty(xhr, "responseText", {
  872. get: () => {
  873. let result = getter.call(xhr);
  874. getShortVideoUrl(result);
  875. return result;
  876. }
  877. });
  878. } else if (/\/api\/video\/checkVideoCanPlay/.test(url)) {
  879. const xhr = this;
  880. const getter = Object.getOwnPropertyDescriptor(
  881. XMLHttpRequest.prototype,
  882. "response"
  883. ).get;
  884. Object.defineProperty(xhr, "responseText", {
  885. get: () => {
  886. return fixShortVideo(getter.call(xhr));
  887. },
  888. });
  889. }
  890.  
  891. originOpen.apply(this, arguments);
  892. };
  893. })();
  894. })();