Acfun Extension

acfun插件

  1. // ==UserScript==
  2. // @name Acfun Extension
  3. // @namespace https://github.com/aoi-umi
  4. // @version 0.4
  5. // @description acfun插件
  6. // @author aoi-umi
  7. // @match *.acfun.cn/*
  8. // @grant none
  9. // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js
  10. // @license MIT
  11. // ==/UserScript==
  12. (function (exports) {
  13. 'use strict';
  14.  
  15. var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
  16.  
  17. function unwrapExports (x) {
  18. return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
  19. }
  20.  
  21. function createCommonjsModule(fn, module) {
  22. return module = { exports: {} }, fn(module, module.exports), module.exports;
  23. }
  24.  
  25. var config = createCommonjsModule(function (module, exports) {
  26. Object.defineProperty(exports, "__esModule", { value: true });
  27. exports.prefix = void 0;
  28. exports.prefix = 'ac-ext';
  29.  
  30. });
  31.  
  32. unwrapExports(config);
  33. config.prefix;
  34.  
  35. var utils = createCommonjsModule(function (module, exports) {
  36. Object.defineProperty(exports, "__esModule", { value: true });
  37. exports.insert = exports.wait = exports.clipboardCopy = void 0;
  38. const clipboardCopy = (data) => {
  39. return navigator && navigator.clipboard && navigator.clipboard.writeText(data);
  40. };
  41. exports.clipboardCopy = clipboardCopy;
  42. const wait = (ms) => {
  43. return new Promise((resolve) => {
  44. setTimeout(() => {
  45. resolve();
  46. }, ms);
  47. });
  48. };
  49. exports.wait = wait;
  50. const insert = (opt) => {
  51. let { input, rangeIndex, text } = opt;
  52. if (rangeIndex) {
  53. let oldVaue = input.value;
  54. input.value = oldVaue.slice(0, rangeIndex) + text + oldVaue.slice(rangeIndex);
  55. rangeIndex = rangeIndex + text.toString().length;
  56. }
  57. else {
  58. input.value += text;
  59. rangeIndex = input.value.length;
  60. }
  61. input.focus();
  62. input.setSelectionRange(rangeIndex, rangeIndex);
  63. let event = document.createEvent("HTMLEvents");
  64. event.initEvent("input", !1, !0);
  65. input.dispatchEvent(event);
  66. };
  67. exports.insert = insert;
  68.  
  69. });
  70.  
  71. unwrapExports(utils);
  72. utils.insert;
  73. utils.wait;
  74. utils.clipboardCopy;
  75.  
  76. var live = createCommonjsModule(function (module, exports) {
  77. var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) {
  78. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  79. return new (P || (P = Promise))(function (resolve, reject) {
  80. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  81. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  82. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  83. step((generator = generator.apply(thisArg, _arguments || [])).next());
  84. });
  85. };
  86. Object.defineProperty(exports, "__esModule", { value: true });
  87. exports.AcLiveExt = void 0;
  88.  
  89.  
  90. let prefix = `${config.prefix}-live-`;
  91. let acceptBtnName = `${prefix}accept`;
  92. let cancelBtnName = `${prefix}cancel`;
  93. let timeInputName = `${prefix}time`;
  94. let dialogName = `${prefix}dialog`;
  95. let emojiName = `${prefix}emoji`;
  96. let defaultTime = 100;
  97. let likeBtn = document.querySelector('.like-heart');
  98. class AcLiveExt {
  99. constructor() { }
  100. get acLikeExt() {
  101. return window.acLiveExt;
  102. }
  103. get acMainExt() {
  104. return window.acMainExt;
  105. }
  106. static globalInit() {
  107. let acLikeExt = window.acLiveExt = new AcLiveExt();
  108. acLikeExt.init();
  109. }
  110. toggle(dom, show) {
  111. if (show === undefined)
  112. dom.toggle();
  113. else
  114. show ? dom.show() : dom.hide();
  115. }
  116. // 样式
  117. initStyle() {
  118. let style = $('<style></style>').text(`
  119. .${prefix}menu {
  120. background: white;
  121. position: fixed; top: 100px; right: 10px;
  122. border-radius: 50%; border: 1px solid #dcdee2;
  123. width: 40px; height: 40px;
  124. z-index: 1000;
  125. cursor: pointer;
  126. display: flex;
  127. justify-content: center;
  128. align-items: center;
  129. }
  130.  
  131. .${prefix}dialog-box {
  132. position: fixed; top: 100px; right: 10px;
  133. display: flex; justify-content: center;
  134. z-index: 1000;
  135. }
  136. .${prefix}dialog {
  137. background: white; width: 250px; height:100px;
  138. border: 1px solid #dcdee2; border-radius: 5px;
  139. padding: 10px;
  140. }
  141. .${prefix}dialog > * {
  142. margin-bottom: 5px;
  143. }
  144. .${prefix}input {
  145. background-color: #f8f8f8 !important;
  146. color: #333;
  147. border-radius: 5px;
  148. padding: 2px 10px;
  149. font-size: 14px;
  150. font-weight: 400;
  151. text-align: left;
  152. line-height: 20px;
  153. border: 0;
  154. }
  155. .${prefix}btn {
  156. box-sizing: border-box;
  157. height: 28px;
  158. border-radius: 4px;
  159. padding: 3px 10px;
  160. font-size: 14px;
  161. line-height: 14px;
  162. cursor: pointer;
  163. position: relative;
  164. display: inline-flex;
  165. align-items: center;
  166. font-weight: 400;
  167. white-space: nowrap;
  168. text-align: center;
  169. background-image: none;
  170. background-color: #fff;
  171. border: 1px solid #e5e5e5;
  172. color: #999;
  173. }
  174. .${prefix}btn-primary {
  175. background: #fd4c5d;
  176. color: #fff;
  177. }
  178. `);
  179. $('head').append(style);
  180. }
  181. initView() {
  182. return __awaiter(this, void 0, void 0, function* () {
  183. // 等待加载
  184. while (true) {
  185. yield utils.wait(1000);
  186. let face = $('.face-text');
  187. if (face)
  188. break;
  189. }
  190. this.addDialog();
  191. this.addEmoji();
  192. });
  193. }
  194. addDialog() {
  195. let dialog = $(`
  196. <div id="${dialogName}" class="${prefix}dialog-box">
  197. <div class="${prefix}dialog">
  198. <div>时间(毫秒/次, 大于等于100的数)</div>
  199. <div>
  200. <input class="${prefix}input" id="${timeInputName}" value=${defaultTime} autocomplete="off" type="text" />
  201. </div>
  202. <div>
  203. <button id="${cancelBtnName}" type="button" class="${prefix}btn">取消</button>
  204. <button id="${acceptBtnName}" type="button" class="${prefix}btn ${prefix}btn-primary" >确定</button>
  205. </div>
  206. </div>
  207. </div>`);
  208. this.toggle(dialog, false);
  209. let acceptBtn = dialog.find(`#${acceptBtnName}`);
  210. acceptBtn.on('click', () => {
  211. this.acceptClick();
  212. });
  213. let cancelBtn = dialog.find(`#${cancelBtnName}`);
  214. cancelBtn.on('click', () => {
  215. this.toggle(dialog);
  216. });
  217. $('body').append(dialog);
  218. this.acLikeExt.dialog = dialog;
  219. }
  220. addEmoji() {
  221. return __awaiter(this, void 0, void 0, function* () {
  222. let face = $('.face-text');
  223. let that = this;
  224. face.css({ display: 'flex', 'align-items': 'center' });
  225. let emoji = $(`<div class="${emojiName}" style="margin-left: 10px; cursor: pointer;">emoji</div>`);
  226. emoji.on('click', function (e) {
  227. that.acMainExt.showEmojiMenu({
  228. e
  229. });
  230. return false;
  231. });
  232. face.find('span').after(emoji);
  233. });
  234. }
  235. acceptClick() {
  236. let dialog = this.acLikeExt.dialog;
  237. let time = dialog.find(`#${timeInputName}`).attr('value');
  238. time = new Number(time);
  239. if (isNaN(time) || time < 100) {
  240. return alert('请输入正确的时间');
  241. }
  242. this.toggle(dialog);
  243. this.toggleLike(time);
  244. }
  245. toggleLike(time) {
  246. if (!likeBtn)
  247. return alert('当前页面不支持');
  248. if (this.acLikeExt.acLike) {
  249. console.log('停止点赞');
  250. clearInterval(this.acLikeExt.acLike);
  251. this.acLikeExt.acLike = 0;
  252. }
  253. else {
  254. console.log('开始点赞');
  255. this.acLikeExt.acLike = setInterval(function () {
  256. if (window.acMainExt.shouldPause)
  257. return;
  258. likeBtn.click();
  259. }, time);
  260. }
  261. }
  262. init() {
  263. this.initStyle();
  264. this.initView();
  265. }
  266. run() {
  267. // 运行中,停止
  268. if (this.acLikeExt.acLike) {
  269. this.toggleLike();
  270. }
  271. else {
  272. let dialog = this.acLikeExt.dialog;
  273. this.toggle(dialog, true);
  274. }
  275. }
  276. }
  277. exports.AcLiveExt = AcLiveExt;
  278.  
  279. });
  280.  
  281. unwrapExports(live);
  282. live.AcLiveExt;
  283.  
  284. var lib = createCommonjsModule(function (module, exports) {
  285. Object.defineProperty(exports, "__esModule", { value: true });
  286. exports.AcMainExt = void 0;
  287.  
  288.  
  289.  
  290. const acfunHost = 'https://www.acfun.cn';
  291. const prefix = `${config.prefix}-main-`;
  292. let floatMenuName = `${prefix}float-menu`;
  293. let defaultMenuName = `${prefix}default-menu`;
  294. let contextMenuName = `${prefix}context-menu`;
  295. let emojiMenuName = `${prefix}emoji-menu`;
  296. let emojiMenuMainName = `${prefix}emoji-menu-main`;
  297. let emojiItemName = `${prefix}emoji-item`;
  298. let transparentName = `${prefix}tp`;
  299. class AcMainExt {
  300. constructor() {
  301. this.init();
  302. }
  303. initStyle() {
  304. let style = $('<style></style>').text(`
  305. .${transparentName} {
  306. background: transparent;
  307. }
  308. .${floatMenuName} {
  309. display: none;
  310. min-width: 100px;
  311. min-height: 20px;
  312. position: fixed;
  313. z-index: 3;
  314. }
  315. .${defaultMenuName} {
  316. background: white;
  317. padding: 15px 15px;
  318. cursor: pointer;
  319. border-radius: 5px;
  320. box-shadow: 1px 1px 5px #888888;
  321. }
  322. .${contextMenuName} {
  323. }
  324. .${contextMenuName} > * {
  325. padding: 5px 0;
  326. }
  327. .${emojiMenuName} {
  328. }
  329. .${emojiMenuMainName} {
  330. width: 260px;
  331. max-height: 150px;
  332. overflow-y: auto;
  333. font-size: 16px;
  334. }
  335. .${emojiItemName} {
  336. display: inline-block;
  337. padding: 2px;
  338. text-align: center;
  339. width: 25px;
  340. height: 25px;
  341. }
  342. .${emojiItemName}:hover {
  343. background-color: #f5f5f5;
  344. }
  345. .${prefix}menu {
  346. position: fixed; top: 100px; right: 10px;
  347. }
  348. .${prefix}menu > * {
  349. margin-bottom: 10px;
  350. }
  351. .${prefix}menu-item {
  352. background: white;
  353. border-radius: 50%; border: 1px solid #dcdee2;
  354. width: 40px; height: 40px;
  355. z-index: 1000;
  356. cursor: pointer;
  357. display: flex;
  358. justify-content: center;
  359. align-items: center;
  360. animation:${prefix}show 1s;
  361. }
  362. .${prefix}hide {
  363. display: none;
  364. }
  365. @keyframes ${prefix}show {
  366. from {opacity:0;}
  367. to {opacity:1;}
  368. }
  369. `);
  370. $('head').append(style);
  371. }
  372. get isLive() {
  373. return /live\/[\d]+/.test(location.href);
  374. }
  375. get shouldPause() {
  376. return $(`.${floatMenuName}`).is(`:visible`)
  377. || $(`.${prefix}menu-sub-item`).is(`:visible`)
  378. // 礼物
  379. || $('.container-gifts').hasClass('unfold')
  380. // 牌子详情
  381. || !$('.medal-panel-wrapper').hasClass('hide');
  382. }
  383. initView() {
  384. this.addMenu();
  385. }
  386. addMenu() {
  387. let dom = $(`<div class="${prefix}menu"></div>`);
  388. let items = [];
  389. // 直播
  390. if (this.isLive) {
  391. let liveBtn = $(`
  392. <svg width="20" viewBox="0 0 70 60">
  393. <path d="M0 10 L10 10 L10 0 L30 0 L30 10 L40 10 L40 0 L60 0 L60 10 L70 10 L70 30 L60 30 L60 40 L50 40 L50 50 L40 50 L40 60 L30 60 L30 50 L20 50 L20 40 L10 40 L10 30 L0 30 Z" fill="red" fill-rule="evenodd" />
  394. </svg>
  395. `);
  396. liveBtn.on('click', () => {
  397. window.acLiveExt.run();
  398. return false;
  399. });
  400. items.push(liveBtn);
  401. live.AcLiveExt.globalInit();
  402. }
  403. // 多菜单
  404. if (items.length > 1) {
  405. let mainMenuItem = $(`
  406. <svg viewBox="0 0 100 80" width="20" height="40">
  407. <rect width="100" height="20"></rect>
  408. <rect y="30" width="100" height="20"></rect>
  409. <rect y="60" width="100" height="20"></rect>
  410. </svg>
  411. `);
  412. mainMenuItem.on('click', () => {
  413. this.toggle($(`.${prefix}menu-sub-item`));
  414. return false;
  415. });
  416. $(document).on('click', () => {
  417. this.toggle($(`.${prefix}menu-sub-item`), false);
  418. });
  419. items.unshift(mainMenuItem);
  420. }
  421. if (items.length) {
  422. items = items.map((ele, idx) => {
  423. let dom = $('<div></div>');
  424. if (idx > 0) {
  425. dom.addClass(`${prefix}menu-sub-item`).addClass(`${prefix}hide`);
  426. }
  427. dom.addClass(`${prefix}menu-item`).append(ele);
  428. return dom;
  429. });
  430. dom.append(items);
  431. $('body').append(dom);
  432. }
  433. }
  434. init() {
  435. this.initStyle();
  436. this.initView();
  437. this.initAvatar();
  438. this.initDanmaku();
  439. }
  440. // 查看头像
  441. initAvatar() {
  442. let that = this;
  443. $(document).on('contextmenu', function (e) {
  444. let dom = $(e.target);
  445. // console.log(dom)
  446. let list = [{
  447. // 直播up主
  448. selector: '.live-author-avatar',
  449. getUrl: (dom) => {
  450. return dom.find('.live-author-avatar-img').attr('src');
  451. }
  452. }, {
  453. // 榜单
  454. selector: '.avatar',
  455. getUrl: (dom) => {
  456. return dom.find('.head-img').attr('src');
  457. }
  458. }, {
  459. // 主页
  460. selector: '.container-cover',
  461. getUrl: (dom) => {
  462. let url = dom.find('.user-photo').css('background-image');
  463. // 格式为url("......")
  464. url = url.substring(5, url.length - 2);
  465. return url;
  466. }
  467. }, {
  468. // 稿件up
  469. selector: '.up-avatar',
  470. getUrl: (dom) => {
  471. return dom.find('.avatar').attr('src');
  472. }
  473. }, {
  474. // 评论
  475. selector: '.area-comment-left .thumb',
  476. getUrl: (dom) => {
  477. return dom.find('.avatar').attr('src');
  478. }
  479. }, {
  480. // 盖楼评论
  481. selector: '.mci-avatar',
  482. getUrl: (dom) => {
  483. return dom.find('img.fc-avatar').attr('src');
  484. }
  485. }, {
  486. // 我的消息
  487. selector: '.avatar-section',
  488. getUrl: (dom) => {
  489. return dom.find('.avatar').attr('src');
  490. }
  491. },];
  492. for (let ele of list) {
  493. let matchedDom;
  494. if (dom.is(ele.selector)) {
  495. matchedDom = dom;
  496. }
  497. else {
  498. let p = dom.parents(ele.selector);
  499. if (p.length)
  500. matchedDom = p;
  501. }
  502. if (matchedDom) {
  503. let avatar = ele.getUrl(matchedDom);
  504. if (avatar) {
  505. avatar = avatar.split('?')[0];
  506. that.showContextMenu({
  507. e,
  508. avatar
  509. });
  510. return false;
  511. }
  512. }
  513. }
  514. });
  515. }
  516. // 弹幕
  517. initDanmaku() {
  518. let that = this;
  519. $(document).on('contextmenu', '.comment', function (e) {
  520. let dom = $(this).find('.nickname');
  521. let danmaku = dom.data('comment');
  522. that.showContextMenu({
  523. e,
  524. danmaku
  525. });
  526. return false;
  527. });
  528. }
  529. toggle(dom, show) {
  530. let hideCls = `${prefix}hide`;
  531. show = show !== null && show !== void 0 ? show : dom.hasClass(hideCls);
  532. if (show)
  533. dom.removeClass(hideCls);
  534. else
  535. dom.addClass(hideCls);
  536. }
  537. getClickPos(e) {
  538. return {
  539. x: e.originalEvent.x || 0,
  540. y: e.originalEvent.y || 0,
  541. };
  542. }
  543. showContextMenu(opt) {
  544. let { e, avatar, danmaku } = opt;
  545. let pos = this.getClickPos(e);
  546. let list = [];
  547. if (avatar) {
  548. list.push({
  549. text: '查看头像',
  550. key: 'openImg',
  551. data: avatar
  552. });
  553. }
  554. if (danmaku) {
  555. list.push({
  556. text: '复制弹幕',
  557. key: 'copyDanmaku',
  558. data: danmaku
  559. });
  560. let acNumRegRs = /(ac[\d]+)/i.exec(danmaku);
  561. if (acNumRegRs) {
  562. let acNo = acNumRegRs[1].toLowerCase();
  563. list.push({
  564. text: `打开视频(${acNo})`,
  565. key: 'openAcVideo',
  566. data: acNo
  567. });
  568. }
  569. let urlRegRs = /(http[^\s]+)/i.exec(danmaku);
  570. if (urlRegRs) {
  571. let url = urlRegRs[1];
  572. list.push({
  573. text: '打开链接',
  574. key: 'openUrl',
  575. data: url
  576. });
  577. }
  578. }
  579. this.createMenu(list, pos);
  580. }
  581. getMenu() {
  582. let that = this;
  583. let menu = $(`.${contextMenuName}`);
  584. if (!menu.length) {
  585. $(document).on('click', function () {
  586. that.getMenu().hide();
  587. });
  588. $(document).on('click', `.${prefix}context-menu-item`, function (e) {
  589. let dom = $(this);
  590. let item = dom.data('item');
  591. that.handleMenuItem(item);
  592. });
  593. menu = $(`<div class="${contextMenuName} ${floatMenuName} ${defaultMenuName}"></div>`);
  594. $('body').append(menu);
  595. }
  596. return menu;
  597. }
  598. createMenu(item, pos) {
  599. let menu = this.getMenu();
  600. menu
  601. .css({
  602. left: pos.x + 'px',
  603. top: pos.y + 'px',
  604. })
  605. .empty()
  606. .append(item.map(ele => {
  607. let dom = $(`<div class="${prefix}context-menu-item">${ele.text}</div>`);
  608. dom.data('item', ele);
  609. return dom;
  610. })).show();
  611. }
  612. handleMenuItem(item) {
  613. let url;
  614. switch (item.key) {
  615. case 'openImg':
  616. case 'openUrl':
  617. url = item.data;
  618. break;
  619. case 'openAcVideo':
  620. url = `${acfunHost}/v/${item.data}`;
  621. break;
  622. case 'copyDanmaku':
  623. utils.clipboardCopy(item.data);
  624. break;
  625. }
  626. if (url) {
  627. window.open(url, '__blank');
  628. }
  629. }
  630. getEmojiMenu() {
  631. let that = this;
  632. let menu = $(`.${emojiMenuName}`);
  633. if (!menu.length) {
  634. menu = $(`<div class="${emojiMenuName} ${floatMenuName} ${transparentName}"></div>`);
  635. let main = $(`<div class="${emojiMenuMainName} ${defaultMenuName}"></div>`);
  636. menu.append(main);
  637. menu.append(`<div class="${transparentName}" style="height: 30px"></div>`);
  638. /*
  639. emoji
  640. http://www.amp-what.com/unicode/search/emoticon
  641. */
  642. let list = [
  643. [9889, 129313, 129397, 128131, 127863],
  644. { from: 128512, to: 128591 },
  645. // 动物
  646. { from: 128045, to: 128060 },
  647. ];
  648. let arr = [];
  649. list.forEach(ele => {
  650. if (ele instanceof Array) {
  651. arr.push(...ele);
  652. }
  653. else {
  654. for (let i = ele.from; i <= ele.to; i++) {
  655. arr.push(i);
  656. }
  657. }
  658. });
  659. let doms = [];
  660. arr.forEach(i => {
  661. let value = typeof i === 'string' ? i : `&#${i};`;
  662. doms.push(`<div class="${emojiItemName}" data-value="${value}">${value}</div>`);
  663. });
  664. main.append(doms.join(''));
  665. $('body').append(menu);
  666. $(document).on('mouseleave', `.${emojiMenuName}`, function (e) {
  667. that.getEmojiMenu().hide();
  668. });
  669. let rangeIndex;
  670. let input = $('.live-feed-input .danmaku-input');
  671. input.on('blur', function () {
  672. rangeIndex = this.selectionStart;
  673. });
  674. $(document).on('click', `.${emojiItemName}`, function () {
  675. let dom = $(this);
  676. let v = dom.data('value');
  677. utils.insert({
  678. input: input[0],
  679. text: v,
  680. rangeIndex
  681. });
  682. });
  683. }
  684. return menu;
  685. }
  686. showEmojiMenu(opt) {
  687. let { e, } = opt;
  688. let pos = this.getClickPos(e);
  689. let menu = this.getEmojiMenu();
  690. menu
  691. .css({
  692. left: pos.x + 'px',
  693. top: (pos.y - menu.outerHeight() + 10) + 'px',
  694. }).show();
  695. }
  696. }
  697. exports.AcMainExt = AcMainExt;
  698. window.acMainExt = new AcMainExt();
  699.  
  700. });
  701.  
  702. var index = unwrapExports(lib);
  703. var lib_1 = lib.AcMainExt;
  704.  
  705. exports.AcMainExt = lib_1;
  706. exports["default"] = index;
  707.  
  708. Object.defineProperty(exports, '__esModule', { value: true });
  709.  
  710. return exports;
  711.  
  712. })({});

QingJ © 2025

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