-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsync-userscript.js
More file actions
91 lines (75 loc) · 3.62 KB
/
sync-userscript.js
File metadata and controls
91 lines (75 loc) · 3.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Sync userscript from extension source
const fs = require('fs');
const ext = fs.readFileSync('extension/ytkit.js', 'utf8');
// Extract version from extension source
const verMatch = ext.match(/const YTKIT_VERSION = '([^']+)'/);
const version = verMatch ? verMatch[1] : '3.2.0';
// Userscript header
const header = `// ==UserScript==
// @name YTKit v${version}
// @namespace https://github.com/SysAdminDoc/YouTube-Kit
// @version ${version}
// @description Ultimate YouTube customization with ad blocking, SponsorBlock, video/channel hiding, playback enhancements, and 115+ features
// @author Matthew Parker
// @match https://www.youtube.com/*
// @match https://youtube.com/*
// @exclude https://m.youtube.com/*
// @exclude https://studio.youtube.com/*
// @run-at document-start
// @inject-into content
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @grant GM.xmlHttpRequest
// @connect sponsor.ajay.app
// @connect returnyoutubedislikeapi.com
// @connect cobalt.meowing.de
// @connect *.meowing.de
// @connect raw.githubusercontent.com
// @connect localhost
// @connect 127.0.0.1
// ==/UserScript==
`;
// Preamble for userscript (replaces gm-compat shim loading)
const preamble = `(function() {
'use strict';
// In userscript context, GM_* APIs are native — no shim needed
// window.ytInitialPlayerResponse and window.__ytab are directly accessible (same page context)
const GM = { xmlHttpRequest: GM_xmlhttpRequest };
// GM_cookie not available in userscripts — provide no-op
const GM_cookie = { list(filter, cb) { cb(null, 'GM_cookie not available in userscript mode'); } };
`;
// Find where the extension preamble ends (after the GM = gm.GM; line)
// The body starts after: const GM = gm.GM;
const bodyStartMarker = 'const GM = gm.GM;';
const bodyStartIdx = ext.indexOf(bodyStartMarker);
if (bodyStartIdx === -1) {
console.error('Could not find body start marker');
process.exit(1);
}
// Skip past the marker and the next newline(s)
let afterMarker = bodyStartIdx + bodyStartMarker.length;
while (ext[afterMarker] === '\r' || ext[afterMarker] === '\n') afterMarker++;
let body = ext.slice(afterMarker);
// Replace the _rw bridge object and references
// Remove the entire _rw bridge block (multi-line const _rw = { ... };)
body = body.replace(/\s*\/\/ Bridge to page context:[\s\S]*?_prCacheHref: ''\r?\n\s*\};/,
'\n // In userscript context, window.ytInitialPlayerResponse and window.__ytab\n // are directly accessible (same page context, no ISOLATED/MAIN world split)');
// Replace all _rw.ytInitialPlayerResponse with window.ytInitialPlayerResponse
body = body.replace(/_rw\.ytInitialPlayerResponse/g, 'window.ytInitialPlayerResponse');
// Replace all _rw.__ytab with window.__ytab
body = body.replace(/_rw\.__ytab/g, 'window.__ytab');
// Replace the async IIFE closing with regular IIFE
// The extension uses (async function() { ... })(); — we use (function() { ... })();
// But the extension's outer wrapper is already stripped by taking the body
// Fix: the extension ends with })(); — we need to match that
// The body still has the closing })(); from the extension's async IIFE
// We need to remove it and add our own
// Remove trailing })(); from extension wrapper
body = body.replace(/\r?\n\}\)\(\);\s*$/, '');
// Compose
const userscript = header + '\n' + preamble + body + '\n})();\n';
fs.writeFileSync('ytkit.user.js', userscript, 'utf8');
const lines = userscript.split('\n').length;
console.log(`Userscript synced: ${lines} lines, v${version}`);