哎呦不错哦(任何事先看简介)

好东西哦(⊙﹏⊙)

  1. // ==UserScript==
  2. // @name 哎呦不错哦(任何事先看简介)
  3. // @name:zh-TW 哎呦不错哦(任何事先簡介)
  4. // @name:zh-HK 哎呦不错哦(任何事先簡介)
  5. // @namespace http://tampermonkey.net/
  6. // @version 1.6.8
  7. // @description 好东西哦(⊙﹏⊙)
  8. // @description:zh-TW 好东西哦(⊙﹏⊙)
  9. // @description:zh-HK 好东西哦(⊙﹏⊙)
  10. // @author hua
  11. // @match https://www.youtube.com/*
  12. // @match https://m.youtube.com/*
  13. // @match https://music.youtube.com/*
  14. // @match https://www.youtubekids.com/*
  15. // @exclude https://www.youtube.com/live_chat*
  16. // @exclude https://www.youtube.com/embed*
  17. // @connect https://api.cobalt.tools
  18. // @grant unsafeWindow
  19. // @grant GM_xmlhttpRequest
  20. // @grant GM_setValue
  21. // @grant GM_getValue
  22. // @grant GM_info
  23. // @grant GM_addValueChangeListener
  24. // @grant GM_removeValueChangeListener
  25. // @grant GM_setClipboard
  26. // @grant GM_listValues
  27. // @grant GM_deleteValue
  28. // @run-at document-start
  29. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  30. // @license MIT
  31. // ==/UserScript==
  32. // https://api.cobalt.tools 视频解析API This is a used to download video parsing API
  33.  
  34.  
  35.  
  36. (function () {
  37. let uuid = GM_getValue('uuid');
  38. if (!uuid) {
  39. uuid = crypto.randomUUID().substring(0, Math.floor(Math.random() * 5) + 6).replace(/-/g, '');
  40. GM_setValue('uuid', uuid);
  41. }
  42.  
  43. if (unsafeWindow[uuid]) {
  44. console.log('重复注入!');
  45. return;
  46. }
  47.  
  48. unsafeWindow[uuid] = true;
  49.  
  50. let debugger_fun_name;
  51.  
  52. const disableRemovePlayerAd = false;
  53.  
  54. const open_config_keyword = '2333';
  55.  
  56. const display_error_keyword = '2444';
  57.  
  58. const reset_config_keyword = '2555';
  59.  
  60. let channel_id = GM_getValue('last_channel_id', 'default');
  61.  
  62. const user_data_listener = get_user_data_listener();
  63.  
  64. const user_data_api = get_user_data_api();
  65.  
  66. let user_data = user_data_api.get();
  67.  
  68. let tmp_debugger_value;
  69.  
  70. let limit_eval = false;
  71.  
  72. let element_monitor_observer;
  73.  
  74. let real_language = user_data.language;
  75.  
  76. let is_account_init;
  77.  
  78. let fake_fetch;
  79.  
  80. const inject_info = {
  81. "ytInitialPlayerResponse": false,
  82. "ytInitialData": false,
  83. "ytInitialReelWatchSequenceResponse": false,
  84. "xhr": false,
  85. "fetch": false
  86. };
  87.  
  88. const $ = unsafeWindow.document.querySelector.bind(unsafeWindow.document);
  89. const $$ = unsafeWindow.document.querySelectorAll.bind(unsafeWindow.document);
  90.  
  91. const origin_console = console;
  92. const script_url = 'https://update.gf.qytechs.cn/scripts/480192/youtube%E5%B9%BF%E5%91%8A%E6%8B%A6%E6%88%AA.user.js';
  93. let href = location.href;
  94. let ytInitialPlayerResponse_rule;
  95. let ytInitialData_rule;
  96. let ytInitialReelWatchSequenceResponse_rule;
  97. let open_debugger = false;
  98. let isinint = false;
  99. let mobile_web;
  100. let movie_channel_info;
  101. let mobile_movie_channel_info;
  102. let flag_info;
  103.  
  104. let debugger_ytInitialPlayerResponse;
  105. let debugger_ytInitialData;
  106. let debugger_ytInitialReelWatchSequenceResponse;
  107. let debugger_music_initialData;
  108. const error_messages = [];
  109. let data_process = get_data_process();
  110. let shorts_fun = get_shorts_fun();
  111. let yt_api = get_yt_api();
  112. const shorts_parse_delay = 500;
  113. const browser_info = getBrowserInfo();
  114. let page_type = get_page_type();
  115. const config_api = get_config_api();
  116. if (disableRemovePlayerAd) {
  117. config_api.common_ytInitialPlayerResponse_rule = config_api.common_ytInitialPlayerResponse_rule.slice(3);
  118. }
  119. const SPLIT_TAG = '###';
  120. let cur_watch_channle_id;
  121. const trustedScript = trustedScriptInit();
  122. setSecurePolicy();
  123. init();
  124.  
  125. function init() {
  126. log('初始化开始!' + href, 0);
  127. url_observer();
  128. is_account_init = false;
  129. data_process.set_obj_filter(obj_process_filter);
  130. config_api.config_init(user_data.language);
  131. const init_hook = init_hook_collection();
  132. init_hook.property();
  133. init_hook.other();
  134. init_hook.request();
  135.  
  136. unsafeWindow.document.addEventListener('DOMContentLoaded', function () {
  137. set_search_listen();
  138. // check_update();
  139. on_page_change();
  140. });
  141.  
  142. isinint = true;
  143. log('初始化结束!' + href, 0);
  144. open_debugger && set_debugger();
  145. }
  146.  
  147. function setSecurePolicy() {
  148. if (!unsafeWindow.isSecureContext || !unsafeWindow.trustedTypes?.createPolicy) return;
  149. try {
  150. unsafeWindow.trustedTypes.createPolicy("default", {
  151. createScriptURL: (url) => url,
  152. createHTML: (html) => html,
  153. createScript: (script) => script
  154. });
  155. } catch (error) {
  156. }
  157. }
  158.  
  159. function trustedScriptInit() {
  160.  
  161. try {
  162. let test_value;
  163. eval('test_eval = 1');
  164. return function (str) {
  165. return str;
  166. };
  167. } catch (error) {
  168. if (unsafeWindow.trustedTypes) {
  169. const policy = unsafeWindow.trustedTypes.createPolicy('eval', {
  170. createScript: (script) => script
  171. });
  172. return function (str) {
  173. return policy.createScript(str);
  174. };
  175. } else {
  176. log('trustedTypes not support', error, -1);
  177. }
  178. };
  179. };
  180.  
  181. function init_hook_collection() {
  182. return {
  183. property() {
  184. const already_inject = [];
  185. let ytInitialPlayerResponse_value = unsafeWindow['ytInitialPlayerResponse'];
  186. function process_property(name, value, rule, reverse = false) {
  187. if (!value) return value;
  188. if (already_inject.includes(name)) {
  189. log(`${name} 重复修改被拦截`, 0);
  190. return value;
  191. }
  192. const start_time = Date.now();
  193. if (typeof value === 'object') {
  194. already_inject.push(name);
  195. open_debugger && !limit_eval && !eval(trustedScript(`debugger_${name}`)) && (eval(trustedScript(`debugger_${name} = JSON.parse(JSON.stringify(value))`)));
  196. rule && data_process.obj_process(value, rule, reverse);
  197. }
  198. if (typeof value === 'string') {
  199. already_inject.push(name);
  200. open_debugger && !limit_eval && !eval(trustedScript(`debugger_${name}`)) && (eval(trustedScript(`debugger_${name} = JSON.parse(value)`)));
  201. value = data_process.text_process(value, rule, 'insert', reverse);
  202. }
  203. log(`${name} 时间:`, Date.now() - start_time, 'spend_time');
  204. return value;
  205. }
  206.  
  207. define_property_hook(unsafeWindow, 'ytInitialPlayerResponse', {
  208. get: function () {
  209. return ytInitialPlayerResponse_value;
  210. },
  211. set: function (value) {
  212. inject_info.ytInitialPlayerResponse = true;
  213. value = process_property('ytInitialPlayerResponse', value, config_api.common_ytInitialPlayerResponse_rule);
  214. ytInitialPlayerResponse_value = value;
  215. },
  216. configurable: false
  217. });
  218. let ytInitialReelWatchSequenceResponse_value = unsafeWindow['ytInitialReelWatchSequenceResponse'];
  219. define_property_hook(unsafeWindow, 'ytInitialReelWatchSequenceResponse', {
  220. get: function () {
  221. return ytInitialReelWatchSequenceResponse_value;
  222. },
  223. set: function (value) {
  224. inject_info.ytInitialReelWatchSequenceResponse = true;
  225. if (['yt_shorts', 'mobile_yt_shorts'].includes(page_type)) {
  226. value = process_property('ytInitialReelWatchSequenceResponse', value,
  227. config_api.get_rules(mobile_web ? 'yt_shorts_mobile' : 'yt_shorts').ytInitialReelWatchSequenceResponse_rule);
  228. }
  229. ytInitialReelWatchSequenceResponse_value = value;
  230. },
  231. configurable: false
  232. });
  233.  
  234. let ytInitialData_value = unsafeWindow['ytInitialData'];
  235. define_property_hook(unsafeWindow, 'ytInitialData', {
  236. get: function () {
  237. return ytInitialData_value;
  238. },
  239. set: function (value) {
  240. inject_info.ytInitialData = true;
  241. let rules = config_api.get_rules(page_type);
  242. !['yt_watch', 'mobile_yt_watch', 'mobile_yt_watch_searching'].includes(page_type) && (rules = rules.ytInitialData_rule);
  243. value = process_property('ytInitialData', value, rules);
  244. ytInitialData_value = value;
  245. },
  246. configurable: false
  247. });
  248.  
  249. const origin_ua = navigator.userAgent;
  250. define_property_hook(navigator, 'userAgent', {
  251. get: function () {
  252. return (browser_info.isMobile || browser_info.name === 'Chrome') ? origin_ua : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36';
  253. }
  254. });
  255. if (unsafeWindow.ytcfg) {
  256. if (unsafeWindow.ytcfg.data_ && typeof (unsafeWindow.ytcfg.data_.LOGGED_IN) === 'boolean') {
  257. account_data_init(unsafeWindow.ytcfg.data_.LOGGED_IN);
  258. } else {
  259. if (unsafeWindow.ytcfg.data_ && typeof unsafeWindow.ytcfg.data_ === 'object') {
  260. define_property_hook(unsafeWindow.ytcfg.data_, 'LOGGED_IN', {
  261. get: function () {
  262. return unsafeWindow.ytcfg.data_.LOGGED_IN_;
  263. },
  264. set: function (value) {
  265. unsafeWindow.ytcfg.data_.LOGGED_IN_ = value;
  266. account_data_init(value);
  267. }
  268. });
  269. }
  270. }
  271. if (!unsafeWindow.ytcfg.data_) {
  272. if (unsafeWindow.yt?.config_) {
  273. const config_ = unsafeWindow.yt.config_;
  274. if (typeof (config_.LOGGED_IN) === 'boolean') {
  275. account_data_init(config_.LOGGED_IN);
  276. }
  277. config_.HL && config_api.config_init(config_.HL);
  278. }
  279. } else {
  280. if (unsafeWindow.ytcfg.data_?.HL) {
  281. config_api.config_init(unsafeWindow.ytcfg.data_.HL);
  282. } else {
  283. if (unsafeWindow.ytcfg.msgs) {
  284. unsafeWindow.ytcfg.msgs.__lang__ && config_api.config_init(unsafeWindow.ytcfg.msgs.__lang__);
  285. } else {
  286. unsafeWindow.ytcfg._msgs = unsafeWindow.ytcfg.msgs;
  287. define_property_hook(unsafeWindow.ytcfg, 'msgs', {
  288. get: function () {
  289. return this._msgs;
  290. },
  291. set: function (newValue) {
  292. if (newValue.__lang__) config_api.config_init(newValue.__lang__);
  293. this._msgs = newValue;
  294. }
  295. });
  296. }
  297. }
  298. }
  299.  
  300. } else {
  301. define_property_hook(unsafeWindow, 'ytcfg', {
  302. get: function () {
  303. return this._ytcfg;
  304. },
  305. set: function (newValue) {
  306. // 过滤 a = a || 1 这种
  307. if (newValue === unsafeWindow.ytcfg) return;
  308. if (newValue.set) {
  309. const origin_set = newValue.set;
  310. newValue.set = function () {
  311. //hook ytmusic 的初始化数据 YTMUSIC_INITIAL_DATA
  312. if (arguments?.[0].YTMUSIC_INITIAL_DATA) {
  313. const yt_music_init_data = arguments[0].YTMUSIC_INITIAL_DATA;
  314. if (yt_music_init_data?.length > 0) {
  315. const browse_data = yt_music_init_data[1];
  316. if (browse_data.path === '/browse') {
  317. const rule = config_api.get_rules('yt_music').ytInitialData_rule;
  318. browse_data.data = process_property('music_initialData', browse_data.data, rule);
  319. }
  320. }
  321. }
  322. origin_set.apply(this, arguments);
  323. if (arguments[0] && typeof arguments[0].LOGGED_IN === 'boolean') {
  324. account_data_init(arguments[0].LOGGED_IN);
  325. }
  326. if (arguments[0].HL) {
  327. config_api.config_init(arguments[0].HL);
  328. }
  329. };
  330. }
  331. this._ytcfg = newValue;
  332. }
  333. });
  334. }
  335. },
  336. other() {
  337. const origin_createElement = unsafeWindow.document.createElement;
  338. unsafeWindow.document.createElement = function () {
  339. const node = origin_createElement.apply(this, arguments);
  340. if (arguments[0] === 'IFRAME') {
  341. const contentWindow_getter = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, "contentWindow").get;
  342. define_property_hook(node, 'contentWindow', {
  343. get: function () {
  344. const contentWindow = contentWindow_getter.call(node);
  345. if (!contentWindow || this.src !== 'about:blank' || contentWindow.change_history) return contentWindow;
  346. set_history_hook(contentWindow);
  347. contentWindow.fetch = fake_fetch;
  348. contentWindow.change_history = true;
  349. return contentWindow;
  350. }
  351. });
  352. }
  353. return node;
  354. };
  355. unsafeWindow.document.createElement.toString = origin_createElement.toString.bind(origin_createElement);
  356.  
  357. },
  358. request() {
  359. async function deal_response(name, response, rule) {
  360. if (!rule) return response;
  361. let is_deal = false;
  362. const responseClone = response.clone();
  363. let result = await responseClone.text();
  364. let origin_result = result;
  365. if (name === 'subscribe' || name === 'unsubscribe') {
  366. let match_list = result.match(/channelId":\"(.*?)"/);
  367. const match_channel_id = match_list && match_list.length > 1 ? match_list[1] : '';
  368. let channel_infos = user_data.channel_infos;
  369. if (match_channel_id) {
  370. if (name === 'unsubscribe') {
  371. let index = channel_infos.ids.indexOf(match_channel_id);
  372. if (index > -1) {
  373. channel_infos.ids.splice(index, 1);
  374. channel_infos.names.splice(index, 1);
  375. }
  376. } else {
  377. channel_infos.ids.push(match_channel_id);
  378. channel_infos.names.push('');
  379. }
  380. user_data.channel_infos = channel_infos;
  381. user_data_api.set();
  382. log(name, match_channel_id, 0);
  383. }
  384. is_deal = true;
  385. }
  386. if (name === 'playlist') {
  387. let obj;
  388. try {
  389. obj = JSON.parse(result);
  390. data_process.obj_process(obj.playerResponse, config_api.common_ytInitialPlayerResponse_rule, false);
  391. data_process.obj_process(obj.response, config_api.get_rules('yt_watch', 'init'), false);
  392. result = JSON.stringify(obj);
  393. } catch (error) {
  394. log('playlist 解析失败', error, -1);
  395. result = origin_result;
  396. }
  397. is_deal = true;
  398. }
  399. if (!is_deal) {
  400. let start_time = Date.now();
  401. result = data_process.text_process(result, rule, 'insert', false);
  402. log(name + ' 时间:', Date.now() - start_time, 'spend_time');
  403. }
  404. if (!result) {
  405. result = origin_result;
  406. debugger;
  407. }
  408. return new Response(result, response);
  409. }
  410. const origin_fetch = unsafeWindow.fetch;
  411. if (!check_native('fetch', origin_fetch)) {
  412. log('fetch have been modified', -1);
  413. }
  414. fake_fetch = function () {
  415. const fetch_ = async function (uri, options) {
  416. async function fetch_request(response) {
  417. let url = response.url;
  418. inject_info.fetch = true;
  419. let request_body;
  420. try {
  421. request_body = uri.body_ ? JSON.parse(uri.body_) : null;
  422. } catch (error) {
  423. request_body = null;
  424. }
  425. if (url.includes('youtubei/v1/next')) {
  426. const rule = config_api.get_rules(mobile_web ? 'mobile_yt_watch' : 'yt_watch', request_body?.videoId ? "init" : 'next');
  427. return await deal_response('next', response, rule);
  428. }
  429. if (url.includes('youtubei/v1/player')) {
  430. return await deal_response('player', response, config_api.common_ytInitialPlayerResponse_rule);
  431. }
  432. if (url.includes('youtubei/v1/reel/reel_watch_sequence')) {
  433. const rule = config_api.get_rules(mobile_web ? 'mobile_yt_shorts' : 'yt_shorts').ytInitialReelWatchSequenceResponse_rule;
  434. return await deal_response('reel_watch_sequence', response, rule);
  435. }
  436. if (url.includes('youtubei/v1/reel/reel_item_watch')) {
  437. // shorts 内容
  438. const rule = config_api.get_rules(mobile_web ? 'mobile_yt_shorts' : 'yt_shorts').ytInitialData_rule;
  439. return await deal_response('reel_item_watch', response, rule);
  440. }
  441. if (url.includes('youtubei/v1/browse?prettyPrint=false')) {
  442. let browse_id = request_body?.browseId;
  443. let rule;
  444. if (href.includes('https://music.youtube.com/')) {
  445. rule = config_api.get_rules('yt_music', 'browse').ytInitialData_rule;
  446. }
  447. // 忽略音乐主页 影视主页
  448. if (!rule && (['yt_home', 'mobile_yt_home'].includes(page_type) || browse_id === 'FEwhat_to_watch')) {
  449. if (!browse_id) {
  450. let node, category_text, node_list, node_index;
  451. if (mobile_web) {
  452. node = $('#filter-chip-bar > div > ytm-chip-cloud-chip-renderer.selected');
  453. node_list = $$('#filter-chip-bar > div > ytm-chip-cloud-chip-renderer');
  454. node_index = Array.from(node_list).indexOf(node);
  455. if (node_index !== 1) return response;
  456. } else {
  457. node = $('#chips > yt-chip-cloud-chip-renderer.style-scope.ytd-feed-filter-chip-bar-renderer.iron-selected');
  458. node_list = $$('#chips > yt-chip-cloud-chip-renderer.style-scope.ytd-feed-filter-chip-bar-renderer');
  459. node_index = Array.from(node_list).indexOf(node);
  460. if (node_index !== 0) return response;
  461. }
  462.  
  463. }
  464. rule = config_api.get_rules(mobile_web ? 'mobile_yt_home' : 'yt_home', request_body?.browseId ? 'init' : 'browse').ytInitialData_rule;
  465. }
  466.  
  467. return await deal_response('browse', response, rule);
  468. }
  469. if (url.startsWith('https://www.youtube.com/playlist?list=')) {
  470. return await deal_response('playlist', response, []);
  471. }
  472. // if (url.includes('https://m.youtube.com/youtubei/v1/guide')) {
  473. // return response;
  474. // }
  475. if (url.includes('/youtubei/v1/search')) {
  476. const rule = config_api.get_rules(mobile_web ? 'mobile_yt_search' : 'yt_search').ytInitialData_rule;
  477. return await deal_response('search', response, rule);
  478. }
  479. if (url.includes('/unsubscribe?prettyPrint=false')) {
  480. return await deal_response('unsubscribe', response, []);
  481. }
  482. if (url.includes('/subscribe?prettyPrint=false')) {
  483. return await deal_response('subscribe', response, []);
  484. }
  485. return response;
  486. }
  487. return origin_fetch(uri, options).then(fetch_request);
  488. };
  489. return fetch_;
  490. }();
  491. unsafeWindow.fetch = fake_fetch;
  492. unsafeWindow.fetch.toString = origin_fetch.toString.bind(origin_fetch);
  493. const origin_Request = unsafeWindow.Request;
  494. if (!check_native('Request', origin_Request)) {
  495. log('Request have been modified', -1);
  496. }
  497. unsafeWindow.Request = class extends unsafeWindow.Request {
  498. constructor(input, options = void 0) {
  499. super(input, options);
  500. this.url_ = input;
  501. if (options && 'body' in options) this['body_'] = options['body'];
  502. }
  503. };
  504.  
  505. unsafeWindow.XMLHttpRequest = class extends unsafeWindow.XMLHttpRequest {
  506. open(method, url, ...opts) {
  507. inject_info.xhr = true;
  508. if (['mobile_yt_watch'].includes(page_type) && url.includes('m.youtube.com/watch?v')) {
  509. log('xhr watch 返回空', 0);
  510. return null;
  511. }
  512. if (['mobile_yt_home'].includes(page_type) && url.includes('m.youtube.com/?pbj')) {
  513. log('xhr home 返回空', 0);
  514. return null;
  515. }
  516. this.url_ = url;
  517. return super.open(method, url, ...opts);
  518. }
  519. send(body) {
  520. this.body_ = body;
  521. super.send(body);
  522. }
  523. get xhrResponseValue() {
  524. const xhr = this;
  525. if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
  526. let result = super.response;
  527. const url = xhr.responseURL;
  528. const result_type = typeof result;
  529. try {
  530. if (url.includes('youtubei/v1/player')) {
  531. // music_watch shorts
  532. if (result_type !== 'string') {
  533. log(`XHR ${url} 返回值不是字符串!`, 0);
  534. return result;
  535. };
  536. result = data_process.text_process(result, config_api.common_ytInitialPlayerResponse_rule, 'insert', false);
  537. return result;
  538. }
  539. if (url.includes('youtube.com/playlist')) {
  540. debugger;
  541. let obj;
  542. obj = JSON.parse(result);
  543. log(`出现 ${url} !`, 0);
  544. data_process.obj_process(obj[2].playerResponse, ytInitialPlayerResponse_rule, false);
  545. data_process.obj_process(obj[3].response, ytInitialData_rule, false);
  546. tmp_debugger_value = obj;
  547. result = JSON.stringify(obj);
  548. return result;
  549. }
  550. } catch (error) {
  551. log(`XHR ${url} 解析失败!`, error, -1);
  552. }
  553. }
  554. return super.response;
  555. }
  556. get responseText() {
  557. return this.xhrResponseValue;
  558. }
  559. get response() {
  560. return this.xhrResponseValue;
  561. }
  562. };
  563.  
  564. }
  565. };
  566. }
  567.  
  568. function on_page_change() {
  569. let analyzing_download_url = false;
  570. function common() {
  571. if (page_type === 'yt_shorts') {
  572. shorts_fun.check_shorts_exist();
  573. }
  574. }
  575.  
  576. function element_monitor() {
  577. element_monitor_observer?.disconnect();
  578. const configs = wait_configs[page_type] || [];
  579. if (configs.length === 0) return;
  580. const callback = function (mutationsList) {
  581. for (let i = configs.length - 1; i >= 0; i--) {
  582. const config = configs[i];
  583. const selector = config.seletor;
  584. const nodes = $$(selector);
  585. for (let node of nodes) {
  586. if (node.offsetHeight > 0) {
  587. if (config.inject) {
  588. if (!node.inject_xxxx) {
  589. node.inject_xxxx = true;
  590. }
  591. else {
  592. configs.splice(i, 1);
  593. break;
  594. }
  595. }
  596. if ('count' in config) {
  597. if (config.count > 0) {
  598. config.count--;
  599. if (config.count === 0) {
  600. configs.splice(i, 1);
  601. }
  602. }
  603. }
  604. // log(`找到${selector}`, 0);
  605. const funs = Array.isArray(config.fun) ? config.fun : [config.fun];
  606. for (let fun of funs) {
  607. fun(node);
  608. }
  609. break;
  610. }
  611. }
  612. }
  613. if (configs.length === 0) {
  614. log('monitor end', 0);
  615. element_monitor_observer.disconnect();
  616. return;
  617. }
  618. };
  619. element_monitor_observer = new MutationObserver(callback);
  620. element_monitor_observer.observe($('body'), {
  621. childList: true,
  622. subtree: true
  623. });
  624. }
  625.  
  626. const wait_configs = {
  627. "yt_shorts": [
  628. {
  629. "seletor": "ytd-reel-video-renderer[is-active] video",
  630. "inject": true,
  631. "fun": [shorts_auto_scroll, set_shorts_dbclick_like, set_shorts_progress]
  632. },
  633. {
  634. "seletor": "ytd-reel-video-renderer[is-active] #comments-button",
  635. "inject": true,
  636. "fun": [shorts_change_comment_click]
  637. },
  638. {
  639. "seletor": "ytd-reel-video-renderer[is-active] video",
  640. "count": 30,
  641. "fun": []
  642. },
  643. // {
  644. // "seletor": "ytd-reel-video-renderer[is-active] div#like-button #like-button button",
  645. // "inject": true,
  646. // "fun": [set_shorts_download]
  647. // }
  648. ],
  649. // "yt_watch": [{
  650. // "seletor": "div.YtSegmentedLikeDislikeButtonViewModelSegmentedButtonsWrapper",
  651. // "count": 30,
  652. // "fun": set_watch_download
  653. // }],
  654. // "yt_music_watch": [{
  655. // "seletor": "#layout > ytmusic-player-bar > div.middle-controls.style-scope.ytmusic-player-bar > div.middle-controls-buttons.style-scope.ytmusic-player-bar > #like-button-renderer > yt-button-shape.dislike > button",
  656. // "inject": true,
  657. // "fun": set_music_download
  658. // }],
  659. // "mobile_yt_watch": [{
  660. // "seletor": "div.YtSegmentedLikeDislikeButtonViewModelSegmentedButtonsWrapper",
  661. // "count": 10,
  662. // "fun": set_watch_download
  663. // }],
  664. "mobile_yt_shorts": [{
  665. "seletor": 'div.carousel-item[aria-hidden="false"] ytm-like-button-renderer',
  666. "count": 10,
  667. "fun": [shorts_auto_scroll, set_shorts_dbclick_like, set_shorts_progress]
  668. }],
  669. };
  670.  
  671. common();
  672. element_monitor();
  673. function set_dbclick(node, handler) {
  674. if (node.inject_dbclick) return;
  675. node.inject_dbclick = true;
  676. let corgin_onclick = node.onclick;
  677. let timers = [];
  678. node.onclick = node.onclick_ = function (event) {
  679. if ((node.dbclick_intercept_propagation || node.click_intercept_propagation)) {
  680. event.stopPropagation();
  681. }
  682. const timer = setTimeout(() => {
  683. if (node.dbclick_intercept_propagation && !node.click_intercept_propagation) {
  684. let parent = node.parentElement;
  685. if (parent) {
  686. let parentHandler = parent.onclick;
  687. if (typeof parentHandler === 'function') {
  688. parentHandler.call(parent, event);
  689. }
  690. parent.dispatchEvent(event);
  691. }
  692. }
  693. timers.splice(timers.indexOf(timer), 1);
  694. corgin_onclick?.call(this, event);
  695. }, 300);
  696. timers.push(timer);
  697. };
  698. define_property_hook(node, 'onclick', {
  699. get: function () {
  700. return this.onclick_;
  701. },
  702. set: function (fun) {
  703. corgin_onclick = fun;
  704. }
  705. });
  706. node.addEventListener('dblclick', function (event) {
  707. if (node.dbclick_intercept_propagation) event.stopPropagation();
  708. for (let timer of timers) {
  709. clearTimeout(timer);
  710. }
  711. timers.length = 0;
  712. handler?.call(this, event);
  713. });
  714. }
  715. function set_download_event(dislike_node, like_node, url = href) {
  716. if (!like_node) log('like_node is null');
  717. if (!dislike_node) log('dislike_node is null');
  718. if (!like_node || !dislike_node) {
  719. log('set_download_event error like_node or dislike_node is null', -1);
  720. return;
  721. };
  722. function randomColor() {
  723. var letters = '0123456789ABCDEF';
  724. var color = '#';
  725. for (var i = 0; i < 6; i++) {
  726. color += letters[Math.floor(Math.random() * 16)];
  727. }
  728. return color;
  729. }
  730. function set_node_download_event(node, type = 'like') {
  731. node.style.transition = `${node.style.transition && (node.style.transition + " , ")}background-color 0.5s ease`;
  732. node.backgroundColor_ = node.style.backgroundColor;
  733. node.resolve_background_color = function () {
  734. if (this.background_color_interval_id) clearInterval(this.background_color_interval_id);
  735. this.style.backgroundColor = this.backgroundColor_;
  736. };
  737. set_dbclick(node, () => {
  738. if (user_data.dbclick_download_video === 'off') return;
  739. const tips = type === 'like' ? flag_info.download_video_confirm_tips : flag_info.download_audio_confirm_tips;
  740. if (!confirm(`${unsafeWindow.document.title}\n\n${tips}`)) return;
  741. const result = video_download(url, type !== 'like', node);
  742. if (result) {
  743. node.background_color_interval_id = setInterval(() => {
  744. node.style.backgroundColor = randomColor();
  745. }, 100);
  746. }
  747. });
  748. }
  749. set_node_download_event(like_node, 'like');
  750. set_node_download_event(dislike_node, 'dislike');
  751. }
  752. function set_shorts_dbclick_like(video_node) {
  753. video_node = page_type === 'yt_shorts' ? video_node : $('div.carousel-item[aria-hidden="false"] div.video-wrapper');
  754. if (!video_node) return;
  755. video_node.dbclick_intercept_propagation = true;
  756. set_dbclick(video_node, function () {
  757. if (user_data.shorts_dbclick_like === 'off') return;
  758. const like_seltor = page_type === 'yt_shorts' ? 'ytd-reel-video-renderer[is-active] #like-button > yt-button-shape > label > button' : 'div.carousel-item[aria-hidden="false"] ytm-like-button-renderer button';
  759. $(like_seltor)?.click();
  760. });
  761. }
  762. function set_shorts_progress(node) {
  763. const video_node = page_type === 'yt_shorts' ? node : $('video');
  764. if (!video_node || video_node.inject_shorts_progress) return;
  765. video_node.inject_shorts_progress = true;
  766. video_node.addEventListener('timeupdate', function () {
  767. if (user_data.shorts_add_video_progress === 'off') return;
  768. const shape_button = page_type === 'yt_shorts' ? $('ytd-reel-video-renderer[is-active] #button-shape > button') : $('div.carousel-item[aria-hidden="false"] ytm-bottom-sheet-renderer button');
  769. if (!shape_button) return;
  770. const progress = video_node.currentTime / video_node.duration * 100;
  771. const transparency = page_type === 'yt_shorts' ? '0.05' : '0.3';
  772. const progress_color = page_type === 'yt_shorts' ? 'rgba(0, 0, 255, 0.4)' : 'rgba(255, 255, 0, 0.4)';
  773. shape_button.style.background = `linear-gradient(to top, ${progress_color} ${progress}%, rgba(0, 0, 0, ${transparency}) ${progress}%)`;
  774. });
  775. }
  776. function shorts_change_comment_click(comments_node) {
  777. const comments_button = comments_node.querySelector('ytd-button-renderer > yt-button-shape > label > button');
  778. const onclick_setter = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "onclick").set;
  779. const current_render_node = $('ytd-reel-video-renderer[is-active]');
  780. const wrap = function (fun) {
  781. return function (event) {
  782. const expand_node = current_render_node.querySelector('#watch-while-engagement-panel > ytd-engagement-panel-section-list-renderer:nth-child(1)');
  783. if (expand_node?.visibility === 'ENGAGEMENT_PANEL_VISIBILITY_EXPANDED') {
  784. const expand_close_node = current_render_node.querySelector('#visibility-button > ytd-button-renderer > yt-button-shape > button');
  785. expand_close_node?.click();
  786. } else {
  787. fun.call(this, event);
  788. }
  789. };
  790. };
  791. comments_button.onclick = comments_button.onclick_ = wrap(comments_button.onclick);
  792. define_property_hook(comments_button, 'onclick', {
  793. get: function () {
  794. return this.onclick_;
  795. },
  796. set: function (fun) {
  797. this.onclick_ = wrap(fun);
  798. onclick_setter.call(comments_button, this.onclick_);
  799. }
  800. });
  801. }
  802. function shorts_auto_scroll(video_node) {
  803. video_node = page_type === 'yt_shorts' ? video_node : $('video');
  804. if (!video_node) return;
  805. if (video_node?.inject_auto_scroll) return;
  806. video_node.inject_auto_scroll = true;
  807. video_node.loop = false;
  808. define_property_hook(video_node, 'loop', {
  809. get: function () {
  810. return false;
  811. }
  812. });
  813. video_node?.addEventListener('ended', function () {
  814. if (user_data.shorts_auto_scroll === 'on') {
  815. if (page_type === 'yt_shorts') {
  816. $('#navigation-button-down > ytd-button-renderer > yt-button-shape > button').click();
  817. } else {
  818. simulate_swipeup(this, 500, 100);
  819. }
  820. return;
  821. }
  822. if (user_data.shorts_disable_loop_play === 'on') {
  823. return;
  824. }
  825. this.play();
  826. });
  827. }
  828. function set_shorts_download(like_button) {
  829. const dislike_button = $('ytd-reel-video-renderer[is-active] #dislike-button > yt-button-shape > label > button');
  830. set_download_event(dislike_button, like_button);
  831. }
  832. function set_music_download(dislike_node) {
  833. const like_node = dislike_node.parentElement.parentElement.querySelector('#button-shape-like > button');
  834. set_download_event(dislike_node, like_node);
  835. }
  836. function set_watch_download(wrapper_node) {
  837. const like_node = wrapper_node.querySelector('like-button-view-model button');
  838. const dislike_node = wrapper_node.querySelector('dislike-button-view-model button');
  839. set_download_event(dislike_node, like_node);
  840. }
  841. function set_mobile_shorts_download(wrapper_node) {
  842. const nodes = wrapper_node.querySelectorAll('button');
  843. if (nodes.length !== 2) {
  844. log('set_mobile_shorts_download 节点数量不正确', nodes.length, -1);
  845. return;
  846. }
  847. const like_node = nodes[0];
  848. const dislike_node = nodes[1];
  849. dislike_node.click_intercept_propagation = true;
  850. dislike_node.dbclick_intercept_propagation = true;
  851. like_node.click_intercept_propagation = true;
  852. like_node.dbclick_intercept_propagation = true;
  853. set_download_event(dislike_node, like_node);
  854. }
  855. function video_download(url = href, isAudioOnly = false, targetNode = null) {
  856. if (page_type === 'yt_music_watch') {
  857. url = 'https://www.youtube.com/watch?' + href.split('?')[1];
  858. }
  859. // log('下载地址', url, isAudioOnly, 0);
  860. if (analyzing_download_url) return false;
  861. analyzing_download_url = true;
  862. const xhr = new XMLHttpRequest();
  863. xhr.open('POST', 'https://api.cobalt.tools/api/json', true);
  864. xhr.setRequestHeader('Cache-Control', 'no-cache');
  865. xhr.setRequestHeader('Accept', 'application/json');
  866. xhr.setRequestHeader('Content-Type', 'application/json');
  867.  
  868. const data = JSON.stringify({
  869. url: encodeURI(url),
  870. vQuality: 'max',
  871. filenamePattern: 'basic',
  872. isAudioOnly: isAudioOnly,
  873. disableMetadata: true,
  874. });
  875. xhr.onload = function () {
  876. if (xhr.status >= 200 && xhr.status < 300) {
  877. const response = JSON.parse(xhr.responseText);
  878. const download_url = response?.url;
  879. if (download_url) {
  880. window.open(download_url, '_blank');
  881. analyzing_download_url = false;
  882. targetNode.resolve_background_color();
  883. return;
  884. }
  885. }
  886. let tips;
  887. try {
  888. const json_data = JSON.parse(xhr.responseText);
  889. tips = json_data?.text;
  890. } catch (error) {
  891. tips = xhr.responseText;
  892. }
  893. alert(`download failed\nstatus: ${xhr.status}\nresponseText: ${tips}\n`);
  894. analyzing_download_url = false;
  895. targetNode.resolve_background_color();
  896. };
  897.  
  898. xhr.onerror = function (error) {
  899. alert(`download failed\nstatus: ${xhr.status}\nerror: ${error}\n`);
  900. analyzing_download_url = false;
  901. targetNode.resolve_background_color();
  902. };
  903. xhr.send(data);
  904. return true;
  905. }
  906. }
  907.  
  908. function get_user_data_listener() {
  909. return {
  910. cur_channel_id: null,
  911. listener_id: null,
  912. set: function () {
  913. if (channel_id === this.cur_channel_id) {
  914. return;
  915. };
  916. !this.cur_channel_id && GM_removeValueChangeListener(this.listener_id);
  917. this.cur_channel_id = channel_id;
  918. this.listener_id = GM_addValueChangeListener(channel_id, (name, oldValue, newValue, remote) => {
  919. if (!remote || this.cur_channel_id !== name) return;
  920. newValue.language = user_data.language;
  921. user_data = newValue;
  922. config_api.config_init();
  923. const popup_node = unsafeWindow.document.getElementById('xxx_popup');
  924. popup_node && display_config_win();
  925. });
  926. }
  927. };
  928. }
  929.  
  930. async function account_data_init(login) {
  931. if (is_account_init) return;
  932. is_account_init = true;
  933. if (login) {
  934. yt_api.get_channel_id();
  935. yt_api.get_subscribe_data();
  936. } else if (channel_id !== 'default') {
  937. channel_id = 'default';
  938. user_data.login = false;
  939. user_data = user_data_api.get();
  940. }
  941. }
  942.  
  943. function native_method_hook(method_path, handler) {
  944. try {
  945. let [last_path, last_key] = data_process.get_lastPath_and_key(method_path);
  946. let last_obj = data_process.string_to_value(unsafeWindow, 'unsafeWindow.' + last_path);
  947. let dec_obj = last_obj[last_key];
  948. last_obj[last_key + '__'] = dec_obj;
  949. if (typeof dec_obj !== 'function') {
  950. log(method_path, 'have been modified', -1);
  951. return;
  952. }
  953. const method_name = dec_obj.name;
  954. if (dec_obj.toString() !== 'function ' + method_name + '() { [native code] }') {
  955. log(method_path, 'have been modified!', -1);
  956. }
  957. last_obj[last_key] = handler;
  958. } catch (error) {
  959. log(method_path, 'hook failed!', error, -1);
  960. }
  961. }
  962.  
  963. function define_property_hook(obj, property, descriptor) {
  964. const old_descriptor = Object.getOwnPropertyDescriptor(obj, property);
  965. if (old_descriptor?.configurable === false) {
  966. debugger;
  967. log(property, 'is not configurable, hook error !', old_descriptor, -1);
  968. return;
  969. }
  970. try {
  971. Object.defineProperty(obj, property, descriptor);
  972. } catch (error) {
  973. log(property, 'hook failed!', error, -1);
  974. }
  975. }
  976.  
  977. function get_config_api() {
  978. return {
  979. flag_infos: {
  980. "zh-CN": {
  981. "sponsored": "赞助商广告",
  982. "free_movie": "免费(含广告)",
  983. "live": "直播",
  984. "movie_channel": "影视",
  985. "free_primetime_movie": "免费 Primetime 电影",
  986. "Playables": "游戏大本营",
  987. "short_buy_super_thanks": "购买超级感谢",
  988. "think_video": "你对这个视频有何看法?|此推荐内容怎么样?",
  989. "try": "试用",
  990. "recommend_popular": "时下流行",
  991. "featured": "Featured",
  992. "category_live": "直播",
  993. "category_game": "游戏",
  994. "category_news": "新闻",
  995. "btn_recommend_movie": "电影推荐",
  996. "btn_recommend_shorts": "Shorts推荐",
  997. "btn_recommend_liveroom": "直播推荐",
  998. "btn_recommend_popular": "时下流行",
  999. "btn_recommend_game": "游戏大本营推荐",
  1000. "btn_save": "保存",
  1001. "goodselect": "精选",
  1002. "music_ad_flag": "无广告打扰",
  1003. "upcoming": "即将开始",
  1004. "init": "初始化",
  1005. "ctoc": "已复制到剪贴板",
  1006. "runing_normally": "运行正常",
  1007. "err_msg": "错误信息",
  1008. "success": "成功",
  1009. "failed": "失败",
  1010. "tips": "你可以发送错误信息或者截图发给脚本开发者",
  1011. "exists_error": "存在错误信息(建议多次刷新观察是否是同样的错误信息)",
  1012. "inject": "注入",
  1013. "btn_lable_open": "开启",
  1014. "btn_lable_close": "关闭",
  1015. "btn_lable_subscribed": "仅订阅",
  1016. "recommend_subscribed_lable_tips": "只显示已订阅的推荐",
  1017. "title_add_shorts_upload_date": "Shorts添加更新时间",
  1018. "title_shorts_change_author_name": "Shorts用户名改频道名",
  1019. "config_info": "配置信息",
  1020. "page_info": "页面信息",
  1021. "rule_info": "规则信息",
  1022. "del_config_confirm_tips": "你确定要删除所有配置信息?",
  1023. "btn_dbclick_download_video_tips": "双击点赞下载视频,双击不喜欢下载音频",
  1024. "btn_dbclick_download_video_title": "双击下载视频",
  1025. "download_video_confirm_tips": "要下载这个视频?",
  1026. "download_audio_confirm_tips": "要下载这个音频?",
  1027. "btn_shorts_auto_scroll_title": "自动滚动",
  1028. "bt_shorts_disable_loop_play_title": "禁止循环播放",
  1029. "btn_shorts_dbclick_like_title": "双击视频点赞",
  1030. "btn_shorts_add_video_progress_title": "添加视频进度",
  1031. "shorts_recommend_split_tag": "Shorts功能配置",
  1032. },
  1033. "zh-TW": {
  1034. "sponsored": "贊助商廣告",
  1035. "free_movie": "免費 \\(含廣告\\)",
  1036. "live": "直播",
  1037. "movie_channel": "電影與電視節目",
  1038. "Playables": "遊戲角落",
  1039. "free_primetime_movie": "免費的特選電影",
  1040. "short_buy_super_thanks": "購買超級感謝",
  1041. "think_video": "你對這部影片有什麼看法?|此推荐内容怎么样?",
  1042. "try": "試用",
  1043. "recommend_popular": "發燒影片",
  1044. "featured": "Featured",
  1045. "category_live": "直播中",
  1046. "category_game": "遊戲",
  1047. "category_news": "新聞",
  1048. "btn_recommend_movie": "电影推薦",
  1049. "btn_recommend_shorts": "Shorts推薦",
  1050. "btn_recommend_liveroom": "直播推薦",
  1051. "btn_recommend_popular": "發燒影片",
  1052. "btn_recommend_game": "遊戲角落推薦",
  1053. "btn_save": "保存",
  1054. "goodselect": "精選內容",
  1055. "music_ad_flag": "零廣告",
  1056. "upcoming": "即将直播",
  1057. "init": "初始化",
  1058. "ctoc": "已複製到剪貼板",
  1059. "runing_normally": "運行正常",
  1060. "err_msg": "錯誤訊息",
  1061. "success": "成功",
  1062. "failed": "失敗",
  1063. "tips": "你可以发送錯誤訊息或截圖給腳本開發者",
  1064. "exists_error": "存在錯誤訊息(建議多次刷新觀察是否是同樣的錯誤訊息)",
  1065. "inject": "注入",
  1066. "btn_lable_open": "開啓",
  1067. "btn_lable_close": "關閉",
  1068. "btn_lable_subscribed": "僅訂閱",
  1069. "recommend_subscribed_lable_tips": "只顯示已訂閱的推薦",
  1070. "title_add_shorts_upload_date": "Shorts添加更新時間",
  1071. "title_shorts_change_author_name": "Shorts用戶名稱改頻道名",
  1072. "config_info": "設定資訊",
  1073. "page_info": "頁面資訊",
  1074. "rule_info": "規則資訊",
  1075. "del_config_confirm_tips": "你確定要刪除所有設定資訊?",
  1076. "btn_dbclick_download_video_tips": "雙擊我喜歡下載影片,雙擊我不喜歡下載音檔",
  1077. "btn_dbclick_download_video_title": "雙擊下載視頻",
  1078. "download_video_confirm_tips": "要下載這個影片?",
  1079. "download_audio_confirm_tips": "要下載這個音檔?",
  1080. "btn_shorts_auto_scroll_title": "自動捲動",
  1081. "bt_shorts_disable_loop_play_title": "禁止循環播放",
  1082. "btn_shorts_dbclick_like_title": "雙擊影片按讚",
  1083. "btn_shorts_add_video_progress_title": "添加影片進度",
  1084. "shorts_recommend_split_tag": "Shorts功能配置",
  1085. },
  1086. "zh-HK": {
  1087. "sponsored": "赞助",
  1088. "free_movie": "免費 \\(有廣告\\)",
  1089. "live": "直播",
  1090. "movie_channel": "電影與電視節目",
  1091. "Playables": "Playables",
  1092. "short_buy_super_thanks": "購買 Super Thanks",
  1093. "free_primetime_movie": "黃金時段電影",
  1094. "think_video": "你對此影片有何意見?|此推荐内容怎么样?",
  1095. "try": "試用",
  1096. "recommend_popular": "熱爆影片",
  1097. "featured": "Featured",
  1098. "category_live": "直播",
  1099. "category_game": "遊戲",
  1100. "category_news": "新聞",
  1101. "btn_recommend_movie": "电影推薦",
  1102. "btn_recommend_shorts": "Shorts推薦",
  1103. "btn_recommend_liveroom": "直播推薦",
  1104. "btn_recommend_popular": "熱爆影片",
  1105. "btn_recommend_game": "Playables推荐",
  1106. "btn_save": "保存",
  1107. "goodselect": "精選",
  1108. "music_ad_flag": "零廣告音樂",
  1109. "upcoming": "即將發佈",
  1110. "init": "初始化",
  1111. "ctoc": "已複製到剪貼板",
  1112. "runing_normally": "運行正常",
  1113. "err_msg": "錯誤訊息",
  1114. "success": "成功",
  1115. "failed": "失敗",
  1116. "tips": "你可以发送錯誤訊息或截圖給腳本開發者",
  1117. "exists_error": "存在錯誤訊息(建議多次刷新觀察是否是同樣的錯誤訊息)",
  1118. "inject": "注入",
  1119. "btn_lable_open": "開啓",
  1120. "btn_lable_close": "關閉",
  1121. "btn_lable_subscribed": "僅訂閱",
  1122. "recommend_subscribed_lable_tips": "只顯示已訂閱的推薦",
  1123. "title_add_shorts_upload_date": "Shorts添加更新時間",
  1124. "title_shorts_change_author_name": "Shorts用戶名稱改頻道名",
  1125. "config_info": "設定資訊",
  1126. "page_info": "頁面資訊",
  1127. "rule_info": "規則資訊",
  1128. "del_config_confirm_tips": "你確定要刪除所有配置信息嗎?",
  1129. "btn_dbclick_download_video_tips": "雙擊我喜歡下載影片,雙擊我不喜歡下載音檔",
  1130. "btn_dbclick_download_video_title": "雙擊下載視頻",
  1131. "download_video_confirm_tips": "要下載這個影片?",
  1132. "download_audio_confirm_tips": "要下載這個音檔?",
  1133. "btn_shorts_auto_scroll_title": "自動捲動",
  1134. "bt_shorts_disable_loop_play_title": "禁止循環播放",
  1135. "btn_shorts_dbclick_like_title": "雙擊影片按讚",
  1136. "btn_shorts_add_video_progress_title": "添加影片進度",
  1137. "shorts_recommend_split_tag": "Shorts功能配置",
  1138. },
  1139. "en": {
  1140. "sponsored": "Sponsored",
  1141. "free_movie": "Free with ads",
  1142. "live": "LIVE",
  1143. "movie_channel": "Movies & TV",
  1144. "Playables": "Playables",
  1145. "short_buy_super_thanks": "Buy Super Thanks",
  1146. "free_primetime_movie": "Free Primetime movies",
  1147. "think_video": "What did you think of this video?|此推荐内容怎么样?",
  1148. "try": "Try",
  1149. "recommend_popular": "Trending",
  1150. "featured": "Featured",
  1151. "category_live": "Live",
  1152. "category_game": "Gaming",
  1153. "category_news": "News",
  1154. "btn_recommend_movie": "MovieRecommend",
  1155. "btn_recommend_shorts": "ShortsRecommend",
  1156. "btn_recommend_liveroom": "LiveRecommend",
  1157. "btn_recommend_popular": "TrendingRecommend",
  1158. "btn_recommend_game": "PlayablesRecommend",
  1159. "btn_save": "Save",
  1160. "goodselect": "Featured",
  1161. "music_ad_flag": "ad-free",
  1162. "upcoming": "UPCOMING",
  1163. "init": "init",
  1164. "ctoc": "Copied to clipboard",
  1165. "runing_normally": "running normally",
  1166. "err_msg": "error message",
  1167. "success": "success",
  1168. "failed": "failed",
  1169. "tips": "You can send error message or screenshot to the developer",
  1170. "exists_error": "Error message exists (It is recommended to refresh multiple times to see if it is the same error message)",
  1171. "inject": "inject",
  1172. "btn_lable_open": "on",
  1173. "btn_lable_close": "off",
  1174. "btn_lable_subscribed": "onlySubscribed",
  1175. "recommend_subscribed_lable_tips": "only show subscribed recommend",
  1176. "title_add_shorts_upload_date": "ShortsAddUploadTime",
  1177. "title_shorts_change_author_name": "ShortsChangeToChannelName",
  1178. "config_info": "config info",
  1179. "page_info": "page info",
  1180. "rule_info": "rule info",
  1181. "del_config_confirm_tips": "Are you sure you want to delete all configuration settings?",
  1182. "btn_dbclick_download_video_tips": "Double click like button to download the video, double click dislike button to download the audio",
  1183. "btn_dbclick_download_video_title": "DoubleClickDownloadVideo",
  1184. "download_video_confirm_tips": "Do you want to download this video?",
  1185. "download_audio_confirm_tips": "Do you want to download this audio?",
  1186. "btn_shorts_auto_scroll_title": "AutoScroll",
  1187. "bt_shorts_disable_loop_play_title": "DisableLoopPlay",
  1188. "btn_shorts_dbclick_like_title": "DoubleClickLikeVideo",
  1189. "btn_shorts_add_video_progress_title": "AddVideoProgress",
  1190. "shorts_recommend_split_tag": "ShortsConfig",
  1191. }
  1192. },
  1193. common_ytInitialPlayerResponse_rule: [
  1194. "abs:playerAds=- $exist",
  1195. "abs:adSlots=- $exist",
  1196. "abs:adPlacements=- $exist",
  1197. "abs:auxiliaryUi.messageRenderers.bkaEnforcementMessageViewModel.isVisible=json(\"true\") $exist",
  1198. "abs:adBreakHeartbeatParams=- $exist",
  1199. "abs:messages[*]=- /.mealbarPromoRenderer$exist",
  1200. ],
  1201. default_language: 'en',
  1202. config_init: function (tmp_language = null) {
  1203. if (!tmp_language) {
  1204. tmp_language = unsafeWindow['ytcfg'].msgs ? unsafeWindow['ytcfg'].msgs.__lang__ : (unsafeWindow['ytcfg'].data ? unsafeWindow['ytcfg'].data.HL : undefined);
  1205. !tmp_language && (tmp_language = unsafeWindow['yt'] && unsafeWindow['yt'].config_ && unsafeWindow['yt'].config_.HL);
  1206. if (!tmp_language) {
  1207. log('语言获取错误', unsafeWindow, -1);
  1208. }
  1209. }
  1210. if (!['en', 'zh-CN', 'zh-TW', 'zh-HK'].includes(tmp_language)) {
  1211. real_language = tmp_language;
  1212. tmp_language = this.default_language;
  1213. }
  1214. if (tmp_language !== user_data.language) {
  1215. user_data.language = tmp_language;
  1216. user_data_api.set();
  1217. }
  1218. flag_info = this.flag_infos[user_data.language];
  1219. movie_channel_info = {
  1220. "guideEntryRenderer": {
  1221. "navigationEndpoint": {
  1222. "clickTrackingParams": "CBQQnOQDGAIiEwj5l8SLqPiCAxUXSEwIHbf1Dw0=",
  1223. "commandMetadata": {
  1224. "webCommandMetadata": {
  1225. "url": "/feed/storefront",
  1226. "webPageType": "WEB_PAGE_TYPE_BROWSE",
  1227. "rootVe": 6827,
  1228. "apiUrl": "/youtubei/v1/browse"
  1229. }
  1230. },
  1231. "browseEndpoint": {
  1232. "browseId": "FEstorefront"
  1233. }
  1234. },
  1235. "icon": {
  1236. "iconType": "CLAPPERBOARD"
  1237. },
  1238. "trackingParams": "CBQQnOQDGAIiEwj5l8SLqPiCAxUXSEwIHbf1Dw0=",
  1239. "formattedTitle": {
  1240. "simpleText": flag_info.movie_channel
  1241. },
  1242. "accessibility": {
  1243. "accessibilityData": {
  1244. "label": flag_info.movie_channel
  1245. }
  1246. }
  1247. }
  1248. };
  1249. data_process.storage_obj('movie_channel_info', movie_channel_info);
  1250. mobile_movie_channel_info = {
  1251. "navigationItemViewModel": {
  1252. "text": {
  1253. "content": flag_info.movie_channel
  1254. },
  1255. "icon": {
  1256. "sources": [
  1257. {
  1258. "clientResource": {
  1259. "imageName": "CLAPPERBOARD"
  1260. }
  1261. }
  1262. ]
  1263. },
  1264. "onTap": {
  1265. "parallelCommand": {
  1266. "commands": [
  1267. {
  1268. "innertubeCommand": {
  1269. "clickTrackingParams": "CBQQnOQDGAIiEwj5l8SLqPiCAxUXSEwIHbf1Dw0=",
  1270. "hideMoreDrawerCommand": {}
  1271. }
  1272. },
  1273. {
  1274. "innertubeCommand": {
  1275. "clickTrackingParams": "CBQQnOQDGAIiEwj5l8SLqPiCAxUXSEwIHbf1Dw0=",
  1276. "commandMetadata": {
  1277. "webCommandMetadata": {
  1278. "url": "/feed/storefront",
  1279. "webPageType": "WEB_PAGE_TYPE_CHANNEL",
  1280. "rootVe": 3611,
  1281. "apiUrl": "/youtubei/v1/browse"
  1282. }
  1283. },
  1284. "browseEndpoint": {
  1285. "browseId": "FEstorefront"
  1286. }
  1287. }
  1288. }
  1289. ]
  1290. }
  1291. },
  1292. "loggingDirectives": {
  1293. "trackingParams": "CBQQnOQDGAIiEwj5l8SLqPiCAxUXSEwIHbf1Dw0=",
  1294. "visibility": {
  1295. "types": "12"
  1296. },
  1297. "enableDisplayloggerExperiment": true
  1298. }
  1299. }
  1300. };
  1301. data_process.storage_obj('mobile_movie_channel_info', mobile_movie_channel_info);
  1302. ytInitialData_rule = null;
  1303. ytInitialReelWatchSequenceResponse_rule = null;
  1304. ytInitialPlayerResponse_rule = null;
  1305. mobile_web = page_type.startsWith('mobile');
  1306. },
  1307. get_rules: function (page_type_, type) {
  1308. page_type_ = page_type_ || page_type;
  1309. if (page_type_ === 'mobile_yt_watch_searching')
  1310. page_type_ = 'mobile_yt_watch';
  1311. else if (page_type_ === 'mobile_yt_home_searching')
  1312. page_type_ = 'mobile_yt_home';
  1313. else if (page_type_ === 'yt_music_channel')
  1314. page_type_ = 'yt_watch';
  1315.  
  1316. let tmp_ytInitialData_rule = null;
  1317. let tmp_ytInitialReelWatchSequenceResponse_rule = null;
  1318. let tmp_ytInitialPlayerResponse_rule = null;
  1319. const common_ytInitialData_rule = [
  1320. 'adSlotRenderer.=-',
  1321. ];
  1322. const return_obj = {
  1323. ytInitialData_rule: null,
  1324. ytInitialReelWatchSequenceResponse_rule: null,
  1325. ytInitialPlayerResponse_rule: null,
  1326. reverse: false,
  1327. };
  1328. if (page_type_ === 'yt_search') {
  1329. tmp_ytInitialData_rule = common_ytInitialData_rule;
  1330. return_obj.ytInitialData_rule = tmp_ytInitialData_rule;
  1331. return return_obj;
  1332. }
  1333.  
  1334. if (page_type_ === 'yt_music') {
  1335. return_obj.ytInitialData_rule = ["abs:overlay.mealbarPromoRenderer=- $exist"];
  1336. return return_obj;
  1337. }
  1338.  
  1339. if (page_type_ === 'mobile_yt_search') {
  1340. tmp_ytInitialData_rule = common_ytInitialData_rule;
  1341. return_obj.ytInitialData_rule = tmp_ytInitialData_rule;
  1342. return return_obj;
  1343. }
  1344.  
  1345. if (page_type_ === 'yt_kids_watch') {
  1346. tmp_ytInitialData_rule = common_ytInitialData_rule;
  1347. return_obj.ytInitialData_rule = tmp_ytInitialData_rule;
  1348. return return_obj;
  1349. }
  1350.  
  1351. if (page_type_ === 'yt_music_watch') {
  1352. tmp_ytInitialData_rule = common_ytInitialData_rule;
  1353. return_obj.ytInitialData_rule = tmp_ytInitialData_rule;
  1354. return return_obj;
  1355. }
  1356.  
  1357. if (page_type_.includes('yt_shorts')) {
  1358. const tmp_ytInitialData_rule__ = [];
  1359. // 添加时间 // 改频道名
  1360. if (user_data.add_shorts_upload_date === 'on' || user_data.shorts_change_author_name === 'on') {
  1361. let dec_path = 'overlay.reelPlayerOverlayRenderer.reelPlayerHeaderSupportedRenderers.reelPlayerHeaderRenderer.channelTitleText.runs[0].text';
  1362. let name_base_path = 'json_obj.engagementPanels[1].engagementPanelSectionListRenderer.content.structuredDescriptionContentRenderer.items[0].videoDescriptionHeaderRenderer.channel.';
  1363. let time_tag_path;
  1364. let name_tag_path;
  1365. if (mobile_web) {
  1366. user_data.add_shorts_upload_date === 'on' && (time_tag_path = '....timestampText.runs[0].text');
  1367. user_data.shorts_change_author_name === 'on' && (name_tag_path = name_base_path + 'runs[0].text');
  1368. } else {
  1369. user_data.add_shorts_upload_date === 'on' && (time_tag_path = '....timestampText.simpleText');
  1370. user_data.shorts_change_author_name === 'on' && (name_tag_path = name_base_path + 'simpleText');
  1371. }
  1372. let rule = `abs:${dec_path}={absObj(${name_tag_path ? name_tag_path : ('json_obj.' + dec_path)})\}${time_tag_path ? ('\n{pathObj(' + time_tag_path + ')\}') : ''}`;
  1373. tmp_ytInitialData_rule__.push(rule);
  1374. }
  1375.  
  1376. // 超级感谢
  1377. if (user_data.short_buy_super_thanks === 'off') {
  1378. // !mobile_web && tmp_ytInitialData_rule__.push('abs:overlay.reelPlayerOverlayRenderer.suggestedAction=- /.shortsSuggestedActionRenderer.text.runs[0].text=' + flag_info.short_buy_super_thanks);
  1379. !mobile_web && tmp_ytInitialData_rule__.push('abs:overlay.reelPlayerOverlayRenderer.suggestedAction=- $exist');
  1380. }
  1381. tmp_ytInitialReelWatchSequenceResponse_rule = ['abs:entries[*]=- /.command.reelWatchEndpoint.adClientParams$exist'];
  1382. tmp_ytInitialData_rule__.length && (tmp_ytInitialData_rule = tmp_ytInitialData_rule__);
  1383. return_obj.ytInitialReelWatchSequenceResponse_rule = tmp_ytInitialReelWatchSequenceResponse_rule;
  1384. return_obj.ytInitialData_rule = tmp_ytInitialData_rule;
  1385. return return_obj;
  1386. }
  1387.  
  1388. if (page_type_.includes('yt_watch')) {
  1389. return function (json_obj) {
  1390. /*
  1391. item_path
  1392. next 类型
  1393. 移动端和桌面端
  1394. abs:onResponseReceivedActions[0].appendContinuationItemsAction.continuationItems[*]
  1395. init 类型
  1396. 移动端
  1397. abs:contents.singleColumnWatchNextResults.results.results.contents[*]
  1398. 桌面端
  1399. abs:contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results[1].itemSectionRenderer.contents[*]
  1400. videoRenderer_path
  1401. 桌面端
  1402. /.compactVideoRenderer
  1403. 移动端
  1404. /.itemSectionRenderer.contents[0].videoWithContexteRnderer
  1405. section_path
  1406. 桌面端
  1407. /.reelShelfRenderer
  1408. 移动端
  1409. /.itemSectionRenderer.contents[0].reelShelfRenderer
  1410. */
  1411.  
  1412. if (json_obj.continuation) return [];
  1413. let video_item_base_path;
  1414. let video_sub_path;
  1415. let section_sub_path;
  1416. let player_bottom_path;
  1417. let player_bottom_section_path;
  1418. type = type || 'init';
  1419. if (type === 'next') {
  1420. if (json_obj.onResponseReceivedEndpoints?.[0]?.appendContinuationItemsAction?.continuationItems?.length) {
  1421. let target_id = json_obj.onResponseReceivedEndpoints[0].appendContinuationItemsAction.targetId;
  1422. if (target_id.startsWith('comment-replies')) return [];
  1423. // 下拉item刷新
  1424. video_item_base_path = "abs:onResponseReceivedEndpoints[0].appendContinuationItemsAction.continuationItems[*]";
  1425. video_sub_path = '/.videoWithContextRenderer';
  1426. section_sub_path = '/.reelShelfRenderer';
  1427. }
  1428.  
  1429. } else if (type === 'init') {
  1430. if (mobile_web) {
  1431. if (json_obj.contents?.singleColumnWatchNextResults?.results?.results?.contents?.length) {
  1432. let length = json_obj.contents.singleColumnWatchNextResults.results.results.contents.length;
  1433. video_item_base_path = `abs:contents.singleColumnWatchNextResults.results.results.contents[${length - 1}].itemSectionRenderer.contents[*]`;
  1434. length > 1 && (player_bottom_path = `abs:contents.singleColumnWatchNextResults.results.results.contents[0-${length - 2}]`);
  1435. cur_watch_channle_id = json_obj.contents.singleColumnWatchNextResults.results.results.contents?.[1]?.slimVideoMetadataSectionRenderer?.contents?.[1]?.slimOwnerRenderer?.title.runs[0].navigationEndpoint.browseEndpoint.browseId;
  1436. player_bottom_section_path = '/.itemSectionRenderer.contents[0].reelShelfRenderer';
  1437. video_sub_path = '/.videoWithContextRenderer';
  1438. section_sub_path = '/.reelShelfRenderer';
  1439. }
  1440. }
  1441. else {
  1442. let is_next_target_id;
  1443. if (json_obj.contents?.twoColumnWatchNextResults?.secondaryResults?.secondaryResults?.results?.[1]?.itemSectionRenderer?.contents?.length) {
  1444. video_item_base_path = "abs:contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results[1].itemSectionRenderer.contents[*]";
  1445. player_bottom_path = 'abs:contents.twoColumnWatchNextResults.results.results.contents[*]';
  1446. is_next_target_id = json_obj.contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results[1].itemSectionRenderer.targetId === 'watch-next-feed';
  1447. cur_watch_channle_id = json_obj.contents.twoColumnWatchNextResults.results.results.contents?.[1]?.videoSecondaryInfoRenderer?.owner?.videoOwnerRenderer?.title.runs[0].navigationEndpoint.browseEndpoint.browseId;
  1448. player_bottom_section_path = '/.itemSectionRenderer.contents[0]';
  1449. video_sub_path = '/.compactVideoRenderer';
  1450. section_sub_path = '/.reelShelfRenderer';
  1451. }
  1452. if (!is_next_target_id && json_obj.contents?.twoColumnWatchNextResults?.secondaryResults?.secondaryResults?.results?.[0]?.richGridRenderer?.contents?.length) {
  1453. video_item_base_path = "abs:contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results[0].richGridRenderer.contents[*]";
  1454. player_bottom_path = 'abs:contents.twoColumnWatchNextResults.results.results.contents[*]';
  1455. is_next_target_id = json_obj.contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results[0].richGridRenderer.targetId === 'watch-next-feed';
  1456. cur_watch_channle_id = json_obj.contents.twoColumnWatchNextResults.results.results.contents?.[1]?.videoSecondaryInfoRenderer?.owner?.videoOwnerRenderer?.title.runs[0].navigationEndpoint.browseEndpoint.browseId;
  1457. player_bottom_section_path = '/.itemSectionRenderer.contents[0]';
  1458. video_sub_path = '/.richItemRenderer.content.videoRenderer';
  1459. section_sub_path = '/.richSectionRenderer.content.richShelfRenderer';
  1460. }
  1461. if (!is_next_target_id && json_obj.contents?.twoColumnWatchNextResults?.secondaryResults?.secondaryResults?.results?.length) {
  1462. video_item_base_path = 'abs:contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results[*]';
  1463. player_bottom_path = 'abs:contents.twoColumnWatchNextResults.results.results.contents[*]';
  1464. cur_watch_channle_id = json_obj.contents.twoColumnWatchNextResults.results.results.contents?.[1]?.videoSecondaryInfoRenderer?.owner?.videoOwnerRenderer?.title.runs[0].navigationEndpoint.browseEndpoint.browseId;
  1465. player_bottom_section_path = '/.itemSectionRenderer.contents[0]';
  1466. video_sub_path = '/.compactVideoRenderer';
  1467. section_sub_path = '/.reelShelfRenderer';
  1468. }
  1469. }
  1470. }
  1471. if (!video_item_base_path) return [];
  1472.  
  1473. const rules = [];
  1474. let video_item_rules = [];
  1475. let section_item_rules = [];
  1476. let player_bottom_section_rules = [];
  1477. let player_bottom_rules = [];
  1478. //赞助商广告
  1479. mobile_web && type === 'init' && player_bottom_rules.push(`${player_bottom_section_path.replace(/\.[^\.]+$/, '')}.adSlotRenderer$exist`);
  1480. video_item_rules.push(`${video_sub_path.replace(/\.[^\.]+$/, '.adSlotRenderer$exist')}`);
  1481.  
  1482. // youtuber商品广告 https://www.youtube.com/watch?v=dFjXK8xpurY&list=RDCLAK5uy_mijutvVbzp7bbNlWt-B5U90qb5KplCkSQ&index=3
  1483. !mobile_web && type === 'init' && player_bottom_rules.push(`/.merchandiseShelfRenderer$exist`);
  1484.  
  1485. //免费电影
  1486. if (user_data.open_recommend_movie === 'off' && cur_watch_channle_id !== 'UClgRkhTL3_hImCAmdLfDE4g') {
  1487. if (mobile_web) {
  1488. video_item_rules.push(`${video_sub_path}.badges[0].metadataBadgeRenderer.style=BADGE_STYLE_TYPE_YPC`);
  1489. } else {
  1490. video_item_rules.push(`${video_sub_path.replace(/\.[^\.]+$/, '.compactMovieRenderer')}$exist`);
  1491. }
  1492. }
  1493.  
  1494. //直播规则
  1495. if (['off', 'subscribed'].includes(user_data.open_recommend_liveroom)) {
  1496. if (mobile_web)
  1497. video_item_rules.push(`${video_sub_path}.thumbnailOverlays[0].thumbnailOverlayTimeStatusRenderer.style=LIVE|UPCOMING`);
  1498. else
  1499. video_item_rules.push(`${video_sub_path}.badges[0].metadataBadgeRenderer.style=BADGE_STYLE_TYPE_LIVE_NOW`);
  1500. }
  1501.  
  1502. // 添加已订阅短视频
  1503. if (user_data.open_recommend_shorts === 'subscribed' && type === 'init' && page_type !== 'mobile_yt_watch') {
  1504. rules.push(`${video_item_base_path.replace('[*]', '')}=+(arr_insert,method(shorts_fun.get_shorts_section()),0) @user_data.shorts_list.length$value>0`);
  1505. }
  1506.  
  1507. // 大标题栏目 短视频等
  1508. if (['off', 'subscribed'].includes(user_data.open_recommend_shorts)) {
  1509. section_item_rules.push(`${section_sub_path}.icon.iconType=YOUTUBE_SHORTS_BRAND_24`);
  1510. mobile_web && type === 'init' && player_bottom_rules.push(`${player_bottom_section_path}.icon.iconType=YOUTUBE_SHORTS_BRAND_24`);
  1511. }
  1512.  
  1513. // 视频下方可能会出现的推荐栏目
  1514. player_bottom_rules.length && rules.push(`${player_bottom_path}=- ${player_bottom_rules.join(data_process.condition_split_or_tag)}`);
  1515. section_item_rules.length && video_item_rules.push(...section_item_rules);
  1516. video_item_rules.length && rules.push(`${video_item_base_path}=- ${video_item_rules.join(data_process.condition_split_or_tag)}`);
  1517. return rules;
  1518. };
  1519. }
  1520.  
  1521. if (page_type_.includes('yt_home')) {
  1522. /*
  1523. item_path
  1524. browse 类型
  1525. 移动端和桌面端
  1526. abs:onResponseReceivedActions[0].appendContinuationItemsAction.continuationItems[*]
  1527. init 类型
  1528. 移动端
  1529. abs:contents.singleColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.richGridRenderer.contents[*]
  1530. 桌面端
  1531. singleColumnBrowseResultsRenderer ---> twoColumnBrowseResultsRenderer
  1532. videoRenderer_path
  1533. 桌面端
  1534. /.richItemRenderer.content.videoRenderer
  1535. 移动端
  1536. videoRenderer --> videoWithContextRenderer
  1537. richSectionRenderer_path
  1538. 桌面端
  1539. /.richSectionRenderer.content.richShelfRenderer
  1540. 移动端
  1541. richShelfRenderer ---> reelShelfRenderer
  1542. */
  1543. let item_path;
  1544. let item_rules = [];
  1545. let rules = [];
  1546. type = type || 'init';
  1547. if (type === 'browse') {
  1548. item_path = "abs:onResponseReceivedActions[0].appendContinuationItemsAction.continuationItems[*]";
  1549. } else if (type === 'init') {
  1550. item_path = `abs:contents.${mobile_web ? 'singleColumnBrowseResultsRenderer' : 'twoColumnBrowseResultsRenderer'}.tabs[0].tabRenderer.content.richGridRenderer.contents[*]`;
  1551. } else {
  1552. return {};
  1553. }
  1554. const video_path = `/.richItemRenderer.content.${mobile_web ? 'videoWithContextRenderer' : 'videoRenderer'}`;
  1555. const section_path = `/.richSectionRenderer.content.${mobile_web ? 'reelShelfRenderer' : 'richShelfRenderer'}`;
  1556.  
  1557. //赞助商广告
  1558. item_rules.push('/.richItemRenderer.content.adSlotRenderer$exist');
  1559.  
  1560. //头部第一个广告
  1561. !mobile_web && type === 'init' && rules.push('abs:contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.richGridRenderer.masthead=- $exist');
  1562.  
  1563. // Shorts
  1564. if (['off', 'subscribed'].includes(user_data.open_recommend_shorts)) {
  1565. item_rules.push(`${section_path}.icon.iconType=YOUTUBE_SHORTS_BRAND_24`);
  1566. }
  1567.  
  1568. // 时下流行
  1569. if (user_data.open_recommend_popular === 'off') {
  1570. item_rules.push(`${section_path}.endpoint.browseEndpoint.browseId=FEtrending`);
  1571. }
  1572.  
  1573. // playables
  1574. if (user_data.open_recommend_playables === 'off') {
  1575. item_rules.push('/.richSectionRenderer.content.richShelfRenderer.endpoint.browseEndpoint.browseId=FEmini_app_destination');
  1576. }
  1577.  
  1578. // 添加已订阅短视频
  1579. if (user_data.open_recommend_shorts === 'subscribed' && type === 'init') {
  1580. rules.push(item_path.replace('[*]', '') + '=+(arr_insert,method(shorts_fun.get_shorts_section()),0) @user_data.shorts_list.length$value>0');
  1581. }
  1582.  
  1583. //直播
  1584. if (['off', 'subscribed'].includes(user_data.open_recommend_liveroom)) {
  1585. !mobile_web && item_rules.push(`${video_path}.badges[0].metadataBadgeRenderer.style=BADGE_STYLE_TYPE_LIVE_NOW`);
  1586. const tag_express = `UPCOMING${mobile_web ? data_process.value_split_or_tag + "LIVE" : ''}`;
  1587. item_rules.push(`${video_path}.thumbnailOverlays[-1].thumbnailOverlayTimeStatusRenderer.style=${tag_express}`);
  1588. }
  1589.  
  1590. //免费电影
  1591. if (user_data.open_recommend_movie === 'off') {
  1592. item_rules.push(`${section_path}.endpoint.browseEndpoint.browseId=FEstorefront|UClgRkhTL3_hImCAmdLfDE4g`);
  1593. item_rules.push(`${video_path}.badges[0].metadataBadgeRenderer.style=BADGE_STYLE_TYPE_YPC`);
  1594. }
  1595.  
  1596. //电视好物
  1597. item_rules.push('/.richSectionRenderer.content.statementBannerRenderer$exist');
  1598.  
  1599. // youtube调查弹窗
  1600. rules.push('abs:survey=- $exist');
  1601.  
  1602. //调查
  1603. item_rules.push(section_path.replace(/\.[^\.]+$/, '.inlineSurveyRenderer$exist'));
  1604.  
  1605. // primetime
  1606. item_rules.push(section_path.replace(/\.[^\.]+$/, '.primetimePromoRenderer$exist'));
  1607.  
  1608.  
  1609. //添加电影频道
  1610. const add_movie_channel_rule = "loadingStrategy.inlineContent.moreDrawerViewModel.content=+sobj(" + (mobile_web ? "mobile_" : "") + "movie_channel_info) !~=" + flag_info.movie_channel;
  1611. rules.push(add_movie_channel_rule);
  1612.  
  1613. rules.push(`${item_path}=- ${item_rules.join(data_process.condition_split_or_tag)}`);
  1614. return_obj.ytInitialData_rule = rules;
  1615. return return_obj;
  1616. }
  1617. return return_obj;
  1618.  
  1619. }
  1620. };
  1621. }
  1622.  
  1623. function set_search_listen() {
  1624. let count = 0;
  1625. const interval_id = setInterval(() => {
  1626. if (!['yt_watch', 'yt_home', 'mobile_yt_home_searching', 'mobile_yt_watch_searching', 'yt_shorts'].includes(page_type)) {
  1627. clearInterval(interval_id);
  1628. return;
  1629. }
  1630. count++;
  1631. const search_selector = href.includes('https://m.youtube.com/') ? 'input.searchbox-input.title' : 'input.yt-searchbox-input';
  1632. const search_input_node = $(search_selector);
  1633. if (search_input_node) {
  1634. clearInterval(interval_id);
  1635. if (search_input_node.set_listener) return;
  1636.  
  1637. search_input_node.set_listener = true;
  1638. const oninput = function (event) {
  1639. if ([display_error_keyword, open_config_keyword, reset_config_keyword].includes(this.value)) {
  1640. setTimeout(function () {
  1641. if (search_input_node.value === open_config_keyword) {
  1642. search_input_node.value = '';
  1643. display_config_win();
  1644. }
  1645. if (search_input_node.value === reset_config_keyword) {
  1646. user_data_api.reset();
  1647. return;
  1648. }
  1649. if (search_input_node.value === display_error_keyword) {
  1650. search_input_node.value = '';
  1651. let tips = `script ${flag_info.init} ${isinint ? flag_info.success : flag_info.failed}`;
  1652. if (error_messages.length === 0 && isinint) tips += ' ' + flag_info.runing_normally;
  1653. for (let key of Object.keys(inject_info)) {
  1654. if (!mobile_web && key === 'ytInitialPlayerResponse') continue;
  1655. if (key === 'ytInitialReelWatchSequenceResponse' && !['yt_shorts', 'mobile_yt_shorts'].includes(page_type)) continue;
  1656. tips += `\n${key} ${flag_info.inject} ${inject_info[key] ? flag_info.success : flag_info.failed}`;
  1657. }
  1658. // 配置信息
  1659. const tmp_user_data = JSON.parse(JSON.stringify(user_data));
  1660. delete tmp_user_data.shorts_list;
  1661. delete tmp_user_data.channel_infos;
  1662. tips += `\n\n${flag_info.config_info}\n${JSON.stringify(tmp_user_data, null, 2)}\n\n${flag_info.page_info}\npage_type: ${page_type}\nhref: ${href}`;
  1663. tips += `\n\nbrowser_info\n${JSON.stringify(browser_info, null, 2)}`;
  1664. // 规则信息
  1665. // tips += `\n\n${flag_info.rule_info}\nytInitialData_rule: \n${ytInitialData_rule && ytInitialData_rule.join('\n')}\n\nytInitialPlayerResponse_rule: \n${ytInitialPlayerResponse_rule && ytInitialPlayerResponse_rule.join('\n')}
  1666. // \n\nytInitialReelWatchSequenceResponse_rule: \n${ytInitialReelWatchSequenceResponse_rule && ytInitialReelWatchSequenceResponse_rule.join('\n')}`;
  1667. //账号信息
  1668. const str_channel_id = '' + channel_id;
  1669. tips += `\n\naccount_info\nchannel_id: ${(str_channel_id === 'default' || str_channel_id.length <= 10) ? str_channel_id : (str_channel_id.slice(0, 5) + '...' + str_channel_id.slice(-5))}`;
  1670. tips += `\nreal_language${real_language}`;
  1671. if (error_messages.length !== 0) {
  1672. tips += `\n\n${flag_info.exists_error}\n-----------${flag_info.err_msg}(${flag_info.ctoc})-----------------\n${error_messages.join('\n')}\n\n${flag_info.tips}`;
  1673. }
  1674. display_error_win(tips);
  1675. }
  1676. }, 500);
  1677. }
  1678. };
  1679. search_input_node.addEventListener('input', oninput);
  1680. } else if (count > 50) {
  1681. clearInterval(interval_id);
  1682. log('搜索框未找到', -1);
  1683. }
  1684. }, 200);
  1685.  
  1686. }
  1687.  
  1688. function simulate_swipeup(target, start, end) {
  1689. function createAndDispatchTouchEvent(type, target, clientY) {
  1690. const touches = type !== 'touchend' && [new Touch({
  1691. identifier: 0,
  1692. target: target,
  1693. clientY: clientY
  1694. })] || [];
  1695. let touchEvent = new TouchEvent(type, {
  1696. touches: touches,
  1697. bubbles: true,
  1698. cancelable: true
  1699. });
  1700. target.dispatchEvent(touchEvent);
  1701. }
  1702. createAndDispatchTouchEvent('touchstart', target, start);
  1703. createAndDispatchTouchEvent('touchmove', target, end);
  1704. createAndDispatchTouchEvent('touchend', target);
  1705. }
  1706.  
  1707. function getCookie(cookieName) {
  1708. const name = cookieName + "=";
  1709. let decodedCookie;
  1710. try {
  1711. decodedCookie = decodeURIComponent(document.cookie);
  1712. } catch (error) {
  1713. log('cookie decode error', error, -1);
  1714. return null;
  1715. }
  1716. const cookieArray = decodedCookie.split(';');
  1717. for (let i = 0; i < cookieArray.length; i++) {
  1718. const cookie = cookieArray[i].trim();
  1719.  
  1720. if (cookie.startsWith(name)) {
  1721. return cookie.substring(name.length, cookie.length);
  1722. }
  1723. }
  1724. return null;
  1725. }
  1726.  
  1727. function copyToClipboard(text) {
  1728. GM_setClipboard(text, "text");
  1729. // if (navigator.clipboard?.writeText) return navigator.clipboard.writeText(text);
  1730. // const textarea = unsafeWindow.document.createElement("textarea");
  1731. // textarea.value = text;
  1732. // unsafeWindow.document.body.appendChild(textarea);
  1733. // textarea.select();
  1734. // unsafeWindow.document.execCommand('copy');
  1735. // unsafeWindow.document.body.removeChild(textarea);
  1736. }
  1737.  
  1738. function check_native(name, fun) {
  1739. const fun_str = fun.toString();
  1740. if (browser_info.name !== 'Firefox') {
  1741. return `function ${name}() { [native code] }` === fun_str;
  1742. } else {
  1743. return `function ${name}() {\n [native code]\n}` === fun_str;
  1744. }
  1745. }
  1746.  
  1747. function set_history_hook(window_obj) {
  1748. const wrap = function (type) {
  1749. const origin = window_obj.history[type];
  1750. return function () {
  1751. let rv;
  1752. try {
  1753. rv = origin.apply(this, arguments);
  1754. } catch (error) {
  1755. log('history hook error', error, 0);
  1756. return;
  1757. }
  1758. let url = arguments[2] || location.href;
  1759. url.startsWith('/') && (url = location.origin + url);
  1760. !url.startsWith('http') && (url = location.origin + '/' + url);
  1761. url_change(url);
  1762. return rv;
  1763. };
  1764. };
  1765. window_obj.history.pushState = wrap('pushState');
  1766. window_obj.history.replaceState = wrap('replaceState');
  1767. }
  1768.  
  1769. function url_observer() {
  1770. // if (unsafeWindow.navigation) {
  1771. // unsafeWindow.navigation.addEventListener('navigate', (event) => {
  1772. // url_change(event);
  1773. // });
  1774. // return;
  1775. // }
  1776. set_history_hook(unsafeWindow);
  1777. unsafeWindow.addEventListener('popstate', function (event) {
  1778. url_change(event);
  1779. });
  1780. unsafeWindow.addEventListener('hashchange', function (event) {
  1781. url_change(event);
  1782. });
  1783. }
  1784.  
  1785. function url_change(event = null) {
  1786. let destination_url;
  1787. if (typeof (event) === 'object')
  1788. destination_url = event?.destination?.url || '';
  1789. else
  1790. destination_url = event;
  1791.  
  1792. if (destination_url.startsWith('about:blank')) return;
  1793. if (destination_url === href) return;
  1794. href = destination_url || location.href;
  1795. log('网页url改变 href -> ' + href, 0);
  1796. const tmp_page_type = get_page_type();
  1797. if (tmp_page_type !== page_type) {
  1798. page_type = tmp_page_type;
  1799. config_api.config_init();
  1800. set_search_listen();
  1801. }
  1802. on_page_change();
  1803. }
  1804.  
  1805. function get_page_type(url = href) {
  1806. if (!url) return 'other';
  1807. url.startsWith('/') && (url = location.origin + url);
  1808. const base_url = url.split('?')[0];
  1809. let tmp_page_type;
  1810. if (base_url.match('https://www.youtube.com/?$')) tmp_page_type = 'yt_home';
  1811. else if (base_url.match('https://m.youtube.com/?#?$')) tmp_page_type = 'mobile_yt_home';
  1812. else if (base_url.match('https://www.youtube.com/watch$')) tmp_page_type = 'yt_watch';
  1813. else if (base_url.match('https://m.youtube.com/watch$')) tmp_page_type = 'mobile_yt_watch';
  1814. else if (base_url.match('https://www.youtube.com/results$')) tmp_page_type = 'yt_search';
  1815. else if (base_url.match('https://m.youtube.com/results$')) tmp_page_type = 'mobile_yt_search';
  1816. else if (base_url.startsWith('https://www.youtube.com/shorts')) tmp_page_type = 'yt_shorts';
  1817. else if (base_url.startsWith('https://m.youtube.com/shorts')) tmp_page_type = 'mobile_yt_shorts';
  1818. else if (base_url.match('https://www.youtubekids.com/watch$')) tmp_page_type = 'yt_kids_watch';
  1819. else if (base_url.match('https://music.youtube.com/?$')) tmp_page_type = 'yt_music_home';
  1820. else if (base_url.match('https://music.youtube.com/watch$')) tmp_page_type = 'yt_music_watch';
  1821. else if (base_url.match('https://m.youtube.com/#searching$')) tmp_page_type = 'mobile_yt_home_searching';
  1822. else if (base_url.startsWith('https://www.youtube.com/playlist')) tmp_page_type = 'yt_watch_playlist';
  1823. else if (base_url.includes('channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ')) tmp_page_type = 'yt_music_channel';
  1824. else tmp_page_type = 'other';
  1825. if (tmp_page_type === 'mobile_yt_watch' && href.endsWith('#searching')) tmp_page_type = 'mobile_yt_watch_searching';
  1826. return tmp_page_type;
  1827. }
  1828.  
  1829. function set_debugger() {
  1830. while (!debugger_fun_name) {
  1831. let tmp = crypto.randomUUID().substring(0, Math.floor(Math.random() * 4) + 3).replace(/-/g, '');
  1832. tmp = tmp.match('[a-z].+')?.[0];
  1833. if (tmp && !unsafeWindow[tmp]) {
  1834. debugger_fun_name = tmp;
  1835. }
  1836. }
  1837. log(`debugger_fun_name ${debugger_fun_name}`, 0);
  1838. const debugger_config_info = {
  1839. 'ytInitialPlayerResponse': debugger_ytInitialPlayerResponse,
  1840. 'ytInitialData': debugger_ytInitialData,
  1841. 'ytInitialReelWatchSequenceResponse': debugger_ytInitialReelWatchSequenceResponse,
  1842. 'music_initialData': debugger_music_initialData,
  1843. 'inject_info': inject_info,
  1844. 'info': [
  1845. 'ytInitialData_rule',
  1846. 'ytInitialPlayerResponse_rule',
  1847. 'is_account_init',
  1848. 'user_data',
  1849. 'mobile_web',
  1850. 'page_type',
  1851. 'tmp_debugger_value',
  1852. ],
  1853. };
  1854. unsafeWindow[debugger_fun_name] = function (action = null) {
  1855. const keys = Object.keys(debugger_config_info);
  1856. if (!action && action !== 0) { debugger; return; }
  1857. if (action === 'ytInitialPlayerResponse') log('ytInitialPlayerResponse', debugger_ytInitialPlayerResponse, 0);
  1858. if (action === 'ytInitialData') log('ytInitialData', debugger_ytInitialData, 0);
  1859. if (action === 'inject_info') log('inject_info', inject_info, 0);
  1860. if (action === 'info') {
  1861. if (limit_eval) {
  1862. log('eval限制使用了', 0);
  1863. } else {
  1864. for (let key of debugger_config_info['info']) {
  1865. log(key, eval(trustedScript(key)), 0);
  1866. }
  1867. }
  1868. return;
  1869. }
  1870. if (action === 'list') {
  1871. keys.forEach(function (key, index) {
  1872. log(index, key, 0);
  1873. });
  1874. }
  1875. if (typeof (action) === 'number') {
  1876. if (action < keys.length) {
  1877. unsafeWindow[debugger_fun_name](keys[action]);
  1878. } else if (action >= keys.length) {
  1879. keys.forEach(function (key) {
  1880. unsafeWindow[debugger_fun_name](key);
  1881. });
  1882. }
  1883. }
  1884. };
  1885. }
  1886.  
  1887. function log() {
  1888. const arguments_arr = [...arguments];
  1889. const flag = arguments_arr.pop();
  1890. if (flag === -1) {
  1891. error_messages.push(arguments_arr.join(' '));
  1892. }
  1893. if (flag === 999) arguments_arr.unshift('-----test---test-----');
  1894. if (flag !== 0 && flag !== 999) arguments_arr.push(getCodeLocation());
  1895. if (flag === 0 || flag === 999) {
  1896. const array_length = arguments_arr.length;
  1897. const color = flag === 0 ? 'orange' : 'blue';
  1898. const css_str = `color: ${color};font-size: 20px`;
  1899. for (let i = 0; i < array_length; i++) {
  1900. if (typeof (arguments_arr[i]) === 'string') {
  1901. arguments_arr[i] = '%c' + arguments_arr[i];
  1902. i === (array_length - 1) ? arguments_arr.push(css_str) : (arguments_arr.splice(i + 1, 0, css_str));
  1903. break;
  1904. }
  1905. }
  1906. }
  1907. if ([-1, 0, 999].includes(flag) || open_debugger) flag === -1 ? origin_console.error(...arguments_arr) : origin_console.log(...arguments_arr);
  1908. }
  1909.  
  1910. function getBrowserInfo() {
  1911. const userAgent = navigator.userAgent;
  1912. let browserName;
  1913. let browserVersion;
  1914. const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
  1915. if (userAgent.indexOf("Firefox") > -1) {
  1916. browserName = "Firefox";
  1917. browserVersion = userAgent.match(/Firefox\/([0-9.]+)/)[1];
  1918. } else if (userAgent.indexOf("OPR") > -1 || userAgent.indexOf("Opera") > -1) {
  1919. browserName = "Opera";
  1920. browserVersion = userAgent.match(/(OPR|Opera)\/([0-9.]+)/)[2];
  1921. } else if (userAgent.indexOf("Edg") > -1) {
  1922. browserName = "Edge";
  1923. browserVersion = userAgent.match(/Edg\/([0-9.]+)/)[1];
  1924. } else if (userAgent.indexOf("Chrome") > -1) {
  1925. browserName = "Chrome";
  1926. browserVersion = userAgent.match(/Chrome\/([0-9.]+)/)[1];
  1927. } else if (userAgent.indexOf("Safari") > -1) {
  1928. browserName = "Safari";
  1929. browserVersion = userAgent.match(/Version\/([0-9.]+)/)[1];
  1930. } else if (userAgent.indexOf("MSIE") > -1 || userAgent.indexOf("rv:") > -1) {
  1931. browserName = "Internet Explorer";
  1932. browserVersion = userAgent.match(/(MSIE |rv:)([0-9.]+)/)[2];
  1933. } else {
  1934. browserName = "Unknown";
  1935. browserVersion = "N/A";
  1936. }
  1937.  
  1938. return {
  1939. name: browserName,
  1940. version: browserVersion,
  1941. isMobile: isMobile,
  1942. };
  1943. }
  1944.  
  1945. function getCodeLocation() {
  1946. if (['Firefox'].includes(browser_info.name)) return "";
  1947. const callstack = new Error().stack.split("\n");
  1948. callstack.shift();
  1949. while (callstack.length && callstack[0].includes("-extension://")) {
  1950. callstack.shift();
  1951. }
  1952. if (!callstack.length) {
  1953. return "";
  1954. }
  1955. return '\n' + callstack[0].trim();
  1956. }
  1957.  
  1958. function display_error_win(msg) {
  1959. const container = unsafeWindow.document.createElement("div");
  1960. container.className = "popup";
  1961. container.id = "infoBox";
  1962. container.innerHTML = `
  1963. <h2 class="recommend-title" style="user-select:none;font-weight:bold;font-size:large;background-color:#3498db;color:#ffffff;border:none;padding:5px 10px;border-radius:5px;text-align:center;margin:0 auto;width:100px;">信息(message)</h2>
  1964. <label id="infoText" style="color: #000; font-size:large;display:block;text-align:center;margin-top:10px;min-width:200px;min-height:50px;width:auto;max-height:200px;overflow:auto;"></label>
  1965. <div style="display:flex;justify-content:space-around;margin-top:10px;">
  1966. <button class="btn" id="copyBtn" style="cursor:pointer;background-color:#3498db;color:#ffffff;border:none;padding:5px 10px;border-radius:5px;">复制(copy)</button>
  1967. <button class="btn" id="closeBtn2" style="cursor:pointer;background-color:#3498db;color:#ffffff;border:none;padding:5px 10px;border-radius:5px;">关闭(close)</button>
  1968. </div>`;
  1969.  
  1970. container.style.cssText = 'z-index:999999999;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);padding:20px;background-color:#ffffff;border:1px solid #3498db;border-radius:5px;box-shadow:0 0 10px rgba(0,0,0,0.3);width:auto;max-width:80%;max-height:80vh;overflow-y:auto;';
  1971. unsafeWindow.document.body.appendChild(container);
  1972. unsafeWindow.document.getElementById('infoText').innerText = msg;
  1973.  
  1974. unsafeWindow.document.getElementById('copyBtn').addEventListener('click', () => {
  1975. copyToClipboard(msg);
  1976. unsafeWindow.document.getElementById('infoBox').remove();
  1977. alert('复制成功!(copy success)');
  1978. });
  1979. unsafeWindow.document.getElementById('closeBtn2').addEventListener('click', () => {
  1980. unsafeWindow.document.getElementById('infoBox').remove();
  1981. });
  1982. }
  1983.  
  1984. function display_config_win() {
  1985. const css_str = '.popup{ z-index:999999999; position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);padding:20px;background-color:#ffffff;border:1px solid #3498db;border-radius:5px;box-shadow:0 0 10px rgba(0,0,0,0.3);width:200px; max-height: 80vh;overflow-y: auto;}.btn{cursor:pointer;background-color:#3498db;color:#ffffff;border:none;padding:5px 10px;margin:0 auto;border-radius:5px;display:block;margin-top:10px;}.recommend-title{user-select: none;font-weight:bold;font-size: large;background-color:#3498db;color:#ffffff;border:none;padding:5px;padding-left:10px;border-radius:5px;width:180px;text-align:start;}.select-group{cursor:pointer;padding:5px;list-style-type:none;margin:0;padding-left:0;user-select: none;}.item-group{list-style-type:none;margin:0;padding-left:0;} .close-btn{position:absolute;top:5px;right:5px;cursor:pointer;border:none;background-color:floralwhite;} label{font-size: large;}';
  1986. const style = unsafeWindow.document.createElement("style");
  1987. style.textContent = css_str;
  1988. $('body').appendChild(style);
  1989. let win_config;
  1990. const home_watch_config = {
  1991. "recommend_btn": [
  1992. {
  1993. "id": "open_recommend_shorts",
  1994. "title": "btn_recommend_shorts",
  1995. "items": [
  1996. {
  1997. "tag": "btn_lable_open",
  1998. "value": "on",
  1999. },
  2000. {
  2001. "tag": "btn_lable_close",
  2002. "value": "off",
  2003. },
  2004. {
  2005. "tag": "btn_lable_subscribed",
  2006. "value": "subscribed",
  2007. "tips": "recommend_subscribed_lable_tips",
  2008. "condition": {
  2009. "login_status": true
  2010. }
  2011. }
  2012. ]
  2013. }, {
  2014. "id": "open_recommend_liveroom",
  2015. "title": "btn_recommend_liveroom",
  2016. "items": [
  2017. {
  2018. "tag": "btn_lable_open",
  2019. "value": "on",
  2020. },
  2021. {
  2022. "tag": "btn_lable_close",
  2023. "value": "off",
  2024. },
  2025. {
  2026. "tag": "btn_lable_subscribed",
  2027. "value": "subscribed",
  2028. "tips": "recommend_subscribed_lable_tips",
  2029. "condition": {
  2030. "login_status": true
  2031. }
  2032. }
  2033. ]
  2034. }
  2035. ,
  2036. {
  2037. "id": "open_recommend_movie",
  2038. "title": "btn_recommend_movie",
  2039. "items": [
  2040. {
  2041. "tag": "btn_lable_open",
  2042. "value": "on",
  2043. },
  2044. {
  2045. "tag": "btn_lable_close",
  2046. "value": "off",
  2047. },
  2048. ]
  2049. },
  2050. {
  2051. "id": "open_recommend_popular",
  2052. "title": "btn_recommend_popular",
  2053. "items": [
  2054. {
  2055. "tag": "btn_lable_open",
  2056. "value": "on",
  2057. },
  2058. {
  2059. "tag": "btn_lable_close",
  2060. "value": "off",
  2061. },
  2062. ]
  2063. },
  2064. {
  2065. "id": "open_recommend_playables",
  2066. "title": "btn_recommend_game",
  2067. "items": [
  2068. {
  2069. "tag": "btn_lable_open",
  2070. "value": "on",
  2071. },
  2072. {
  2073. "tag": "btn_lable_close",
  2074. "value": "off",
  2075. },
  2076. ]
  2077. }
  2078.  
  2079. ]
  2080. };
  2081. const shorts_config = {
  2082. "recommend_btn": [
  2083. {
  2084. "id": "add_shorts_upload_date",
  2085. "title": "title_add_shorts_upload_date",
  2086. "items": [
  2087. {
  2088. "tag": "btn_lable_open",
  2089. "value": "on",
  2090. },
  2091. {
  2092. "tag": "btn_lable_close",
  2093. "value": "off",
  2094. },
  2095. ]
  2096. },
  2097. {
  2098. "id": "shorts_change_author_name",
  2099. "title": "title_shorts_change_author_name",
  2100. "items": [
  2101. {
  2102. "tag": "btn_lable_open",
  2103. "value": "on",
  2104. },
  2105. {
  2106. "tag": "btn_lable_close",
  2107. "value": "off",
  2108. },
  2109. ]
  2110. },
  2111. {
  2112. "id": "short_buy_super_thanks",
  2113. "title": "short_buy_super_thanks",
  2114. "items": [
  2115. {
  2116. "tag": "btn_lable_open",
  2117. "value": "on",
  2118. },
  2119. {
  2120. "tag": "btn_lable_close",
  2121. "value": "off",
  2122. },
  2123. ]
  2124. },
  2125. {
  2126. "id": "shorts_disable_loop_play",
  2127. "title": "bt_shorts_disable_loop_play_title",
  2128. "items": [
  2129. {
  2130. "tag": "btn_lable_open",
  2131. "value": "on",
  2132. },
  2133. {
  2134. "tag": "btn_lable_close",
  2135. "value": "off",
  2136. },
  2137. ]
  2138. },
  2139. {
  2140. "id": "shorts_auto_scroll",
  2141. "title": "btn_shorts_auto_scroll_title",
  2142. "items": [
  2143. {
  2144. "tag": "btn_lable_open",
  2145. "value": "on",
  2146. },
  2147. {
  2148. "tag": "btn_lable_close",
  2149. "value": "off",
  2150. },
  2151. ]
  2152. },
  2153. {
  2154. "id": "shorts_add_video_progress",
  2155. "title": "btn_shorts_add_video_progress_title",
  2156. "items": [
  2157. {
  2158. "tag": "btn_lable_open",
  2159. "value": "on",
  2160. },
  2161. {
  2162. "tag": "btn_lable_close",
  2163. "value": "off",
  2164. },
  2165. ]
  2166. },
  2167. {
  2168. "id": "shorts_dbclick_like",
  2169. "title": "btn_shorts_dbclick_like_title",
  2170. "items": [
  2171. {
  2172. "tag": "btn_lable_open",
  2173. "value": "on",
  2174. },
  2175. {
  2176. "tag": "btn_lable_close",
  2177. "value": "off",
  2178. },
  2179. ]
  2180. }
  2181. ]
  2182. };
  2183. // if (page_type === 'yt_shorts') {
  2184. // shorts_config.recommend_btn.push();
  2185. // }
  2186. const common_config = {
  2187. "recommend_btn": [
  2188. // {
  2189. // "id": "dbclick_download_video",
  2190. // "title": "btn_dbclick_download_video_title",
  2191. // "tips": "btn_dbclick_download_video_tips",
  2192. // "items": [
  2193. // {
  2194. // "tag": "btn_lable_open",
  2195. // "tips": "btn_dbclick_download_video_tips",
  2196. // "value": "on",
  2197. // },
  2198. // {
  2199. // "tag": "btn_lable_close",
  2200. // "value": "off",
  2201. // },
  2202. // ]
  2203. // }
  2204. ]
  2205. };
  2206. if (['mobile_yt_home_searching', 'mobile_yt_watch_searching'].includes(page_type)) {
  2207. home_watch_config.recommend_btn.push({ "split_line": true, "title": "shorts_recommend_split_tag" });
  2208. home_watch_config.recommend_btn.push(...shorts_config.recommend_btn);
  2209. }
  2210. ['yt_home', 'yt_watch', 'mobile_yt_watch_searching', 'mobile_yt_home_searching'].includes(page_type) && (win_config = home_watch_config);
  2211. ['yt_shorts'].includes(page_type) && (win_config = shorts_config);
  2212. win_config && (win_config.recommend_btn.push(...common_config.recommend_btn));
  2213. if (!win_config) return;
  2214. const popup_node = unsafeWindow.document.getElementById('xxx_popup');
  2215. if (popup_node) {
  2216. popup_node.remove_popup_listener('rm');
  2217. }
  2218. const popup = unsafeWindow.document.createElement('div');
  2219. popup.id = 'xxx_popup';
  2220. popup.className = 'popup';
  2221. const close_btn = unsafeWindow.document.createElement('button');
  2222. close_btn.className = 'close-btn';
  2223. close_btn.innerHTML = 'x';
  2224. close_btn.addEventListener('click', remove_popup_hander
  2225. );
  2226. popup.append(close_btn);
  2227. const item_groups = [];
  2228. const item_group = unsafeWindow.document.createElement('ul');
  2229. item_group.className = 'item-group';
  2230. win_config.recommend_btn.forEach(recommend_item_info => {
  2231. if (recommend_item_info.split_line) {
  2232. let p = unsafeWindow.document.createElement('h1');
  2233. p.style.fontSize = 'large';
  2234. p.style.textAlign = 'center';
  2235. p.style.color = 'red';
  2236. p.style.padding = '20px 20px';
  2237. p.style.fontWeight = 'bold';
  2238. p.style.p;
  2239. p.innerText = flag_info[recommend_item_info.title];
  2240. item_groups.push(p);
  2241. return;
  2242. }
  2243. const recommend_id = recommend_item_info.id;
  2244. const recommend_title = flag_info[recommend_item_info.title];
  2245. const recommend_tips = recommend_item_info.tips && flag_info[recommend_item_info.tips];
  2246. const select_item_infos = recommend_item_info.items || [];
  2247. const select_items = [];
  2248. const item = unsafeWindow.document.createElement('li');
  2249. const select_group = unsafeWindow.document.createElement('ul');
  2250. select_group.className = 'select-group';
  2251. select_group.id = recommend_id;
  2252. select_item_infos.forEach(select_item_info => {
  2253. const tag = flag_info[select_item_info.tag];
  2254. const value = select_item_info.value;
  2255. const tips = flag_info[select_item_info.tips];
  2256. const condition = select_item_info.condition;
  2257. const select_item = unsafeWindow.document.createElement('li');
  2258. const input = unsafeWindow.document.createElement('input');
  2259. input.type = 'radio';
  2260. input.name = recommend_id + '_option';
  2261. input.id = recommend_id + '_' + value;
  2262. input.value = value;
  2263. if (condition && condition.login_status) {
  2264. if (condition.login_status !== user_data.login) {
  2265. input.disabled = true;
  2266. }
  2267. }
  2268. if (user_data[recommend_id] === value) {
  2269. input.checked = true;
  2270. }
  2271. input.addEventListener('click', () => {
  2272. handle_recommend_radio(input);
  2273. });
  2274. const label = unsafeWindow.document.createElement('label');
  2275. label.htmlFor = input.id;
  2276. label.innerText = tag;
  2277. tips && (label.title = tips);
  2278. select_item.append(input, label);
  2279. select_items.push(select_item);
  2280. });
  2281. const recommend_title_div = unsafeWindow.document.createElement('div');
  2282. recommend_title_div.className = 'recommend-title';
  2283. recommend_title_div.innerText = recommend_title;
  2284. recommend_tips && (recommend_title_div.title = recommend_tips);
  2285. select_group.append(...select_items);
  2286. item.append(recommend_title_div, select_group);
  2287. item_groups.push(item);
  2288. });
  2289. item_group.append(...item_groups);
  2290. popup.append(item_group);
  2291. unsafeWindow.document.body.append(popup);
  2292.  
  2293. function remove_popup_hander(event) {
  2294. if ((event.target && !popup.contains(event.target)) || event.target === close_btn || event === 'rm') {
  2295. popup.remove();
  2296. unsafeWindow.document.removeEventListener('click', remove_popup_hander);
  2297. if (['mobile_yt_watch_searching', 'mobile_yt_home_searching'].includes(page_type)) {
  2298. history.back();
  2299. }
  2300. }
  2301. }
  2302. popup.remove_popup_listener = remove_popup_hander;
  2303. unsafeWindow.document.addEventListener('click', remove_popup_hander);
  2304.  
  2305. return;
  2306. // 隐藏搜索推荐元素
  2307. let count = 0;
  2308. const interval_id = setInterval(() => {
  2309. if (count = 30)
  2310. clearInterval(interval_id);
  2311. count++;
  2312. let class_name;
  2313. if (['yt_home', , 'yt_watch', 'yt_shorts'].includes(page_type)) class_name = 'gstl_50';
  2314. if (['mobile_yt_home', , 'mobile_yt_watch'].includes(page_type)) class_name = 'searchbox-dropdown';
  2315. if (class_name) {
  2316. let nodes = unsafeWindow.document.getElementsByClassName(class_name);
  2317. if (nodes.length) {
  2318. nodes = Array.from(nodes);
  2319. nodes.forEach(function (node) {
  2320. node.style.display = 'none';
  2321. setTimeout(() => {
  2322. node.style.display = '';
  2323. }, 100);
  2324. });
  2325. }
  2326. }
  2327. }, 100);
  2328. }
  2329.  
  2330. function handle_recommend_radio(input_obj) {
  2331. user_data[input_obj.parentNode.parentNode.id] = input_obj.value;
  2332. user_data_api.set();
  2333. config_api.config_init(user_data.language);
  2334. }
  2335.  
  2336. function display_update_win() {
  2337. function btn_click() {
  2338. const btn = this;
  2339. if (btn.id === 'go_btn') {
  2340. location.href = script_url;
  2341. }
  2342. container.remove();
  2343. }
  2344. const css_str = "#update_tips_win { z-index:9999999999; display: flex; position: fixed; bottom: 20px; right: 20px; padding: 10px 20px; background-color: #fff; border: 1px solid #ccc; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); border-radius: 10px; } .btn { margin: 0 10px; display: inline-block; padding: 5px 10px; background-color: #3498db; color: #fff; border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.3s ease; } .btn:hover { background-color: #2980b9; }";
  2345. const style = unsafeWindow.document.createElement("style");
  2346. style.innerText = css_str;
  2347. $('body').appendChild(style);
  2348. const container = unsafeWindow.document.createElement("div");
  2349. container.id = "update_tips_win";
  2350. const span = unsafeWindow.document.createElement("span");
  2351. span.textContent = GM_info.script.name + '有更新了!!';
  2352. container.appendChild(span);
  2353. const go_btn = unsafeWindow.document.createElement("button");
  2354. go_btn.textContent = 'GO';
  2355. go_btn.id = 'go_btn';
  2356. go_btn.className = 'btn';
  2357. go_btn.onclick = btn_click;
  2358. container.appendChild(go_btn);
  2359. const no_btn = unsafeWindow.document.createElement("button");
  2360. no_btn.textContent = 'NO';
  2361. no_btn.className = 'btn';
  2362. no_btn.id = 'no_btn';
  2363. no_btn.onclick = btn_click;
  2364. container.appendChild(no_btn);
  2365. $('body').appendChild(container);
  2366. }
  2367.  
  2368. function check_update() {
  2369. const script_handler = GM_info.scriptHandler;
  2370. if (['Via'].includes(script_handler)) return;
  2371. const last_check_time = GM_getValue('last_check_time', 0);
  2372. if ((Date.now() - last_check_time) < 1000 * 60 * 60 * 24) return;
  2373. GM_xmlhttpRequest({
  2374. method: 'GET',
  2375. url: script_url,
  2376. onload: function (response) {
  2377. const onlineScript = response.responseText;
  2378. // 从线上脚本中提取版本号和元数据信息
  2379. const onlineMeta = onlineScript.match(/@version\s+([^\s]+)/i);
  2380. const onlineVersion = onlineMeta ? onlineMeta[1] : '';
  2381. if (onlineVersion > GM_info.script.version) {
  2382. display_update_win();
  2383. }
  2384. }
  2385. });
  2386. GM_setValue('last_check_time', Date.now());
  2387. }
  2388.  
  2389. function obj_process_filter(path_info, json_obj) {
  2390. if (!['yt_home', 'yt_watch', 'mobile_yt_home', 'mobile_yt_watch'].includes(page_type)) return false;
  2391. if (!user_data.login || user_data.channel_infos.ids.length === 0) return false;
  2392.  
  2393. if (user_data.open_recommend_shorts === 'subscribed' && path_info.condition_value === 'YOUTUBE_SHORTS_BRAND_24') {
  2394. if (path_info.express.includes('YOUTUBE_SHORTS_BRAND_24')) {
  2395. let video_list_path;
  2396. video_list_path = path_info.conform_value_path.split('["icon"]')[0] + (page_type === 'yt_home' ? '["contents"]' : '["items"]');
  2397. const video_list = data_process.string_to_value(json_obj, video_list_path) || [];
  2398. shorts_fun.node_parse(video_list);
  2399. }
  2400. }
  2401.  
  2402. if (user_data.open_recommend_liveroom === 'subscribed' && ['UPCOMING', 'LIVE', 'BADGE_STYLE_TYPE_LIVE_NOW'].includes(path_info.condition_value)) {
  2403. if (path_info.express.includes('UPCOMING')) {
  2404. try {
  2405. const match = JSON.stringify(data_process.string_to_value(json_obj, path_info.deal_path)).match(/"browseId"\:"(.*?)"/);
  2406. let id;
  2407. if (match && match.length > 1) id = match[1];
  2408. if (!id) {
  2409. log('id获取失败\n' + JSON.stringify(path_info), -1);
  2410. }
  2411. if (user_data.channel_infos.ids.includes(id)) {
  2412. const index = user_data.channel_infos.ids.indexOf(id);
  2413. const name = user_data.channel_infos.names[index];
  2414. log('不过滤 ' + name + (path_info.condition_value === 'UPCOMING' ? ' 等待发布的直播' : ' 正在进行的直播'), 'shorts');
  2415. return true;
  2416. }
  2417. let msg = `过滤 ${id} ${path_info.condition_value === 'UPCOMING' ? ' 等待发布的直播' : ' 正在进行的直播'}`;
  2418. log(msg, 'shorts');
  2419. } catch (error) {
  2420. log(error, -1);
  2421. }
  2422. }
  2423. }
  2424. return false;
  2425. }
  2426.  
  2427. function get_shorts_fun() {
  2428. class ShortsFun {
  2429. constructor() {
  2430. this.parsing = false;
  2431. this.shorts_list = [];
  2432. }
  2433. node_parse(video_list) {
  2434. !user_data.shorts_list && (user_data.shorts_list = []);
  2435. let video_id, title, views_lable, thumbnail_url;
  2436. let count = 0;
  2437. for (let video_info of video_list) {
  2438. count++;
  2439. if (page_type === "yt_home") {
  2440. video_id = video_info.richItemRenderer.content.reelItemRenderer.videoId;
  2441. title = video_info.richItemRenderer.content.reelItemRenderer.headline.simpleText;
  2442. views_lable = video_info.richItemRenderer.content.reelItemRenderer.viewCountText.simpleText;
  2443. thumbnail_url = video_info.richItemRenderer.content.reelItemRenderer.thumbnail.thumbnails[0].url;
  2444. }
  2445. if (page_type === "yt_watch") {
  2446. video_id = video_info.reelItemRenderer.videoId;
  2447. title = video_info.reelItemRenderer.headline.simpleText;
  2448. views_lable = video_info.reelItemRenderer.viewCountText.simpleText;
  2449. thumbnail_url = video_info.reelItemRenderer.thumbnail.thumbnails[0].url;
  2450. }
  2451. if (["mobile_yt_home", "mobile_yt_watch"].includes(page_type)) {
  2452. video_id = video_info.shortsLockupViewModel.entityId.replace('shorts-shelf-item-', '');
  2453. title = video_info.shortsLockupViewModel.overlayMetadata.primaryText.content;
  2454. views_lable = video_info.shortsLockupViewModel.overlayMetadata.secondaryText.content;
  2455. thumbnail_url = video_info.shortsLockupViewModel.thumbnail.sources[0].url;
  2456. }
  2457. this.shorts_list.push({
  2458. id: video_id,
  2459. title: title,
  2460. views_lable: views_lable,
  2461. thumbnail_url: thumbnail_url
  2462. });
  2463. if (!this.parsing) {
  2464. this.parsing = true;
  2465. setTimeout(() => {
  2466. this.parse_shorts_list();
  2467. }, shorts_parse_delay);
  2468. }
  2469.  
  2470. }
  2471.  
  2472. }
  2473. get_shorts_section() {
  2474. if (!user_data.shorts_list || !user_data.shorts_list.length) return;
  2475. let root, item_path;
  2476. const items = [];
  2477. if (page_type == 'yt_home') {
  2478. root = {
  2479. "richSectionRenderer": {
  2480. "content": {
  2481. "richShelfRenderer": {
  2482. "title": {
  2483. "runs": [
  2484. {
  2485. "text": "Shorts"
  2486. }
  2487. ]
  2488. },
  2489. "contents": [],
  2490. "trackingParams": "CNMEEN-DAyITCOGA_NHuz4UDFWdqTAgdfF4E-Q==",
  2491. "menu": {
  2492. "menuRenderer": {
  2493. "trackingParams": "CNMEEN-DAyITCOGA_NHuz4UDFWdqTAgdfF4E-Q==",
  2494. "topLevelButtons": [
  2495. {
  2496. "buttonRenderer": {
  2497. "style": "STYLE_OPACITY",
  2498. "size": "SIZE_DEFAULT",
  2499. "isDisabled": false,
  2500. "serviceEndpoint": {
  2501. "clickTrackingParams": "CNYEEKqJCRgMIhMI4YD80e7PhQMVZ2pMCB18XgT5",
  2502. "commandMetadata": {
  2503. "webCommandMetadata": {
  2504. "sendPost": true,
  2505. "apiUrl": "/youtubei/v1/feedback"
  2506. }
  2507. },
  2508. "feedbackEndpoint": {
  2509. "feedbackToken": "AB9zfpIcTXNyA3lbF_28icb4umRJ5AveSSTqmF7T9gE8k-Sw7HrOTLE5wzA2TScqfTByCI-cR9nPuVMSWAgbNuuaruVBYx2-2dGAzujQTL8KGMOyCFM_wmGhkLTSdUBQzsFQRHEibpg_",
  2510. "uiActions": {
  2511. "hideEnclosingContainer": true
  2512. },
  2513. "actions": [
  2514. {
  2515. "clickTrackingParams": "CNYEEKqJCRgMIhMI4YD80e7PhQMVZ2pMCB18XgT5",
  2516. "replaceEnclosingAction": {
  2517. "item": {
  2518. "notificationMultiActionRenderer": {
  2519. "responseText": {
  2520. "runs": [
  2521. {
  2522. "text": "Shelf will be hidden for "
  2523. },
  2524. {
  2525. "text": "30"
  2526. },
  2527. {
  2528. "text": " days"
  2529. }
  2530. ]
  2531. },
  2532. "buttons": [
  2533. {
  2534. "buttonRenderer": {
  2535. "style": "STYLE_BLUE_TEXT",
  2536. "text": {
  2537. "simpleText": "Undo"
  2538. },
  2539. "serviceEndpoint": {
  2540. "clickTrackingParams": "CNgEEPBbGAAiEwjhgPzR7s-FAxVnakwIHXxeBPk=",
  2541. "commandMetadata": {
  2542. "webCommandMetadata": {
  2543. "sendPost": true,
  2544. "apiUrl": "/youtubei/v1/feedback"
  2545. }
  2546. },
  2547. "undoFeedbackEndpoint": {
  2548. "undoToken": "AB9zfpLpAillN1hH9cyfSbyPRWwAhTOJo6mUTu-ony4HASc0KgCEy0ifaIrDUdJJEk4OXiPC43EMPZBEK8WGiIqeci4r97TGpabAUk84dEh7tHzF7-rsziFBGZjY92Jyk3YujrF2_wxC",
  2549. "actions": [
  2550. {
  2551. "clickTrackingParams": "CNgEEPBbGAAiEwjhgPzR7s-FAxVnakwIHXxeBPk=",
  2552. "undoFeedbackAction": {
  2553. "hack": true
  2554. }
  2555. }
  2556. ]
  2557. }
  2558. },
  2559. "trackingParams": "CNgEEPBbGAAiEwjhgPzR7s-FAxVnakwIHXxeBPk="
  2560. }
  2561. }
  2562. ],
  2563. "trackingParams": "CNcEEKW8ASITCOGA_NHuz4UDFWdqTAgdfF4E-Q=="
  2564. }
  2565. }
  2566. }
  2567. }
  2568. ]
  2569. }
  2570. },
  2571. "icon": {
  2572. "iconType": "DISMISSAL"
  2573. },
  2574. "tooltip": "Not interested",
  2575. "trackingParams": "CNYEEKqJCRgMIhMI4YD80e7PhQMVZ2pMCB18XgT5",
  2576. "accessibilityData": {
  2577. "accessibilityData": {
  2578. "label": "Not interested"
  2579. }
  2580. }
  2581. }
  2582. }
  2583. ]
  2584. }
  2585. },
  2586. "showMoreButton": {
  2587. "buttonRenderer": {
  2588. "style": "STYLE_OPACITY",
  2589. "size": "SIZE_DEFAULT",
  2590. "text": {
  2591. "runs": [
  2592. {
  2593. "text": "Show more"
  2594. }
  2595. ]
  2596. },
  2597. "icon": {
  2598. "iconType": "EXPAND"
  2599. },
  2600. "accessibility": {
  2601. "label": "Show more"
  2602. },
  2603. "trackingParams": "CNUEEJnjCyITCOGA_NHuz4UDFWdqTAgdfF4E-Q=="
  2604. }
  2605. },
  2606. "isExpanded": false,
  2607. "icon": {
  2608. "iconType": "YOUTUBE_SHORTS_BRAND_24"
  2609. },
  2610. "isTopDividerHidden": false,
  2611. "isBottomDividerHidden": false,
  2612. "showLessButton": {
  2613. "buttonRenderer": {
  2614. "style": "STYLE_OPACITY",
  2615. "size": "SIZE_DEFAULT",
  2616. "text": {
  2617. "runs": [
  2618. {
  2619. "text": "Show less"
  2620. }
  2621. ]
  2622. },
  2623. "icon": {
  2624. "iconType": "COLLAPSE"
  2625. },
  2626. "accessibility": {
  2627. "label": "Show less"
  2628. },
  2629. "trackingParams": "CNQEEPBbIhMI4YD80e7PhQMVZ2pMCB18XgT5"
  2630. }
  2631. }
  2632. }
  2633. },
  2634. "trackingParams": "CNIEEOOXBRgEIhMI4YD80e7PhQMVZ2pMCB18XgT5",
  2635. "fullBleed": false
  2636. }
  2637. };
  2638. item_path = 'root.richSectionRenderer.content.richShelfRenderer.contents';
  2639. }
  2640. if (['mobile_yt_watch', 'yt_watch'].includes(page_type)) {
  2641. root = {
  2642. "reelShelfRenderer": {
  2643. "title": {
  2644. "runs": [
  2645. {
  2646. "text": "Shorts"
  2647. }
  2648. ]
  2649. },
  2650. "items": [],
  2651. "trackingParams": "CM4CEN-DAxgEIhMInKOvhY3QhQMVGcCXCB04HQR6",
  2652. "icon": {
  2653. "iconType": "YOUTUBE_SHORTS_BRAND_24"
  2654. }
  2655. }
  2656. };
  2657. item_path = 'root.reelShelfRenderer.items';
  2658. }
  2659. if (page_type == 'mobile_yt_home') {
  2660. root = {
  2661. "richSectionRenderer": {
  2662. "content": {
  2663. "reelShelfRenderer": {
  2664. "title": {
  2665. "runs": [
  2666. {
  2667. "text": "Shorts"
  2668. }
  2669. ]
  2670. },
  2671. "button": {
  2672. "menuRenderer": {
  2673. "trackingParams": "CHYQ34MDIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
  2674. "topLevelButtons": [
  2675. {
  2676. "buttonRenderer": {
  2677. "style": "STYLE_DEFAULT",
  2678. "size": "SIZE_DEFAULT",
  2679. "isDisabled": false,
  2680. "serviceEndpoint": {
  2681. "clickTrackingParams": "CLMBEKqJCRgPIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
  2682. "commandMetadata": {
  2683. "webCommandMetadata": {
  2684. "sendPost": true,
  2685. "apiUrl": "/youtubei/v1/feedback"
  2686. }
  2687. },
  2688. "feedbackEndpoint": {
  2689. "feedbackToken": "AB9zfpJSnrbvskPWkpziyGduKV-4gTxm30-eNNYDobzecpLq84dL6HwCxdX_zbvm_OmxSKdlsngHEE1CF7JKYGiyDVYV_Q7p9ihGCzOYcnqKcAJfNnSp-U-njcnKLgCWu_USr-2prW3x",
  2690. "uiActions": {
  2691. "hideEnclosingContainer": true
  2692. },
  2693. "actions": [
  2694. {
  2695. "clickTrackingParams": "CLMBEKqJCRgPIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
  2696. "replaceEnclosingAction": {
  2697. "item": {
  2698. "notificationMultiActionRenderer": {
  2699. "responseText": {
  2700. "runs": [
  2701. {
  2702. "text": "Shelf will be hidden for "
  2703. },
  2704. {
  2705. "text": "30"
  2706. },
  2707. {
  2708. "text": " days"
  2709. }
  2710. ]
  2711. },
  2712. "buttons": [
  2713. {
  2714. "buttonRenderer": {
  2715. "style": "STYLE_MONO_TONAL",
  2716. "text": {
  2717. "runs": [
  2718. {
  2719. "text": "Undo"
  2720. }
  2721. ]
  2722. },
  2723. "serviceEndpoint": {
  2724. "clickTrackingParams": "CLUBEPBbGAAiEwip6oDKjtCFAxXPeUwIHaYIDQk=",
  2725. "commandMetadata": {
  2726. "webCommandMetadata": {
  2727. "sendPost": true,
  2728. "apiUrl": "/youtubei/v1/feedback"
  2729. }
  2730. },
  2731. "undoFeedbackEndpoint": {
  2732. "undoToken": "AB9zfpK-nY3vxgYDkvJSkuFdbeBltD0r4XdLzoFqxz6OPnmJrroOAxKfUuDny8kPjB9yyWzwEerOZqe90BakCPEJXycRSrH8sZAdnlWpEs0n0lx6qOFERE6o5jkK3mgbcVCM-Al38oGV",
  2733. "actions": [
  2734. {
  2735. "clickTrackingParams": "CLUBEPBbGAAiEwip6oDKjtCFAxXPeUwIHaYIDQk=",
  2736. "undoFeedbackAction": {
  2737. "hack": true
  2738. }
  2739. }
  2740. ]
  2741. }
  2742. },
  2743. "trackingParams": "CLUBEPBbGAAiEwip6oDKjtCFAxXPeUwIHaYIDQk="
  2744. }
  2745. }
  2746. ],
  2747. "trackingParams": "CLQBEKW8ASITCKnqgMqO0IUDFc95TAgdpggNCQ=="
  2748. }
  2749. }
  2750. }
  2751. }
  2752. ]
  2753. }
  2754. },
  2755. "icon": {
  2756. "iconType": "DISMISSAL"
  2757. },
  2758. "tooltip": "Not interested",
  2759. "trackingParams": "CLMBEKqJCRgPIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
  2760. "accessibilityData": {
  2761. "accessibilityData": {
  2762. "label": "Not interested"
  2763. }
  2764. }
  2765. }
  2766. }
  2767. ]
  2768. }
  2769. },
  2770. "items": [
  2771.  
  2772. ],
  2773. "trackingParams": "CHYQ34MDIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
  2774. "icon": {
  2775. "iconType": "YOUTUBE_SHORTS_BRAND_24"
  2776. }
  2777. }
  2778. },
  2779. "trackingParams": "CHUQ45cFGAEiEwip6oDKjtCFAxXPeUwIHaYIDQk=",
  2780. "fullBleed": false
  2781. }
  2782. };
  2783. item_path = 'root.richSectionRenderer.content.reelShelfRenderer.items';
  2784. }
  2785. let shorts;
  2786. while (shorts = user_data.shorts_list.pop()) {
  2787. const id = shorts['id'];
  2788. const title = shorts['title'];
  2789. const ago_str = shorts['ago_str'];
  2790. const author = shorts['author_name'];
  2791. const views_lable = shorts['views_lable'] + (author ? (' · ' + author) : '') + (ago_str ? (' · ' + ago_str) : '');
  2792. const thumbnail_url = shorts['thumbnail_url'];
  2793. let tmp_item;
  2794. if (['yt_home', 'yt_watch'].includes(page_type)) {
  2795. tmp_item = {
  2796. "reelItemRenderer": {
  2797. "videoId": id,
  2798. "headline": {
  2799. "simpleText": title
  2800. },
  2801. "thumbnail": {
  2802. "thumbnails": [
  2803. {
  2804. "url": thumbnail_url,
  2805. "width": 405,
  2806. "height": 720
  2807. }
  2808. ],
  2809. "isOriginalAspectRatio": true
  2810. },
  2811. "viewCountText": {
  2812. "accessibility": {
  2813. "accessibilityData": {
  2814. "label": views_lable
  2815. }
  2816. },
  2817. "simpleText": views_lable
  2818. },
  2819. "navigationEndpoint": {
  2820. "clickTrackingParams": "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6mgEFCCUQ-B0=",
  2821. "commandMetadata": {
  2822. "webCommandMetadata": {
  2823. "url": "/shorts/" + id,
  2824. "webPageType": "WEB_PAGE_TYPE_SHORTS",
  2825. "rootVe": 37414
  2826. }
  2827. },
  2828. "reelWatchEndpoint": {
  2829. "videoId": id,
  2830. "playerParams": "8AEBoAMCyAMluAQGogYVAdXZ-jvMfGWnXiNDPh0oiMSTJMUn",
  2831. "thumbnail": {
  2832. "thumbnails": [
  2833. {
  2834. "url": "https://i.ytimg.com/vi/" + id + "/frame0.jpg",
  2835. "width": 1080,
  2836. "height": 1920
  2837. }
  2838. ],
  2839. "isOriginalAspectRatio": true
  2840. },
  2841. "overlay": {
  2842. "reelPlayerOverlayRenderer": {
  2843. "style": "REEL_PLAYER_OVERLAY_STYLE_SHORTS",
  2844. "trackingParams": "CO4CELC1BCITCJyjr4WN0IUDFRnAlwgdOB0Eeg==",
  2845. "reelPlayerNavigationModel": "REEL_PLAYER_NAVIGATION_MODEL_UNSPECIFIED"
  2846. }
  2847. },
  2848. "params": "CAYwAg%3D%3D",
  2849. "sequenceProvider": "REEL_WATCH_SEQUENCE_PROVIDER_RPC",
  2850. "sequenceParams": "CgtLRmRCbnpnSjJZWSoCGAZQGWgA",
  2851. "loggingContext": {
  2852. "vssLoggingContext": {
  2853. "serializedContextData": "CgIIDA%3D%3D"
  2854. },
  2855. "qoeLoggingContext": {
  2856. "serializedContextData": "CgIIDA%3D%3D"
  2857. }
  2858. },
  2859. "ustreamerConfig": "CAwSHDFIakVXUytucVRyTENNWlgzMXdDZmYwamZQQ0U="
  2860. }
  2861. },
  2862. "menu": {
  2863. "menuRenderer": {
  2864. "items": [
  2865. {
  2866. "menuServiceItemRenderer": {
  2867. "text": {
  2868. "runs": [
  2869. {
  2870. "text": "Report"
  2871. }
  2872. ]
  2873. },
  2874. "icon": {
  2875. "iconType": "FLAG"
  2876. },
  2877. "serviceEndpoint": {
  2878. "clickTrackingParams": "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
  2879. "commandMetadata": {
  2880. "webCommandMetadata": {
  2881. "sendPost": true,
  2882. "apiUrl": "/youtubei/v1/flag/get_form"
  2883. }
  2884. },
  2885. "getReportFormEndpoint": {
  2886. "params": "EgtLRmRCbnpnSjJZWUABWABwAXgB2AEA6AEA"
  2887. }
  2888. },
  2889. "trackingParams": "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6"
  2890. }
  2891. },
  2892. {
  2893. "menuServiceItemRenderer": {
  2894. "text": {
  2895. "runs": [
  2896. {
  2897. "text": "Not interested"
  2898. }
  2899. ]
  2900. },
  2901. "icon": {
  2902. "iconType": "NOT_INTERESTED"
  2903. },
  2904. "serviceEndpoint": {
  2905. "clickTrackingParams": "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
  2906. "commandMetadata": {
  2907. "webCommandMetadata": {
  2908. "sendPost": true,
  2909. "apiUrl": "/youtubei/v1/feedback"
  2910. }
  2911. },
  2912. "feedbackEndpoint": {
  2913. "feedbackToken": "AB9zfpIBjY8nLioWtHjvUvMvrLXfhPMooShdpv91xgNNrZuxibAl6QyPeYMe7faEHcrSUm-TIqvLe2ThmYQpNRUy9rPbV1k3jjrvqqc5cOLBvnV8oN0Kbrq3-K9IjJXYitJPyOzJU0uy",
  2914. "actions": [
  2915. {
  2916. "clickTrackingParams": "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
  2917. "replaceEnclosingAction": {
  2918. "item": {
  2919. "notificationMultiActionRenderer": {
  2920. "responseText": {
  2921. "runs": [
  2922. {
  2923. "text": "Video removed"
  2924. }
  2925. ]
  2926. },
  2927. "buttons": [
  2928. {
  2929. "buttonRenderer": {
  2930. "style": "STYLE_BLUE_TEXT",
  2931. "text": {
  2932. "runs": [
  2933. {
  2934. "text": "Undo"
  2935. }
  2936. ]
  2937. },
  2938. "serviceEndpoint": {
  2939. "clickTrackingParams": "CO0CEPBbGAAiEwico6-FjdCFAxUZwJcIHTgdBHo=",
  2940. "commandMetadata": {
  2941. "webCommandMetadata": {
  2942. "sendPost": true,
  2943. "apiUrl": "/youtubei/v1/feedback"
  2944. }
  2945. },
  2946. "undoFeedbackEndpoint": {
  2947. "undoToken": "AB9zfpK74nsMbZ4OfNgKTgA9g0w3Q8o72jdm384D3y82OAuy2KgvTUOAn-iII915ZC_7aqAxTK-XNir21X_T3WQEeAzdy4hCZ6o0f12hfdHW8xI1js1WB_CEn3EW27P9_1vu5dw2kDeW",
  2948. "actions": [
  2949. {
  2950. "clickTrackingParams": "CO0CEPBbGAAiEwico6-FjdCFAxUZwJcIHTgdBHo=",
  2951. "undoFeedbackAction": {
  2952. "hack": true
  2953. }
  2954. }
  2955. ]
  2956. }
  2957. },
  2958. "trackingParams": "CO0CEPBbGAAiEwico6-FjdCFAxUZwJcIHTgdBHo="
  2959. }
  2960. }
  2961. ],
  2962. "trackingParams": "COwCEKW8ASITCJyjr4WN0IUDFRnAlwgdOB0Eeg=="
  2963. }
  2964. }
  2965. }
  2966. }
  2967. ]
  2968. }
  2969. },
  2970. "trackingParams": "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
  2971. "accessibility": {
  2972. "accessibilityData": {
  2973. "label": "Not interested"
  2974. }
  2975. }
  2976. }
  2977. },
  2978. {
  2979. "menuNavigationItemRenderer": {
  2980. "text": {
  2981. "runs": [
  2982. {
  2983. "text": "Send feedback"
  2984. }
  2985. ]
  2986. },
  2987. "icon": {
  2988. "iconType": "FEEDBACK"
  2989. },
  2990. "navigationEndpoint": {
  2991. "clickTrackingParams": "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
  2992. "commandMetadata": {
  2993. "webCommandMetadata": {
  2994. "ignoreNavigation": true
  2995. }
  2996. },
  2997. "userFeedbackEndpoint": {
  2998. "additionalDatas": [
  2999. {
  3000. "userFeedbackEndpointProductSpecificValueData": {
  3001. "key": "video_id",
  3002. "value": id
  3003. }
  3004. },
  3005. {
  3006. "userFeedbackEndpointProductSpecificValueData": {
  3007. "key": "lockup",
  3008. "value": "shelf"
  3009. }
  3010. }
  3011. ]
  3012. }
  3013. },
  3014. "trackingParams": "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
  3015. "accessibility": {
  3016. "accessibilityData": {
  3017. "label": "Send feedback"
  3018. }
  3019. }
  3020. }
  3021. }
  3022. ],
  3023. "trackingParams": "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
  3024. "accessibility": {
  3025. "accessibilityData": {
  3026. "label": "More actions"
  3027. }
  3028. }
  3029. }
  3030. },
  3031. "trackingParams": "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6QIazp8Dzs9CrKA==",
  3032. "accessibility": {
  3033. "accessibilityData": {
  3034. "label": title + " - play Short"
  3035. }
  3036. },
  3037. "style": "REEL_ITEM_STYLE_AVATAR_CIRCLE",
  3038. "dismissalInfo": {
  3039. "feedbackToken": "AB9zfpLIJd1aRU9JzdOjpgeJBW2QvHH79sx6dM6ZCDEzyc5qrISZBSpNRe5lerckNHwQ10BOwEQhlquLlHP-nkuA4VSSCXX0XgMJHBnKWBxlIXkQ1pLIUjd6cQKhrCUioDfix7xn5Ecj"
  3040. },
  3041. "videoType": "REEL_VIDEO_TYPE_VIDEO",
  3042. "loggingDirectives": {
  3043. "trackingParams": "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
  3044. "visibility": {
  3045. "types": "12"
  3046. },
  3047. "enableDisplayloggerExperiment": true
  3048. }
  3049. }
  3050. };
  3051. }
  3052. if (page_type == "yt_home") {
  3053. tmp_item = {
  3054. "richItemRenderer": {
  3055. "content": tmp_item,
  3056. "trackingParams": "CJsFEJmNBRgAIhMI4YD80e7PhQMVZ2pMCB18XgT5"
  3057. }
  3058. };
  3059. }
  3060. if (["mobile_yt_home", "mobile_yt_watch"].includes(page_type)) {
  3061. tmp_item = {
  3062. "shortsLockupViewModel": {
  3063. "entityId": "shorts-shelf-item-" + id,
  3064. "accessibilityText": title + ", " + views_lable + " - play Short",
  3065. "thumbnail": {
  3066. "sources": [
  3067. {
  3068. "url": thumbnail_url,
  3069. "width": 405,
  3070. "height": 720
  3071. }
  3072. ]
  3073. },
  3074. "onTap": {
  3075. "innertubeCommand": {
  3076. "clickTrackingParams": "CK8BEIf2BBgAIhMIqeqAyo7QhQMVz3lMCB2mCA0JWg9GRXdoYXRfdG9fd2F0Y2iaAQUIJBCOHg==",
  3077. "commandMetadata": {
  3078. "webCommandMetadata": {
  3079. "url": "/shorts/" + id,
  3080. "webPageType": "WEB_PAGE_TYPE_SHORTS",
  3081. "rootVe": 37414
  3082. }
  3083. },
  3084. "reelWatchEndpoint": {
  3085. "videoId": id,
  3086. "playerParams": "8AEBoAMByAMkuAQFogYVAdXZ-jveUoR0s0_R7sLGUd85_xAk",
  3087. "thumbnail": {
  3088. "thumbnails": [
  3089. {
  3090. "url": "https://i.ytimg.com/vi/" + id + "/frame0.jpg",
  3091. "width": 1080,
  3092. "height": 1920
  3093. }
  3094. ],
  3095. "isOriginalAspectRatio": true
  3096. },
  3097. "overlay": {
  3098. "reelPlayerOverlayRenderer": {
  3099. "style": "REEL_PLAYER_OVERLAY_STYLE_SHORTS",
  3100. "trackingParams": "CLIBELC1BCITCKnqgMqO0IUDFc95TAgdpggNCQ==",
  3101. "reelPlayerNavigationModel": "REEL_PLAYER_NAVIGATION_MODEL_UNSPECIFIED"
  3102. }
  3103. },
  3104. "params": "CAUwAg%3D%3D",
  3105. "sequenceProvider": "REEL_WATCH_SEQUENCE_PROVIDER_RPC",
  3106. "sequenceParams": "CgtwblVoZV9PUTE2byoCGAVQGWgA",
  3107. "loggingContext": {
  3108. "vssLoggingContext": {
  3109. "serializedContextData": "CgIIDA%3D%3D"
  3110. },
  3111. "qoeLoggingContext": {
  3112. "serializedContextData": "CgIIDA%3D%3D"
  3113. }
  3114. },
  3115. "ustreamerConfig": "CAwSHDFIakVXUytucVRyTENNWlgzMXdDZmYwamZQQ0U="
  3116. }
  3117. }
  3118. },
  3119. "inlinePlayerData": {
  3120. "onVisible": {
  3121. "innertubeCommand": {
  3122. "clickTrackingParams": "CK8BEIf2BBgAIhMIqeqAyo7QhQMVz3lMCB2mCA0JMgZnLWhpZ2haD0ZFd2hhdF90b193YXRjaJoBBQgkEI4e",
  3123. "commandMetadata": {
  3124. "webCommandMetadata": {
  3125. "url": "/watch?v=" + id + "&pp=YAHIAQG6AwIYAugFAQ%3D%3D",
  3126. "webPageType": "WEB_PAGE_TYPE_WATCH",
  3127. "rootVe": 3832
  3128. }
  3129. },
  3130. "watchEndpoint": {
  3131. "videoId": id,
  3132. "playerParams": "YAHIAQG6AwIYAugFAQ%3D%3D"
  3133. }
  3134. }
  3135. }
  3136. },
  3137. "menuOnTap": {
  3138. "innertubeCommand": {
  3139. "clickTrackingParams": "CK8BEIf2BBgAIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
  3140. "showSheetCommand": {
  3141. "panelLoadingStrategy": {
  3142. "inlineContent": {
  3143. "sheetViewModel": {
  3144. "content": {
  3145. "listViewModel": {
  3146. "listItems": [
  3147. {
  3148. "listItemViewModel": {
  3149. "title": {
  3150. "content": "Not interested"
  3151. },
  3152. "leadingImage": {
  3153. "sources": [
  3154. {
  3155. "clientResource": {
  3156. "imageName": "NOT_INTERESTED"
  3157. }
  3158. }
  3159. ]
  3160. },
  3161. "rendererContext": {
  3162. "commandContext": {
  3163. "onTap": {
  3164. "innertubeCommand": {
  3165. "clickTrackingParams": "CK8BEIf2BBgAIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
  3166. "commandMetadata": {
  3167. "webCommandMetadata": {
  3168. "sendPost": true,
  3169. "apiUrl": "/youtubei/v1/feedback"
  3170. }
  3171. },
  3172. "feedbackEndpoint": {
  3173. "feedbackToken": "AB9zfpJnMNgSEnsvYAu4UXP6IN5z0VfAt-OZOs8ypsKND9Mv5RhoELjmgb_vxVOvvYoiM2f8q9QFcdGMOEOCSk7LPYMnGshEHKcis4oeot-Z5OsgYpmOP3DbMXgFHUgQhOUAjL-FIj5y",
  3174. "actions": [
  3175. {
  3176. "clickTrackingParams": "CK8BEIf2BBgAIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
  3177. "replaceEnclosingAction": {
  3178. "item": {
  3179. "notificationMultiActionRenderer": {
  3180. "responseText": {
  3181. "runs": [
  3182. {
  3183. "text": "Video removed"
  3184. }
  3185. ]
  3186. },
  3187. "buttons": [
  3188. {
  3189. "buttonRenderer": {
  3190. "style": "STYLE_BLUE_TEXT",
  3191. "text": {
  3192. "runs": [
  3193. {
  3194. "text": "Undo"
  3195. }
  3196. ]
  3197. },
  3198. "serviceEndpoint": {
  3199. "clickTrackingParams": "CLEBEPBbGAAiEwip6oDKjtCFAxXPeUwIHaYIDQk=",
  3200. "commandMetadata": {
  3201. "webCommandMetadata": {
  3202. "sendPost": true,
  3203. "apiUrl": "/youtubei/v1/feedback"
  3204. }
  3205. },
  3206. "undoFeedbackEndpoint": {
  3207. "undoToken": "AB9zfpI_UgAQH8eSODf7gCfkDtllqeFC5Qr38N7cNnlz8NmYZ78F2KiuX3KZNcumX2jfVXRzNfd2M0V7vud8UdS2Hz7SshgqVTn2TOJApWBlkIPTbUYWuQkX2CSbVKZw1p3wIHkjQOH7",
  3208. "actions": [
  3209. {
  3210. "clickTrackingParams": "CLEBEPBbGAAiEwip6oDKjtCFAxXPeUwIHaYIDQk=",
  3211. "undoFeedbackAction": {
  3212. "hack": true
  3213. }
  3214. }
  3215. ]
  3216. }
  3217. },
  3218. "trackingParams": "CLEBEPBbGAAiEwip6oDKjtCFAxXPeUwIHaYIDQk="
  3219. }
  3220. }
  3221. ],
  3222. "trackingParams": "CLABEKW8ASITCKnqgMqO0IUDFc95TAgdpggNCQ=="
  3223. }
  3224. }
  3225. }
  3226. }
  3227. ]
  3228. }
  3229. }
  3230. }
  3231. }
  3232. }
  3233. }
  3234. },
  3235. {
  3236. "listItemViewModel": {
  3237. "title": {
  3238. "content": "Send feedback"
  3239. },
  3240. "leadingImage": {
  3241. "sources": [
  3242. {
  3243. "clientResource": {
  3244. "imageName": "FEEDBACK"
  3245. }
  3246. }
  3247. ]
  3248. },
  3249. "rendererContext": {
  3250. "commandContext": {
  3251. "onTap": {
  3252. "innertubeCommand": {
  3253. "clickTrackingParams": "CK8BEIf2BBgAIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
  3254. "commandMetadata": {
  3255. "webCommandMetadata": {
  3256. "ignoreNavigation": true
  3257. }
  3258. },
  3259. "userFeedbackEndpoint": {
  3260. "additionalDatas": [
  3261. {
  3262. "userFeedbackEndpointProductSpecificValueData": {
  3263. "key": "video_id",
  3264. "value": id
  3265. }
  3266. },
  3267. {
  3268. "userFeedbackEndpointProductSpecificValueData": {
  3269. "key": "lockup",
  3270. "value": "shelf"
  3271. }
  3272. }
  3273. ]
  3274. }
  3275. }
  3276. }
  3277. }
  3278. }
  3279. }
  3280. }
  3281. ]
  3282. }
  3283. }
  3284. }
  3285. }
  3286. }
  3287. }
  3288. }
  3289. },
  3290. "indexInCollection": 0,
  3291. "menuOnTapA11yLabel": "More actions",
  3292. "overlayMetadata": {
  3293. "primaryText": {
  3294. "content": title,
  3295. "styleRuns": [
  3296. {
  3297. "startIndex": 0,
  3298. "fontName": "",
  3299. "fontSize": 0,
  3300. "fontColor": 4294967295
  3301. }
  3302. ]
  3303. },
  3304. "secondaryText": {
  3305. "content": views_lable,
  3306. "styleRuns": [
  3307. {
  3308. "startIndex": 0,
  3309. "fontName": "",
  3310. "fontSize": 0,
  3311. "fontColor": 4294967295
  3312. }
  3313. ]
  3314. }
  3315. },
  3316. "loggingDirectives": {
  3317. "trackingParams": "CK8BEIf2BBgAIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
  3318. "visibility": {
  3319. "types": "12"
  3320. },
  3321. "enableDisplayloggerExperiment": true
  3322. }
  3323. }
  3324. };
  3325. }
  3326. items.push(tmp_item);
  3327. }
  3328. if (item_path) {
  3329. eval(trustedScript(item_path + ' = items'));
  3330. user_data_api.set();
  3331. return root;
  3332. }
  3333. return {};
  3334. }
  3335. get_shorts_info(video_id) {
  3336. return new Promise((resolve, reject) => {
  3337. let basic_url, author_id_reg, author_name_reg, upload_date_reg, ago_reg;
  3338. if (page_type.startsWith('mobile')) {
  3339. basic_url = 'https://m.youtube.com/shorts/';
  3340. author_id_reg = /"channelId":"(.*?)"/;
  3341. author_name_reg = /"ownerChannelName":"(.*?)"/;
  3342. // upload_date_reg = /"uploadDate":"(.*?)"/;
  3343. ago_reg = /timestampText.*?:\\x22(.*?)\\x22\\x7d/;
  3344.  
  3345. } else {
  3346. basic_url = 'https://www.youtube.com/shorts/';
  3347. author_id_reg = /"browseId":"([a-zA-Z0-9\-_]+)","canonicalBaseUrl"/;
  3348. author_name_reg = /"channel":\{"simpleText":"(.*?)"/;
  3349. // upload_date_reg = /"uploadDate":"(.*?)"/;
  3350. ago_reg = /"timestampText":{"simpleText":"(.*?)"}/;
  3351. }
  3352. const url = basic_url + video_id;
  3353. const xhr = new XMLHttpRequest();
  3354. xhr.open('GET', url);
  3355. xhr.setRequestHeader('accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7');
  3356. let author_id = '';
  3357. let author_name = '';
  3358. // let upload_date_str = '';
  3359. let ago_str = '';
  3360. // let upload_date;
  3361. xhr.onload = function () {
  3362. if (xhr.status === 200) {
  3363. let match;
  3364. const result = xhr.responseText;
  3365. match = result.match(author_id_reg);
  3366. if (match && match.length > 1) author_id = match[1];
  3367. match = result.match(author_name_reg);
  3368. if (match && match.length > 1) author_name = match[1];
  3369. match = result.match(ago_reg);
  3370. if (match && match.length > 1) ago_str = match[1];
  3371. resolve({
  3372. id: video_id,
  3373. author_id: author_id,
  3374. author_name: author_name,
  3375. ago_str: ago_str
  3376. });
  3377. } else {
  3378. reject(xhr.responseText);
  3379. }
  3380. };
  3381. xhr.onerror = function () {
  3382. reject(new Error('XHR request failed'));
  3383. };
  3384. xhr.send();
  3385. });
  3386. }
  3387. parse_shorts_list() {
  3388. if (!this.shorts_list.length) return;
  3389. const { id, title, views_lable, thumbnail_url } = this.shorts_list.pop();
  3390. this.get_shorts_info(id).then((author_info) => {
  3391. const { author_id, author_name, ago_str } = author_info;
  3392. if (author_id && user_data.channel_infos.ids.includes(author_id)) {
  3393. if (user_data.shorts_list.some((value) => { return value.id === id; })) {
  3394. log('已存在' + author_name + '的短视频:' + title, 'shorts');
  3395. } else {
  3396. log('不过滤' + author_name + '的短视频:' + title, 'shorts');
  3397. const shorts_info = {
  3398. id: id,
  3399. title: title,
  3400. author_id: author_id,
  3401. author_name: author_name,
  3402. views_lable: views_lable,
  3403. from: page_type,
  3404. thumbnail_url: thumbnail_url,
  3405. ago_str: ago_str,
  3406. };
  3407. user_data.shorts_list.push(shorts_info);
  3408. user_data_api.set();
  3409. }
  3410. } else {
  3411. log('过滤' + author_name + '的短视频:' + title, 'shorts');
  3412. }
  3413. }
  3414. ).finally(() => {
  3415. if (this.shorts_list.length > 0)
  3416. setTimeout(() => { this.parse_shorts_list(); }, shorts_parse_delay);
  3417. else
  3418. this.parsing = false;
  3419. });
  3420. }
  3421. check_shorts_exist() {
  3422. const short_id = href.split('/').pop();
  3423. for (let i = 0; i < user_data.shorts_list.length; i++) {
  3424. if (user_data.shorts_list[i].id === short_id) {
  3425. user_data.shorts_list.splice(i, 1);
  3426. user_data_api.set();
  3427. return;
  3428. }
  3429. }
  3430. }
  3431. get_interval_tag(upload_date_str) {
  3432. if (!upload_date_str) return '';
  3433. const uploadDate = new Date(upload_date_str);
  3434. const currentDate = new Date();
  3435. const timeDifference = Math.abs(currentDate - uploadDate); // Difference in milliseconds
  3436. const secondsDifference = timeDifference / 1000;
  3437. const minutesDifference = secondsDifference / 60;
  3438. const hoursDifference = minutesDifference / 60;
  3439. const daysDifference = hoursDifference / 24;
  3440. const weeksDifference = daysDifference / 7;
  3441. const monthsDifference = weeksDifference / 4.345; // Average number of weeks in a month
  3442. const yearsDifference = monthsDifference / 12;
  3443. if (secondsDifference < 60) {
  3444. return `${Math.floor(secondsDifference)} seconds ago`;
  3445. } else if (minutesDifference < 60) {
  3446. return `${Math.floor(minutesDifference)} minutes ago`;
  3447. } else if (hoursDifference < 24) {
  3448. return `${Math.floor(hoursDifference)} hours ago`;
  3449. } else if (daysDifference < 7) {
  3450. return `${Math.floor(daysDifference)} days ago`;
  3451. } else if (weeksDifference < 4.345) {
  3452. return `${Math.floor(weeksDifference)} weeks ago`;
  3453. } else if (monthsDifference < 12) {
  3454. return `${Math.floor(monthsDifference)} months ago`;
  3455. } else {
  3456. return `${Math.floor(yearsDifference)} years ago`;
  3457. }
  3458. }
  3459. };
  3460. return new ShortsFun();
  3461. }
  3462.  
  3463. function get_yt_api() {
  3464. return {
  3465. get_subscribe_data: function (retry = 0) {
  3466. const headers = {
  3467. "authority": "www.youtube.com",
  3468. "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
  3469. };
  3470. const url = "https://www.youtube.com/feed/channels";
  3471. const requestConfig = {
  3472. method: 'GET',
  3473. headers: headers,
  3474. url: url
  3475. };
  3476. const save_this = this;
  3477. GM_xmlhttpRequest({
  3478. ...requestConfig,
  3479. onload: function (response) {
  3480. const tmp_channel_names = [];
  3481. const tmp_channel_ids = [];
  3482. const regex = /var ytInitialData \= (.*?);\<\/script\>/;
  3483. try {
  3484. const match = response.responseText.match(regex);
  3485. const ytInitialData_obj = JSON.parse(match[1]);
  3486. const items = ytInitialData_obj.contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0].shelfRenderer.content.expandedShelfContentsRenderer.items;
  3487. for (let item of items) {
  3488. const channel_name = item.channelRenderer.title.simpleText;
  3489. const match_channel_id = item.channelRenderer.channelId;
  3490. tmp_channel_ids.push(match_channel_id);
  3491. tmp_channel_names.push(channel_name);
  3492. }
  3493. if (tmp_channel_ids.length > 0) {
  3494. user_data.channel_infos.ids = tmp_channel_ids;
  3495. user_data.channel_infos.names = tmp_channel_names;
  3496. user_data_api.set();
  3497. }
  3498. log('获取关注列表成功' + user_data.channel_infos.ids.length + '个', 0);
  3499. } catch (error) {
  3500. if (retry < 3) {
  3501. setTimeout(() => { save_this.get_subscribe_data(retry + 1); }, 1000);
  3502. }
  3503. log('获取关注列表失败\n', error, -1);
  3504. }
  3505. },
  3506. onerror: function (error) {
  3507. if (retry < 3) {
  3508. setTimeout(() => { save_this.get_subscribe_data(retry + 1); }, 1000);
  3509. }
  3510. log('获取关注列表失败\n', error, -1);
  3511. },
  3512. });
  3513. },
  3514. get_authorization: function () {
  3515. function Vja() {
  3516. function a() {
  3517. e[0] = 1732584193;
  3518. e[1] = 4023233417;
  3519. e[2] = 2562383102;
  3520. e[3] = 271733878;
  3521. e[4] = 3285377520;
  3522. u = q = 0;
  3523. }
  3524. function b(x) {
  3525. for (var y = l, C = 0; 64 > C; C += 4)
  3526. y[C / 4] = x[C] << 24 | x[C + 1] << 16 | x[C + 2] << 8 | x[C + 3];
  3527. for (C = 16; 80 > C; C++)
  3528. x = y[C - 3] ^ y[C - 8] ^ y[C - 14] ^ y[C - 16],
  3529. y[C] = (x << 1 | x >>> 31) & 4294967295;
  3530. x = e[0];
  3531. var E = e[1]
  3532. , H = e[2]
  3533. , R = e[3]
  3534. , T = e[4];
  3535. for (C = 0; 80 > C; C++) {
  3536. if (40 > C) {
  3537. if (20 > C) {
  3538. var X = R ^ E & (H ^ R);
  3539. var la = 1518500249;
  3540. } else
  3541. X = E ^ H ^ R,
  3542. la = 1859775393;
  3543. } else
  3544. 60 > C ? (X = E & H | R & (E | H),
  3545. la = 2400959708) : (X = E ^ H ^ R,
  3546. la = 3395469782);
  3547. X = ((x << 5 | x >>> 27) & 4294967295) + X + T + la + y[C] & 4294967295;
  3548. T = R;
  3549. R = H;
  3550. H = (E << 30 | E >>> 2) & 4294967295;
  3551. E = x;
  3552. x = X;
  3553. }
  3554. e[0] = e[0] + x & 4294967295;
  3555. e[1] = e[1] + E & 4294967295;
  3556. e[2] = e[2] + H & 4294967295;
  3557. e[3] = e[3] + R & 4294967295;
  3558. e[4] = e[4] + T & 4294967295;
  3559. }
  3560. function c(x, y) {
  3561. if ("string" === typeof x) {
  3562. x = unescape(encodeURIComponent(x));
  3563. for (var C = [], E = 0, H = x.length; E < H; ++E)
  3564. C.push(x.charCodeAt(E));
  3565. x = C;
  3566. }
  3567. y || (y = x.length);
  3568. C = 0;
  3569. if (0 == q)
  3570. for (; C + 64 < y;)
  3571. b(x.slice(C, C + 64)),
  3572. C += 64,
  3573. u += 64;
  3574. for (; C < y;)
  3575. if (h[q++] = x[C++],
  3576. u++,
  3577. 64 == q)
  3578. for (q = 0,
  3579. b(h); C + 64 < y;)
  3580. b(x.slice(C, C + 64)),
  3581. C += 64,
  3582. u += 64;
  3583. }
  3584. function d() {
  3585. var x = []
  3586. , y = 8 * u;
  3587. 56 > q ? c(m, 56 - q) : c(m, 64 - (q - 56));
  3588. for (var C = 63; 56 <= C; C--)
  3589. h[C] = y & 255,
  3590. y >>>= 8;
  3591. b(h);
  3592. for (C = y = 0; 5 > C; C++)
  3593. for (var E = 24; 0 <= E; E -= 8)
  3594. x[y++] = e[C] >> E & 255;
  3595. return x;
  3596. }
  3597. for (var e = [], h = [], l = [], m = [128], p = 1; 64 > p; ++p)
  3598. m[p] = 0;
  3599. var q, u;
  3600. a();
  3601. return {
  3602. reset: a,
  3603. update: c,
  3604. digest: d,
  3605. digestString: function () {
  3606. for (var x = d(), y = "", C = 0; C < x.length; C++)
  3607. y += "0123456789ABCDEF".charAt(Math.floor(x[C] / 16)) + "0123456789ABCDEF".charAt(x[C] % 16);
  3608. return y;
  3609. }
  3610. };
  3611. }
  3612. const sapisid_cookie = getCookie('SAPISID') || getCookie('APISID') || getCookie('__Secure-3PAPISID');
  3613. if (sapisid_cookie) {
  3614. const timestamp = Math.floor(Date.now() / 1000);
  3615. const b = Vja();
  3616. b.update(timestamp + ' ' + sapisid_cookie + ' https://www.youtube.com');
  3617. const hash_value = b.digestString().toLowerCase();
  3618. return 'SAPISIDHASH ' + timestamp + '_' + hash_value;
  3619. }
  3620. return '';
  3621. },
  3622. get_channel_id: function (retry = 0) {
  3623. const authorization = this.get_authorization();
  3624. if (!authorization) {
  3625. log('获取authorization失败', 0);
  3626. return;
  3627. }
  3628. const url = "https://www.youtube.com/youtubei/v1/account/account_menu";
  3629. const params = {
  3630. "prettyPrint": "false"
  3631. };
  3632. const data = {
  3633. "context": {
  3634. "client": {
  3635. "clientName": "WEB",
  3636. "clientVersion": "2.20240308.00.00",
  3637. },
  3638. },
  3639. };
  3640. const jsonData = JSON.stringify(data);
  3641. const headers = {
  3642. "authorization": authorization,
  3643. "content-type": "application/json",
  3644. "origin": "https://www.youtube.com",
  3645. "referer": "https0://www.youtube.com/",
  3646. };
  3647. const requestConfig = {
  3648. method: 'POST',
  3649. headers: headers,
  3650. data: jsonData,
  3651. url: url + "?" + new URLSearchParams(params),
  3652. };
  3653.  
  3654. GM_xmlhttpRequest({
  3655. ...requestConfig,
  3656. onload: function (response) {
  3657. const match = response.responseText.match(/"browseId"\:"(.*?)"/);
  3658. if (match && match.length > 1) {
  3659. const tmp_id = match[1];
  3660. if (tmp_id && tmp_id != channel_id) {
  3661. channel_id = tmp_id;
  3662. user_data = user_data_api.get();
  3663. GM_setValue('last_channel_id', channel_id);
  3664. }
  3665. log('获取channel_id成功' + channel_id, 0);
  3666. } else {
  3667. if (retry < 3) {
  3668. setTimeout(() => { yt_api.get_channel_id(retry + 1); }, 500);
  3669. } else {
  3670. log('获取channel_id失败', response, response.responseText, -1);
  3671. }
  3672. }
  3673. },
  3674. onerror: function (error) {
  3675. if (retry < 3) {
  3676. setTimeout(() => { yt_api.get_channel_id(retry + 1); }, 500);
  3677. yt_api.get_channel_id(retry + 1);
  3678. } else {
  3679. log('获取channel_id失败', error, 0);
  3680. }
  3681. },
  3682. });
  3683. }
  3684. };
  3685. }
  3686.  
  3687. function get_user_data_api() {
  3688. return {
  3689. get() {
  3690. const default_user_data = {
  3691. "open_recommend_shorts": 'on',
  3692. "open_recommend_movie": 'on',
  3693. "open_recommend_popular": 'on',
  3694. "open_recommend_liveroom": 'on',
  3695. "open_recommend_playables": "on",
  3696. "add_shorts_upload_date": 'on',
  3697. "shorts_change_author_name": 'on',
  3698. "short_buy_super_thanks": 'on',
  3699. "shorts_auto_scroll": 'off',
  3700. "shorts_add_video_progress": 'off',
  3701. "shorts_dbclick_like": 'off',
  3702. "shorts_disable_loop_play": 'off',
  3703. "dbclick_download_video": 'off',
  3704. "language": 'zh-CN',
  3705. "channel_infos": {
  3706. "ids": [],
  3707. "names": []
  3708. },
  3709. "shorts_list": [],
  3710. "watch_page_config": {
  3711. "shop_banner": "on"
  3712. },
  3713. "login": false,
  3714. };
  3715. let diff = false;
  3716. user_data_listener.set();
  3717. let tmp_user_data = GM_getValue(channel_id);
  3718. if (!tmp_user_data) {
  3719. tmp_user_data = default_user_data;
  3720. diff = true;
  3721. }
  3722. for (let key in default_user_data) {
  3723. if (!(key in tmp_user_data)) {
  3724. diff = true;
  3725. tmp_user_data[key] = default_user_data[key];
  3726. }
  3727. }
  3728. const tmp_login = channel_id !== 'default';
  3729. if (tmp_user_data.login !== tmp_login) {
  3730. diff = true;
  3731. tmp_user_data.login = tmp_login;
  3732. }
  3733. (diff || this.update(tmp_user_data)) && GM_setValue(channel_id, tmp_user_data);
  3734. return tmp_user_data;
  3735. },
  3736. set() {
  3737. return GM_setValue(channel_id, user_data);
  3738. },
  3739. reset() {
  3740. if (!confirm(flag_info.del_config_confirm_tips)) return;
  3741. const keys = GM_listValues();
  3742. for (let key of keys) {
  3743. GM_deleteValue(key);
  3744. }
  3745. unsafeWindow.document.location.reload();
  3746. },
  3747. update(tmp_user_data) {
  3748. let diff = false;
  3749. const last_version = GM_getValue('last_version', -1);
  3750. if (last_version === -1 && !tmp_user_data.open_recommend_shorts) {
  3751. tmp_user_data.open_recommend_shorts = GM_getValue("open_recommend_shorts", "on");
  3752. tmp_user_data.open_recommend_movie = GM_getValue("open_recommend_movie", "on");
  3753. tmp_user_data.open_recommend_popular = GM_getValue("open_recommend_popular", "on");
  3754. tmp_user_data.open_recommend_liveroom = GM_getValue("open_recommend_liveroom", "on");
  3755. diff = true;
  3756. }
  3757. if (typeof (tmp_user_data.open_recommend_shorts) === 'boolean') {
  3758. tmp_user_data.open_recommend_shorts = tmp_user_data.open_recommend_shorts ? 'on' : 'off';
  3759. tmp_user_data.open_recommend_movie = tmp_user_data.open_recommend_movie ? 'on' : 'off';
  3760. tmp_user_data.open_recommend_popular = tmp_user_data.open_recommend_popular ? 'on' : 'off';
  3761. tmp_user_data.open_recommend_liveroom = tmp_user_data.open_recommend_liveroom ? 'on' : 'off';
  3762. diff = true;
  3763. }
  3764. last_version !== GM_info.script.version && GM_setValue("last_version", GM_info.script.version);
  3765. return diff;
  3766. }
  3767. };
  3768. }
  3769.  
  3770. function get_data_process() {
  3771. class DATA_PROCESS {
  3772. constructor() {
  3773. this.limit_eval = false;
  3774. this.obj_filter;
  3775. this.obj_storage = {};
  3776. }
  3777. condition_split_and_tag = '&&';
  3778. condition_split_or_tag = '||';
  3779. value_split_and_tag = '&';
  3780. value_split_or_tag = '|';
  3781.  
  3782. storage_obj(key, obj) {
  3783. this.obj_storage[key] = obj;
  3784. }
  3785.  
  3786. set_obj_filter(obj_filter) {
  3787. if (typeof obj_filter !== 'function') return;
  3788. this.obj_filter = function () {
  3789. try {
  3790. obj_filter.apply(this, arguments);
  3791. } catch (error) {
  3792. log('obj_filter error', error, -1);
  3793. return false;
  3794. }
  3795. };
  3796. };
  3797.  
  3798. text_process(data, values, mode, traverse_all) {
  3799. if (!values) return data;
  3800. const origin_data = data;
  3801. try {
  3802. mode = mode || 'cover';
  3803. if (mode === 'reg') {
  3804. for (let value of values) {
  3805. const patten_express = value.split(SPLIT_TAG)[0];
  3806. const replace_value = value.split(SPLIT_TAG)[1];
  3807. const patten = new RegExp(patten_express, "g");
  3808. data = data.replace(patten, replace_value);
  3809. }
  3810. }
  3811. if (mode === 'cover') {
  3812. data = values[0];
  3813. }
  3814. if (mode === 'insert') {
  3815. traverse_all = traverse_all || false;
  3816. let json_data;
  3817. try {
  3818. json_data = JSON.parse(data);
  3819. } catch (error) {
  3820. log('text_process JSON parse error', -1);
  3821. return data;
  3822. }
  3823. this.obj_process(json_data, values, traverse_all);
  3824. data = JSON.stringify(json_data);
  3825. }
  3826. } catch (error) {
  3827. log('text_process error', error, -1);
  3828. data = origin_data;
  3829. }
  3830. return data;
  3831. }
  3832.  
  3833. get_relative_path(basic_path, relative_path) {
  3834. if (relative_path === '/') return basic_path;
  3835. let real_path;
  3836. if (relative_path.startsWith('/.')) {
  3837. real_path = basic_path + relative_path.slice(1);
  3838. }
  3839. if (relative_path.startsWith('.')) {
  3840. const reg = /[\.\[]/g;
  3841. const positions = [];
  3842. let match;
  3843. while ((match = reg.exec(basic_path)) !== null) {
  3844. positions.push(match.index);
  3845. }
  3846. if (positions.length === 0) {
  3847. return basic_path;
  3848. }
  3849. const pointer_match = relative_path.match(/^\.+/);
  3850. const split_index = positions[positions.length - pointer_match[0].length];
  3851. const relative_attribute = relative_path.slice(pointer_match[0].length);
  3852. real_path = basic_path.slice(0, split_index) + (relative_attribute ? ((relative_attribute.startsWith('[') ? '' : '.') + relative_attribute) : '');
  3853. }
  3854. return this.convertPathToBracketNotation(real_path);
  3855. }
  3856.  
  3857. value_parse(parse_value, path_info = null, json_obj = null) {
  3858. const formula_match = parse_value.match(/\{.*?\}/g);
  3859. if (formula_match) {
  3860. for (let express_ of formula_match) {
  3861. const express = express_.slice(1, -1);
  3862. if (!express) continue;
  3863. parse_value = parse_value.replace(express_, this.value_parse(express, path_info, json_obj));
  3864. }
  3865. }
  3866. const json_math = parse_value.match(/^json\((.*)\)$/);
  3867. if (json_math) return JSON.parse(json_math[1]);
  3868. const obj_match = parse_value.match(/^obj\((.*)\)$/);
  3869. if (obj_match) return this.string_to_value(unsafeWindow, obj_match[1]);
  3870. const storage_obj_match = parse_value.match(/^sobj\((.*)\)$/);
  3871. if (storage_obj_match) return this.string_to_value(this.obj_storage, storage_obj_match[1]);
  3872. const number_match = parse_value.match(/^num\((.*)\)$/);
  3873. if (number_match) return Number(number_match[1]);
  3874. const method_match = parse_value.match(/^method\((.*)\)$/);
  3875. if (method_match) {
  3876. // eval 限制的时候可以使用num() obj()这些添加数字对象 方法也要放到unsafeWindow里 例:method(b("123",num(23)))
  3877. // 不限制的时候 不能使用num和obj 方法不需要放到unsafeWindow里 例:method(b("123",23))
  3878. if (this.limit_eval) {
  3879. const method_info = method_match[1].match(/(.*?)\((.*)\)$/);
  3880. const method_name = method_info[1];
  3881. const method_args_string = method_info[2];
  3882. const method_args = method_args_string.split(',');
  3883. const args = [];
  3884. for (let arg of method_args) {
  3885. args.push(this.value_parse(arg, path_info, json_obj));
  3886. }
  3887. return unsafeWindow[method_name](...args);
  3888. }
  3889. return eval(trustedScript(method_match[1]));
  3890. }
  3891. const deal_obj_match = parse_value.match(/^dealObj\((.*)\)$/);
  3892. if (deal_obj_match) {
  3893. const path_msg = deal_obj_match[1];
  3894. return this.string_to_value(json_obj.this.get_relative_path(path_info.deal_path, path_msg));
  3895. }
  3896. const path_obj_match = parse_value.match(/^pathObj\((.*)\)$/);
  3897. if (path_obj_match) {
  3898. const path_msg = path_obj_match[1];
  3899. return this.string_to_value(json_obj, this.get_relative_path(path_info.path, path_msg));
  3900. }
  3901. const abs_obj_match = parse_value.match(/^absObj\((.*)\)$/);
  3902. if (abs_obj_match) {
  3903. const abs_path = abs_obj_match[1];
  3904. return this.string_to_value(json_obj, abs_path);
  3905. }
  3906. const string_match = parse_value.match(/^["'](.*)["']$/);
  3907. if (string_match) return string_match[1];
  3908. if (parse_value === 'undefined') return undefined;
  3909. if (parse_value === 'null') return null;
  3910. return parse_value;
  3911. }
  3912.  
  3913. string_to_value(obj, path) {
  3914. try {
  3915. if (!this.limit_eval) {
  3916. return eval(trustedScript(path.replace('json_obj', 'obj')));
  3917. }
  3918. let tmp_obj = obj;
  3919. let matches = path.match(/\[(.*?)\]/g);
  3920. if (matches) {
  3921. matches.map((match) => {
  3922. if (match.includes('["')) {
  3923. tmp_obj = Reflect.get(tmp_obj, match.replace(/\["|"\]/g, ''));
  3924. } else {
  3925. tmp_obj = Reflect.get(tmp_obj, Number(match.replace(/\[|\]/g, '')));
  3926. }
  3927. });
  3928. return tmp_obj;
  3929. }
  3930. matches = path.split('.');
  3931. if (matches) {
  3932. matches.splice(0, 1);
  3933. matches.map((match) => {
  3934. tmp_obj = Reflect.get(tmp_obj, match);
  3935. });
  3936. return tmp_obj;
  3937. }
  3938. } catch (error) {
  3939. return null;
  3940. }
  3941. }
  3942.  
  3943. get_lastPath_and_key(path) {
  3944. let last_path, last_key;
  3945. let matches = path.match(/\[(.*?)\]/g);
  3946. if (matches && matches.length > 0) {
  3947. const tmp = matches[matches.length - 1];
  3948. if (tmp.includes('["')) {
  3949. last_key = tmp.replace(/\["|"\]/g, '');
  3950. } else {
  3951. last_key = Number(tmp.replace(/\[|\]/g, ''));
  3952. }
  3953. last_path = path.substring(0, path.lastIndexOf(tmp));
  3954. }
  3955. if (!matches) {
  3956. matches = path.split('.');
  3957. if (matches && matches.length > 0) {
  3958. last_key = matches[matches.length - 1];
  3959. last_path = path.replace('.' + last_key, '');
  3960. }
  3961. }
  3962. return [last_path, last_key];
  3963. }
  3964.  
  3965. convertPathToBracketNotation(path) {
  3966. if (!path) return '';
  3967. return path.replace(/\.[\d\w\-\_\$@]+/g, function (match) {
  3968. return '["' + match.slice(1) + '"]';
  3969. });
  3970. }
  3971.  
  3972. paths_sort(paths_arr, key_name = null, reverse = false) {
  3973. // 路径格式是json_obj["onResponseReceivedActions"][0]["appendContinuationItemsAction"]
  3974. // 支持数组元素是对象,根据里面的某个属性排序
  3975. // 支持数组元素是字符串,根据字符串排序
  3976. if (!Array.isArray(paths_arr)) {
  3977. throw new Error('paths_arr must be an array');
  3978. }
  3979. if (paths_arr.length === 0) return;
  3980. let tmp_paths_arr = paths_arr;
  3981. if (!key_name) {
  3982. key_name = 'path';
  3983. if (typeof paths_arr[0] !== 'string') throw new Error('paths_arr must be a string array');
  3984. tmp_paths_arr = [];
  3985. paths_arr.forEach(path => {
  3986. tmp_paths_arr.push({
  3987. path: path
  3988. });
  3989. });
  3990. }
  3991. const reverse_factor = reverse ? -1 : 1;
  3992. tmp_paths_arr.sort((a, b) => {
  3993. function get_sort_key(obj) {
  3994. if (!obj.sort_keys) {
  3995. const reg = /\["?(.*?)"?\]/g;
  3996. let matches = [];
  3997. let match;
  3998. while (match = reg.exec(obj[key_name])) {
  3999. if (!match[0].startsWith('["')) {
  4000. if (isNaN(match[1])) throw new Error('array index must be a number');
  4001. match[1] = parseInt(match[1]);
  4002. }
  4003. matches.push(match[1]);
  4004. }
  4005. obj.sort_keys = matches;
  4006. }
  4007. }
  4008. if (a[key_name] === b[key_name]) return 0;
  4009. get_sort_key(a);
  4010. get_sort_key(b);
  4011. const a_sort_keys = a.sort_keys;
  4012. const b_sort_keys = b.sort_keys;
  4013. if (a_sort_keys.length !== b_sort_keys.length) {
  4014. return (b_sort_keys.length - a_sort_keys.length) * reverse_factor;
  4015. }
  4016. for (let i = 0; i < a_sort_keys.length; i++) {
  4017. if (a_sort_keys[i] !== b_sort_keys[i]) {
  4018. return (b_sort_keys[i] > a_sort_keys[i] ? 1 : -1) * reverse_factor;
  4019. }
  4020. }
  4021. return 0;
  4022. });
  4023. if (paths_arr !== tmp_paths_arr) {
  4024. paths_arr.length = 0;
  4025. tmp_paths_arr.forEach(path_info => {
  4026. paths_arr.push(path_info.path);
  4027. });
  4028. }
  4029. }
  4030.  
  4031. obj_process(json_obj, express_list, traverse_all = false) {
  4032. if (typeof json_obj !== 'object') {
  4033. log('obj_process不是对象', express_list, -1);
  4034. return;
  4035. }
  4036. if (typeof express_list === 'function') {
  4037. try {
  4038. express_list = express_list(json_obj);
  4039. if (!express_list || Array.isArray(express_list) && express_list.length === 0) return;
  4040. } catch (error) {
  4041. log('obj_process express_list函数执行错误', error, -1);
  4042. return;
  4043. }
  4044. }
  4045. const data_this = this;
  4046. const abs_path_info_list = [];
  4047. const relative_path_info_list = [];
  4048. const relative_path_list = [];
  4049. const relative_short_path_list = [];
  4050. if (!json_obj || !express_list) return;
  4051. const is_array_obj = Array.isArray(json_obj);
  4052. try {
  4053. express_list.forEach(express => {
  4054. if (!express) return;
  4055. let reg;
  4056. const express_type = typeof (express);
  4057. let matches;
  4058. let conditions;
  4059. reg = /^(abs:)?(.*?)(=\-|~=|=\+|=)(\(?([^ ][\s\S]*?)\)?)?( ([\s\S]*))?$/;
  4060. if (express_type === 'string') {
  4061. matches = express.match(reg);
  4062. } else {
  4063. matches = express.value.match(reg);
  4064. conditions = express.conditions;
  4065. }
  4066. const abs = matches[1];
  4067. let path = matches[2];
  4068. const operator = matches[3];
  4069. let value = matches[4];
  4070. const condition = matches[7];
  4071. const path_extral_match = path.match(/\/\..*$|\.+$|\.\(.*$/);
  4072. let path_extral;
  4073. if (path_extral_match) {
  4074. path_extral = path_extral_match[0];
  4075. path = path.replace(path_extral, '');
  4076. }
  4077. let value_mode;
  4078. if (express_type === 'string') {
  4079. const mode_match = value?.match(/^\((.*)\)$/);
  4080. if (mode_match) {
  4081. // =('arr_insert',value,0)
  4082. const mode_info = mode_match[1].split(',');
  4083. value = mode_info[1];
  4084. const mode = mode_info[0];
  4085. mode_info.shift();
  4086. mode_info.shift();
  4087. value_mode = {
  4088. 'mode': mode,
  4089. 'params': mode_info
  4090. };
  4091. }
  4092. if (condition) {
  4093. // (fffddf|||ffff)&&&(ffff)
  4094. const tmp_conditions = condition ? condition.split(this.condition_split_and_tag) : [];
  4095. conditions = {};
  4096. for (let index = 0; index < tmp_conditions.length; index++) {
  4097. conditions['value' + index] = tmp_conditions[index].split(this.condition_split_or_tag);
  4098. }
  4099. }
  4100. }
  4101. matches = path.match(/\[([\*\d\-,]*)\]$/);
  4102. let array_index;
  4103. if (matches) {
  4104. path = path.replace(/\[([\*\d\-,]*)\]$/, '');
  4105. array_index = matches[1];
  4106. }
  4107. if (abs) {
  4108. add_data_to_abs_path({
  4109. "path": `json_obj${is_array_obj ? '' : '.'}` + path,
  4110. "express": express,
  4111. "relative_path": path,
  4112. "operator": operator,
  4113. "value": value,
  4114. "condition": conditions,
  4115. "array_index": array_index,
  4116. "path_extral": path_extral,
  4117. "value_mode": value_mode
  4118. });
  4119. } else {
  4120. relative_path_list.push(path);
  4121. const tmp_short_path = path.split('.').pop();
  4122. relative_short_path_list.push(tmp_short_path);
  4123. relative_path_info_list.push({
  4124. "express": express,
  4125. "path": path,
  4126. "operator": operator,
  4127. "value": value,
  4128. "value_mode": value_mode,
  4129. "conditions": conditions,
  4130. "array_index": array_index,
  4131. "path_extral": path_extral
  4132. });
  4133. }
  4134. });
  4135. if (relative_path_list.length > 0) {
  4136. const dec_list = [];
  4137. const dec_index_list = [];
  4138. obj_property_traverse(json_obj, '', {
  4139. "short_keys": relative_short_path_list,
  4140. "real_keys": relative_path_list
  4141. }, dec_list, dec_index_list, traverse_all);
  4142. for (let i = 0; i < dec_index_list.length; i++) {
  4143. const real_index = dec_index_list[i];
  4144. const real_path_info = relative_path_info_list[real_index];
  4145. const tmp_path = 'json_obj' + dec_list[i];
  4146. add_data_to_abs_path({
  4147. "path": tmp_path,
  4148. "express": real_path_info.express,
  4149. "relative_path": real_path_info.path,
  4150. "operator": real_path_info.operator,
  4151. "value": real_path_info.value,
  4152. "condition": real_path_info.conditions,
  4153. "array_index": real_path_info.array_index,
  4154. "path_extral": real_path_info.path_extral,
  4155. "value_mode": real_path_info.value_mode
  4156. });
  4157. }
  4158. }
  4159. try {
  4160. this.paths_sort(abs_path_info_list, 'deal_path');
  4161. } catch (error) {
  4162. abs_path_info_list.sort((a, b) => a < b ? 1 : -1);
  4163. }
  4164. for (let path_info of abs_path_info_list) {
  4165. if (!this.obj_conditional(path_info, json_obj)) continue;
  4166. if (this.obj_filter && this.obj_filter(path_info, json_obj)) continue;
  4167. obj_modify(json_obj, path_info);
  4168. }
  4169. } catch (error) {
  4170. log('obj_process处理失败', error, -1);
  4171. }
  4172.  
  4173. function add_data_to_abs_path(params) {
  4174. let { path, express, relative_path, operator, value, condition, array_index, path_extral, value_mode } = params;
  4175. let tmp;
  4176. path = data_this.convertPathToBracketNotation(path);
  4177. if (array_index === undefined) {
  4178. tmp = {};
  4179. path = path;
  4180. tmp.path = path;
  4181. tmp.relative_path = relative_path;
  4182. tmp.operator = operator;
  4183. tmp.value = value;
  4184. tmp.value_mode = value_mode;
  4185. tmp.condition = condition;
  4186. tmp.path_extral = path_extral;
  4187. tmp.express = express;
  4188. add_path(tmp);
  4189. return;
  4190. }
  4191. let array_index_list = [];
  4192. if (array_index === '*') {
  4193. let array_length;
  4194. try {
  4195. array_length = data_this.string_to_value(json_obj, path)?.length || 0;
  4196. if (!array_length) return;
  4197. } catch (error) {
  4198. log('obj_process获取数组长度失败--->' + path, error, -1);
  4199. return;
  4200. }
  4201. array_index_list = Array.from({ length: array_length }, (_, i) => i);
  4202. } else if (array_index.includes(',')) {
  4203. let is_error = false;
  4204. array_index_list = array_index.split(',').map(item => {
  4205. if (is_error) return;
  4206. if (isNaN(item)) {
  4207. is_error = true;
  4208. return;
  4209. }
  4210. return Number(item);
  4211. });
  4212. if (is_error) {
  4213. return log('obj_process数组索引格式错误--->' + path, -1);
  4214. }
  4215. } else if (array_index.includes('-')) {
  4216. const index_arr = array_index.split('-');
  4217. if (index_arr.length !== 2) return log('obj_process数组索引格式错误--->' + path, -1);
  4218. const start = Number(index_arr[0]);
  4219. const end = Number(index_arr[1]);
  4220. if (isNaN(start) || isNaN(end)) {
  4221. return log('obj_process数组索引格式错误--->' + path, -1);
  4222. }
  4223. array_index_list = Array.from({ length: end - start + 1 }, (_, i) => start + i);
  4224. } else if (!isNaN(array_index)) {
  4225. array_index_list = [array_index];
  4226. } else {
  4227. return log('obj_process数组索引格式错误--->' + path, -1);
  4228. }
  4229. for (let tmp_index = array_index_list.length - 1; tmp_index >= 0; tmp_index--) {
  4230. tmp = {};
  4231. tmp.path = path + "[" + array_index_list[tmp_index] + "]";
  4232. tmp.operator = operator;
  4233. tmp.value = value;
  4234. tmp.value_mode = value_mode;
  4235. tmp.condition = condition;
  4236. tmp.path_extral = path_extral;
  4237. tmp.relative_path = relative_path;
  4238. tmp.express = express;
  4239. add_path(tmp);
  4240. }
  4241. function add_path(path_info) {
  4242. path_info.deal_path = path_extral ? data_this.get_relative_path(path, path_extral) : path_info.path;
  4243. abs_path_info_list.push(path_info);
  4244. }
  4245. }
  4246.  
  4247. function obj_property_traverse(obj, cur_path, dec_infos, dec_list, dec_index_list, traverse_all = false) {
  4248. if (Array.isArray(obj)) {
  4249. obj.forEach((tmp_obj, index) => {
  4250. const tmp_path = cur_path + '[' + index + ']';
  4251. if (!tmp_obj || typeof (tmp_obj) !== 'object') return;
  4252. obj_property_traverse(tmp_obj, tmp_path, dec_infos, dec_list, dec_index_list, traverse_all);
  4253. });
  4254. return;
  4255. }
  4256. Object.keys(obj).forEach((key) => {
  4257. const tmp_path = cur_path + '.' + key;
  4258. let deal = false;
  4259. for (let i = 0; i < dec_infos["short_keys"].length; i++) {
  4260. if (dec_infos["short_keys"][i] === key) {
  4261. const len = dec_infos["real_keys"][i].length;
  4262. if (tmp_path.slice(tmp_path.length - len) === dec_infos["real_keys"][i]) {
  4263. dec_list.push(tmp_path);
  4264. dec_index_list.push(i);
  4265. if (!deal && traverse_all && typeof (obj[key]) === 'object') {
  4266. obj_property_traverse(obj[key], tmp_path, dec_infos, dec_list, dec_index_list, traverse_all);
  4267. }
  4268. deal = true;
  4269. }
  4270. }
  4271. }
  4272. const value = obj[key];
  4273. if (deal || !value || typeof (value) !== 'object') return;
  4274. obj_property_traverse(value, tmp_path, dec_infos, dec_list, dec_index_list, traverse_all);
  4275. });
  4276. }
  4277.  
  4278. function obj_modify(json_obj, path_info) {
  4279. const path = path_info['deal_path'];
  4280. const operator = path_info['operator'];
  4281. let value = path_info['value'];
  4282. const [last_path, last_key] = data_this.get_lastPath_and_key(path);
  4283. const last_obj = data_this.string_to_value(json_obj, last_path);
  4284. if (!last_obj) {
  4285. debugger;
  4286. return log('obj_modify处理失败,找不到对象--->' + path_info, -1);
  4287. }
  4288. if (operator === '=-') {
  4289. const is_array = typeof last_key === 'number';
  4290. if (is_array)
  4291. last_obj.splice(last_key, 1);
  4292. else
  4293. delete last_obj[last_key];
  4294. log('依据:' + path_info.express, 'obj_process');
  4295. log('删除属性-->' + path, 'obj_process');
  4296. return;
  4297. }
  4298. if (operator === '=') {
  4299. value = data_this.value_parse(value, path_info, json_obj);
  4300. last_obj[last_key] = value;
  4301. log('依据:' + path_info.express, 'obj_process');
  4302. log('修改属性-->' + path, 'obj_process');
  4303. }
  4304. const dec_obj = last_obj[last_key];
  4305. if (!dec_obj) {
  4306. return log('obj_modify处理失败,找不到对象--->' + path_info, -1);
  4307. }
  4308. if (operator === '=+') {
  4309. value = data_this.value_parse(value, path_info, json_obj);
  4310. if (dec_obj === null || dec_obj === undefined) throw new Error('dec_obj is null');
  4311. let type_ = typeof dec_obj;
  4312. if (Array.isArray(dec_obj)) type_ = 'array';
  4313. if (type_ === 'array') {
  4314. const mode_info = path_info.value_mode;
  4315. if (mode_info) {
  4316. try {
  4317. mode_info.mode === 'arr_insert' && last_obj[last_key].splice(Number(mode_info.params[0]), 0, value);
  4318. } catch (error) {
  4319. log(error, -1);
  4320. }
  4321. } else {
  4322. last_obj[last_key].push(value);
  4323. }
  4324. }
  4325. if (type_ === 'string' || type_ === 'number') last_obj[last_key] = last_obj[last_key] + value;
  4326. log('依据:' + path_info.express, 'obj_process');
  4327. log('修改属性-->' + path, 'obj_process');
  4328. }
  4329. if (operator === '~=') {
  4330. const search_value = value.split(SPLIT_TAG)[0];
  4331. const replace_value = value.split(SPLIT_TAG)[1];
  4332. last_obj[last_key] = dec_obj.replace(new RegExp(search_value, 'g'), replace_value);
  4333. log('依据:' + path_info.express, 'obj_process');
  4334. log('修改属性-->' + path, 'obj_process');
  4335. }
  4336. }
  4337. }
  4338.  
  4339. path_process(json_obj, path) {
  4340. if (path.includes('[-')) {
  4341. const match = path.match(/\[(-\d+)\]/);
  4342. const index = parseInt(match[1]);
  4343. const dec_obj_path = path.slice(0, match.index);
  4344. const array_length = this.string_to_value(json_obj, dec_obj_path + '["length"]');
  4345. if (!array_length) return path;
  4346. const real_index = array_length + index;
  4347. path = path.replace(`[${index}`, `[${real_index}`);
  4348. return this.path_process(json_obj, path);
  4349. }
  4350. return path;
  4351. }
  4352.  
  4353. value_conditional(value, condition_express) {
  4354. const reg = /(\$text|\$value|\$exist|\$notexist)?((>=|<=|>|<|!~=|!=|~=|=))?(.*)/;
  4355. const match = condition_express.match(reg);
  4356. const condition_type = match[1] || '$text';
  4357. const condition_operator = match[2];
  4358. const condition_test_value = match[4];
  4359. const operator_reg = /(>=|<=|>|<|!~=|!=|~=|=)?(.*)$/;
  4360. if (condition_type === '$value') {
  4361. // $value=1|2 或 $value>=1&2
  4362. if (!['>=', '<=', '>', '<', '='].includes(condition_operator)) return false;
  4363. const split_tag = condition_test_value.includes(this.value_split_or_tag) && this.value_split_or_tag || this.value_split_and_tag;
  4364. const condition_test_value_arr = condition_test_value.split(split_tag);
  4365. let result;
  4366. for (let test_value of condition_test_value_arr) {
  4367. const operator_match = test_value.match(operator_reg);
  4368. const operator = operator_match && operator_match[1] || condition_operator;
  4369. test_value = operator_match && operator_match[2];
  4370. if (isNaN(test_value)) {
  4371. if (split_tag === this.value_split_and_tag) return false; else continue;
  4372. };
  4373. test_value = parseInt(test_value);
  4374. if (operator === '=') result = test_value === value;
  4375. if (operator === '>=') result = value >= test_value;
  4376. if (operator === '<=') result = value <= test_value;
  4377. if (operator === '>') result = value > test_value;
  4378. if (operator === '<') result = value < test_value;
  4379. if (!result) {
  4380. if (split_tag === this.value_split_and_tag) return false; else continue;
  4381. };
  4382. return true;
  4383. }
  4384. }
  4385. if (condition_type === '$exist') {
  4386. return value !== undefined && value !== null;
  4387. }
  4388. if (condition_type === '$notexist') {
  4389. return value === undefined || value === null;
  4390. }
  4391. if (condition_type === '$text') {
  4392. let split_tag;
  4393. let condition_test_value_arr;
  4394. if (['!~=', '~='].includes(condition_operator)) {
  4395. split_tag = this.value_split_and_tag;
  4396. condition_test_value_arr = [condition_test_value];
  4397. } else {
  4398. split_tag = condition_test_value.includes(this.value_split_or_tag) && this.value_split_or_tag || this.value_split_and_tag;
  4399. condition_test_value_arr = condition_test_value.split(split_tag);
  4400. }
  4401. let result;
  4402. if (typeof (value) === 'object') value = JSON.stringify(value);
  4403. for (let test_value of condition_test_value_arr) {
  4404. const operator_match = test_value.match(operator_reg);
  4405. const operator = operator_match && operator_match[1] || condition_operator;
  4406. test_value = operator_match && operator_match[2] || test_value;
  4407. if (operator === '!=') result = test_value !== value;
  4408. if (operator === '=') result = test_value === value;
  4409. if (operator === '~=') result = new RegExp(test_value).test(value);
  4410. if (operator === '!~=') result = !new RegExp(test_value).test(value);
  4411. if (operator === '>=') result = value.length >= test_value.length;
  4412. if (operator === '>') result = value.length > test_value.length;
  4413. if (operator === '<=') result = value.length <= test_value.length;
  4414. if (operator === '>') result = value.length > test_value.length;
  4415. if (!result) {
  4416. if (split_tag === this.value_split_and_tag) return false; else continue;
  4417. };
  4418. return true;
  4419. }
  4420. }
  4421. return false;
  4422. }
  4423.  
  4424. obj_conditional(express_info, json_obj) {
  4425. //json_obj 在eval里直接调用
  4426. if (!express_info['condition']) return true;
  4427. const condition_infos = express_info['condition'];
  4428. // 与
  4429. for (let condition_list of Object.values(condition_infos)) {
  4430. let result = false;
  4431. for (let condition of condition_list) {
  4432. const reg = /^([a-zA-Z_0-9\/\-\.@\[\]]*)?(.*)/;
  4433. const match = condition.match(reg);
  4434. let condition_path = match[1];
  4435. let mod;
  4436. if (condition_path) {
  4437. if (condition_path.startsWith('/')) {
  4438. mod = 'child';
  4439. } else if (condition_path.startsWith('.')) {
  4440. mod = 'parent';
  4441. } else if (condition_path.startsWith('@')) {
  4442. mod = 'global';
  4443. } else {
  4444. mod = 'other';
  4445. }
  4446. } else {
  4447. condition_path = express_info.path;
  4448. }
  4449. const conditional_express = match[2];
  4450. if (['child', 'parent'].includes(mod)) {
  4451. // child /.a.b.c path相对路径
  4452. // parent ..a.b.c path相对路径
  4453. condition_path = this.get_relative_path(express_info.path, condition_path);
  4454. }
  4455. if (mod === 'other') {
  4456. // json_obj里的绝对路径
  4457. condition_path = this.get_relative_path('json_obj', '/.' + condition_path);
  4458. }
  4459. if (mod === 'global') {
  4460. // 提取全局里的数据
  4461. condition_path = condition_path.replace('@', this.limit_eval ? 'unsafeWindow.' : '');
  4462. }
  4463. let condition_value;
  4464. try {
  4465. condition_path = this.path_process(json_obj, condition_path);
  4466. condition_value = this.string_to_value(mod === 'global' ? unsafeWindow : json_obj, condition_path);
  4467. } catch (error) {
  4468. continue;
  4469. }
  4470. result = this.value_conditional(condition_value, conditional_express);
  4471. if (result) {
  4472. express_info.condition_value = condition_value;
  4473. express_info.conform_value_path = condition_path;
  4474. log('条件成立-->', condition, typeof condition_value === 'object' ? '[object Object]' : condition_value, 'obj_process');
  4475. break;
  4476. }
  4477. }
  4478. if (!result) return false;
  4479. }
  4480. return true;
  4481. }
  4482. }
  4483. return new DATA_PROCESS();
  4484. }
  4485. })();

QingJ © 2025

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