YouTube Preferred Settings

Save the setting permanently

目前为 2022-12-07 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube Preferred Settings
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description Save the setting permanently
  6. // @author CY Fung
  7. // @icon data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='189' height='140' viewBox='0 0 270 200' %3E%3Crect x='0' y='0' width='270' height='200'%3E%3C/rect%3E%3Cpath d='m106 190c-27.3-.6-48.3-1.5-60.6-3-7.2-.9-9.9-1.5-13.2-2.7-8.1-2.4-15-8.7-18.9-16.2-2.7-5.7-4.5-15.3-5.7-30.6-2.4-26.4-2.4-49.2 0-75.9 1.2-13.8 3-23.1 5.4-28.5 3.9-7.8 11.1-14.4 19.2-16.8 9.6-3 32.7-4.8 76.2-5.7 35.4-.6 81.3.3 105.9 2.4 20.4 1.5 27.6 3.9 34.8 11.1 7.5 7.5 9.9 15 12 37.2 1.2 12.9 1.5 22.5 1.5 39 0 25.8-1.5 46.5-4.5 59.7-1.5 6.9-4.2 12-9 16.5-7.2 7.5-14.1 9.6-34.8 11.4-24.6 1.8-71.7 3-108.3 2.1z' fill='%23018000'/%3E%3Cpath d='M110 66 178 104 110 142Z' fill='%23fff'/%3E%3C/svg%3E
  8. // @license MIT
  9. // @match https://www.youtube.com/*
  10. // @require https://cdnjs.cloudflare.com/ajax/libs/js-cookie/3.0.1/js.cookie.min.js
  11. // @noframes
  12. // @grant GM_registerMenuCommand
  13. // @grant GM_unregisterMenuCommand
  14. // @grant GM.setValue
  15. // @grant GM.getValue
  16. // @grant GM_addStyle
  17. // @run-at document-start
  18. // ==/UserScript==
  19.  
  20. /* jshint esversion:8 */
  21.  
  22. (function () {
  23. 'use strict';
  24.  
  25. const gmKey = 'yps-r';
  26. const cookieDomain ='youtube.com';
  27. /*
  28.  
  29. this.j = g.N("ALT_PREF_COOKIE_NAME", "PREF");
  30. this.u = g.N("ALT_PREF_COOKIE_DOMAIN", "youtube.com");
  31. */
  32.  
  33. /*global Cookies*/
  34.  
  35. console.assert(typeof Cookies === "object");
  36.  
  37. const skipKeys = ['SIDCC'];
  38.  
  39. const isPassiveArgSupport = (typeof IntersectionObserver === 'function');
  40. // https://caniuse.com/?search=observer
  41. // https://caniuse.com/?search=addEventListener%20passive
  42.  
  43. const bubblePassive = isPassiveArgSupport ? { capture: false, passive: true } : false;
  44. const capturePassive = isPassiveArgSupport ? { capture: true, passive: true } : true;
  45.  
  46.  
  47. let registerH = 0;
  48.  
  49.  
  50.  
  51. let navigationCheckPromiseResolve = null;
  52. const navigationCheckPromise = new Promise(r=>{
  53. navigationCheckPromiseResolve=r;
  54. });
  55.  
  56. let _beginCookie = null;
  57. function regBegin(){
  58.  
  59. if(registerH>0){
  60. GM_unregisterMenuCommand(registerH);
  61. }
  62. registerH = GM_registerMenuCommand("Setting Record Begin", begin, "b");
  63. }
  64. function regEnd(){
  65.  
  66. if(registerH>0){
  67. GM_unregisterMenuCommand(registerH);
  68. }
  69. registerH = GM_registerMenuCommand("Setting Record End", end, "e");
  70. }
  71. function begin() {
  72. _beginCookie = Cookies.get();
  73. regEnd();
  74. let elm = document.querySelector('ytd-app');
  75. if(elm) elm.classList.add('yps-recording');
  76. }
  77.  
  78.  
  79. async function goSave(req){
  80.  
  81. let gmValue = null;
  82. try{
  83. gmValue = await GM.getValue(gmKey);
  84. }catch(e){}
  85. if(!gmValue || typeof gmValue !=='string'){
  86. gmValue = '{}';
  87. }
  88. let pObj = null;
  89. try{
  90. pObj = JSON.parse(gmValue);
  91. }catch(e){
  92.  
  93. }
  94. if(!pObj || pObj.version !== 1.0){
  95. pObj = {
  96. version: 1.0,
  97. cookies:{
  98.  
  99. }
  100. };
  101. }
  102.  
  103. const cookies = pObj.cookies;
  104.  
  105. for(const {key} of req.removed){
  106.  
  107. cookies[key]={
  108. v: null
  109. };
  110. }
  111. for(const {key, newValue} of req.added){
  112.  
  113. cookies[key]={
  114. v: newValue
  115. };
  116. }
  117. for(const {key, newValue} of req.changed){
  118.  
  119. cookies[key]={
  120. v: newValue
  121. };
  122. }
  123.  
  124.  
  125. let success = true;
  126. try{
  127. let gmValue = JSON.stringify(pObj);
  128. await GM.setValue(gmKey, gmValue);
  129. }catch(e){
  130. console.warn(e);
  131. success = false;
  132. }
  133.  
  134. if(success){
  135. alert('The settings have been saved.');
  136. }
  137.  
  138.  
  139.  
  140. }
  141.  
  142. function end() {
  143. regBegin();
  144. const beginCookie = _beginCookie;
  145. if (!beginCookie) return alert('Recording was not started.');
  146. _beginCookie = null;
  147.  
  148.  
  149. let elm = document.querySelector('ytd-app');
  150. if(elm) elm.classList.remove('yps-recording');
  151.  
  152. let endCookie = Cookies.get();
  153. let removed = [];
  154. let added = [];
  155. let keysBegin = Object.keys(beginCookie);
  156. let keysEnd = Object.keys(endCookie);
  157.  
  158. let changed = [];
  159. for (const key of keysBegin) {
  160.  
  161. if (keysEnd.includes(key)) {
  162. if (beginCookie[key] !== endCookie[key] && !skipKeys.includes(key)) {
  163.  
  164. changed.push({
  165. key: key,
  166. preValue: beginCookie[key],
  167. newValue: endCookie[key],
  168. changed: true
  169. });
  170. }
  171. endCookie[key] = null;
  172. continue;
  173. }
  174. removed.push({
  175. key: key,
  176. prevValue: beginCookie[key],
  177. removed: true
  178. });
  179. }
  180.  
  181. for (const key of keysEnd) {
  182.  
  183. if (endCookie[key] === null) continue;
  184. added.push({
  185. key: key,
  186. newValue: endCookie[key],
  187. added: true
  188. });
  189. }
  190.  
  191. if (removed.length > 0 || added.length > 0 || changed.length > 0) {
  192.  
  193. let text = [
  194. `The recorded changes are as follows:`,
  195. `Removed keys: ${JSON.stringify(removed, null, 2)},`,
  196. `Added keys: ${JSON.stringify(added, null, 2)},`,
  197. `Changed keys: ${JSON.stringify(changed, null, 2)},`,
  198. `If you want to save these changes permanently, please type 'confirm'.`
  199. ].join('\n');
  200. let ret = prompt(text, "confirm");
  201. if (ret.toLowerCase() === 'confirm') {
  202. goSave({
  203. version: 1.0,
  204. removed: removed,
  205. added: added,
  206. changed: changed
  207. });
  208. }
  209. } else {
  210. alert('No changes can be recorded.');
  211. }
  212.  
  213.  
  214. }
  215.  
  216. regBegin();
  217.  
  218. async function init(){
  219.  
  220. let gmValue = null;
  221. try{
  222. gmValue = await GM.getValue(gmKey);
  223. }catch(e){}
  224. if(!gmValue) return;
  225.  
  226. let pObj = null;
  227. try{
  228. pObj = JSON.parse(gmValue);
  229. }catch(e){
  230. }
  231.  
  232. if(!pObj || pObj.version !== 1.0){
  233. pObj = {
  234. version: 1.0,
  235. cookies:{
  236. }
  237. };
  238. return;
  239. }
  240.  
  241. if(!pObj.cookies) return;
  242.  
  243. await Promise.resolve(0);
  244.  
  245. const cookies = pObj.cookies;
  246.  
  247.  
  248. for (const key in cookies) {
  249. if(cookies[key].v===null){
  250. Cookies.remove(key);
  251. }else if(typeof cookies[key].v ==='string'){
  252. Cookies.set(key, cookies[key].v, { domain: cookieDomain} );
  253. }
  254. }
  255.  
  256. if(typeof navigationCheckPromiseResolve === 'function'){
  257. navigationCheckPromiseResolve();
  258. }
  259.  
  260.  
  261. }
  262. init();
  263.  
  264.  
  265.  
  266. async function updateRecord(){
  267.  
  268. let gmValue = null;
  269. try{
  270. gmValue = await GM.getValue(gmKey);
  271. }catch(e){}
  272. if(!gmValue) return;
  273.  
  274. let pObj = null;
  275. try{
  276. pObj = JSON.parse(gmValue);
  277. }catch(e){
  278. }
  279.  
  280. if(!pObj || pObj.version !== 1.0){
  281. pObj = {
  282. version: 1.0,
  283. cookies:{
  284. }
  285. };
  286. return;
  287. }
  288.  
  289. if(!pObj.cookies) return;
  290.  
  291. await Promise.resolve(0);
  292.  
  293. const cookies = pObj.cookies;
  294.  
  295. let overrided = 0;
  296.  
  297. for (const key in cookies) {
  298.  
  299.  
  300. if(cookies[key].v===null){
  301. if(typeof Cookies.get(key) ==='string'){
  302. delete cookies[key];
  303. overrided++;
  304. }
  305. }else if(typeof cookies[key].v ==='string'){
  306.  
  307. if(Cookies.get(key)!==cookies[key].v){
  308. delete cookies[key];
  309. overrided++;
  310. }
  311. }
  312.  
  313. }
  314.  
  315. if(overrided>0){
  316.  
  317. let success = true;
  318. try{
  319. let gmValue = JSON.stringify(pObj);
  320. await GM.setValue(gmKey, gmValue);
  321. }catch(e){
  322. console.warn(e);
  323. success = false;
  324. }
  325. if(success){
  326. console.log('The preferred settings have been overrided.');
  327. }
  328.  
  329.  
  330. }
  331.  
  332.  
  333.  
  334.  
  335. }
  336.  
  337. document.addEventListener('yt-navigate-finish', function(){
  338.  
  339. navigationCheckPromise.then(()=>{
  340. updateRecord();
  341. });
  342.  
  343. }, bubblePassive);
  344.  
  345.  
  346. let keyupDT = 0;
  347.  
  348. document.addEventListener('keyup', function(evt){
  349.  
  350. if(evt.shiftKey && evt.code ==='KeyR'){
  351.  
  352. }else{
  353. if(keyupDT>0 && evt.code ==='KeyR'){
  354.  
  355. }else{
  356.  
  357. keyupDT = 0;
  358. return;
  359. }
  360. }
  361.  
  362. let t = Date.now();
  363. if(keyupDT>0){
  364. if(t - keyupDT <300){
  365. console.log('Shift-R dblclick');
  366. if(!_beginCookie){
  367. begin();
  368. }else{
  369. end();
  370. }
  371. }
  372. keyupDT = 0;
  373. }
  374. keyupDT = t;
  375.  
  376.  
  377. }, bubblePassive);
  378.  
  379. GM_addStyle(
  380. `ytd-app.yps-recording{
  381. filter:brightness(0.7);
  382. }`
  383. );
  384.  
  385.  
  386. // Your code here...
  387. })();