PageSourceSearch

https://youku.cfd/play/vt.js

js youku.cfd collected 2026-10-04 01:41:45 UTC 27,194 bytes, 745 lines download raw bytes

1// ==UserScript==
2// @name         Video Together 一起看视频
3// @namespace    https://videotogether.github.io/
4// @version      1760266471
5// @description  Watch video together 一起看视频
6// @author       [email protected]
7// @match        *://*/*
8// @icon         https://videotogether.github.io/icon/favicon-32x32.png
9// @grant        GM.xmlHttpRequest
10// @grant        GM_addElement
11// @grant        GM.setValue
12// @grant        GM.getValue
13// @grant        GM.getTab
14// @grant        GM.saveTab
15// @connect      2gether.video
16// @connect      api.2gether.video
17// @connect      api.begin0114.wiki
18// @connect      api.panghair.com
19// @connect      vt.panghair.com
20// @connect      raw.githubusercontent.com
21// @connect      fastly.jsdelivr.net
22// @connect      videotogether.oss-cn-hangzhou.aliyuncs.com
23// ==/UserScript==
24
25(async function () {
26    if (['challenges.cloudflare.com'].indexOf(window.location.hostname) != -1) {
27        return;
28    }
29    let isDevelopment = false;
30
31    if (document instanceof XMLDocument) {
32        return;
33    }
34
35    let version = '9999999999'
36    // 1760266471
37    let type = 'website'
38    function getBrowser() {
39        switch (type) {
40            case 'Safari':
41                return browser;
42            case 'Chrome':
43            case 'Firefox':
44                return chrome;
45        }
46    }
47    let isExtension = (type == "Chrome" || type == "Safari" || type == "Firefox");
48    let isWebsite = (type == "website" || type == "website_debug");
49    let isUserscript = (type == "userscript");
50    let websiteGM = {};
51    let extensionGM = {};
52    const encodedChinaCdnA = 'aHR0cHM6Ly92aWRlb3RvZ2V0aGVyLm9zcy1jbi1oYW5nemhvdS5hbGl5dW5jcy5jb20='
53    const encodeFastlyJsdelivrCdn = 'aHR0cHM6Ly9mYXN0bHkuanNkZWxpdnIubmV0L2doL1ZpZGVvVG9nZXRoZXIvVmlkZW9Ub2dldGhlckBsYXRlc3Q='
54    function getCdnPath(encodedCdn, path) {
55        const cdn = encodedCdn.startsWith('https') ? encodedCdn : atob(encodedCdn);
56        return `${cdn}/${path}`;
57    }
58    async function getCdnConfig(encodedCdn) {
59        return fetch(getCdnPath(encodedCdn, 'release/cdn-config.json')).then(r => r.json())
60    }
61    async function getChinaCdnB() {
62        return getCdnConfig(encodedChinaCdnA).then(c => c.jsCdnHostChina)
63    }
64
65    function getGM() {
66        if (type == "website" || type == "website_debug") {
67            return websiteGM;
68        }
69        if (type == "Chrome" || type == "Safari" || type == "Firefox") {
70            return extensionGM;
71        }
72        return GM;
73    }
74
75    function getRealTableName(table) {
76        return table.replace('-mini', '');
77    }
78
79    setInterval(() => {
80        if (isWebsite) {
81            (function () {
82                const iframes = document.getElementsByTagName('iframe');
83                for (const iframe of iframes) {
84                    try {
85                        if (iframe.contentWindow.VideoTogetherParentInject != true &&
86                            window.location.origin === iframe.contentWindow.location.origin) {
87                            console.log("inject to iframe");
88                            const script = document.createElement('script');
89                            script.src = '/play/vt.zh-cn.website.js?timestamp=' + version;
90                            iframe.contentWindow.document.body.appendChild(script);
91                            iframe.contentWindow.VideoTogetherParentInject = true;
92                        }
93                    } catch (error) {
94                    }
95                }
96            })();
97        }
98    }, 2000);
99
100    if (type == "website" || type == "website_debug") {
101
102        getGM().setValue = async (key, value) => {
103            return localStorage.setItem(key, JSON.stringify(value));
104        }
105
106        getGM().getValue = async (key) => {
107            return JSON.parse(localStorage.getItem(key));
108        }
109
110        getGM().getTab = async () => {
111            let tab = sessionStorage.getItem('VideoTogetherTab');
112            return tab == null ? {} : JSON.parse(tab);
113        }
114
115        getGM().saveTab = async (tab) => {
116            return sessionStorage.setItem('VideoTogetherTab', JSON.stringify(tab));
117        }
118
119        getGM().xmlHttpRequest = async (props) => {
120            try {
121                fetch(props.url, {
122                    method: props.method,
123                    body: props.method == "GET" ? undefined : JSON.stringify(props.data)
124                })
125                    .then(r => r.text())
126                    .then(text => props.onload({ responseText: text }))
127                    .catch(e =>
127 props.onerror(e));
128            } catch (e) {
129                props.onerror(e);
130            }
131        }
132    }
133    if (type == "Chrome" || type == "Safari" || type == "Firefox") {
134        getGM().setValue = async (key, value) => {
135            return await new Promise((resolve, reject) => {
136                try {
137                    let item = {};
138                    item[key] = value;
139                    getBrowser().storage.local.set(item, function () {
140                        resolve();
141                    });
142                } catch (e) {
143                    reject(e);
144                }
145            })
146        }
147        getGM().getValue = async (key) => {
148            return await new Promise((resolve, reject) => {
149                try {
150                    getBrowser().storage.local.get([key], function (result) {
151                        resolve(result[key]);
152                    });
153                } catch (e) {
154                    reject(e);
155                }
156
157            })
158        }
159        getGM().getTab = async () => {
160            return await new Promise((resolve, reject) => {
161                try {
162                    getBrowser().runtime.sendMessage(JSON.stringify({ type: 1 }), function (response) {
163                        resolve(response);
164                    })
165                } catch (e) {
166                    reject(e);
167                }
168
169            })
170        }
171        getGM().saveTab = async (tab) => {
172            return await new Promise((resolve, reject) => {
173                try {
174                    getBrowser().runtime.sendMessage(JSON.stringify({ type: 2, tab: tab }), function (response) {
175                        resolve(response);
176                    })
177                } catch (e) {
178                    reject(e);
179                }
180            })
181        }
182        getGM().xmlHttpRequest = async (props) => {
183            try {
184                getBrowser().runtime.sendMessage(JSON.stringify({ type: 3, props: props }), function (response) {
185                    if (response.error != undefined) {
186                        throw response.error;
187                    }
188                    props.onload(response);
189                })
190            } catch (e) {
191                props.onerror(e);
192            }
193        }
194    }
195
196    if (isExtension) {
197        let vtEnabled = await getGM().getValue('vtEnabled');
198        if (vtEnabled === false) {
199            getBrowser().runtime.sendMessage(JSON.stringify({ type: 4, enabled: false }));
200            return;
201        } else {
202            getBrowser().runtime.sendMessage(JSON.stringify({ type: 4, enabled: true }));
203        }
204    }
205
206
207    const languages = ['en-us', 'zh-cn', 'ja-jp'];
208    let language = 'en-us';
209    let settingLanguage = undefined;
210    try {
211        settingLanguage = await getGM().getValue("DisplayLanguage");
212    } catch (e) { };
213
214    if (typeof settingLanguage != 'string') {
215        settingLanguage = navigator.language;
216    }
217    if (typeof settingLanguage == 'string') {
218        settingLanguage = settingLanguage.toLowerCase();
219        if (languages.includes(settingLanguage)) {
220            language = settingLanguage;
221        } else {
222            const settingLanguagePrefix = settingLanguage.split('-')[0];
223            for (let i = 0; i < languages.length; i++) {
224                const languagePrefix = languages[i].split('-')[0];
225                if (settingLanguagePrefix === languagePrefix) {
226                    language = languages[i];
227                    break;
228                }
229            }
230        }
231    }
232
233    let vtRefreshVersion = version + language;
234    try {
235        let publicVtVersion = await getGM().getValue("PublicVtVersion")
236        if (publicVtVersion != null) {
237            vtRefreshVersion = vtRefreshVersion + String(publicVtVersion);
238        }
239    } catch (e) { };
240    console.log(vtRefreshVersion)
241
242    let cachedVt = null;
243    /*
244    try {
245        let vtType = isWebsite ? "website" : "user";
246        let privateCachedVt = await getGM().getValue("PrivateCachedVt");
247        let cachedVersion = null;
248        try {
249            cachedVersion = privateCachedVt['version'];
250        } catch { };
251        if (cachedVersion == vtRefreshVersion) {
252            cachedVt = privateCachedVt['data'];
253        } else {
254            console.log("Refresh VT");
255            fetch(getCdnPath(encodeFastlyJsdelivrCdn, `release/vt.${language}.${vtType}.js?vtRefreshVersion=${vtRefreshVersion}`))
256                .then(r => r.text())
257                .then(data => getGM().setValue('PrivateCachedVt', {
258                    'version': vtRefreshVersion,
259                    'data': data
260                }))
261                .catch(() => {
262                    getChinaCdnB().then(chinaCdnB => fetch(getCdnPath(chinaCdnB, `release/vt.${language}.${vtType}.js?vtRefreshVersion=${vtRefreshVersion}`)))
263                        .then(r => r.text())
264                        .then(data => getGM().setValue('PrivateCachedVt', {
265                            'version': vtRefreshVersion,
266                            'data': data
267                        }))
268                })
269        }
270    } catch (e) { };
271    */
272
273    async function AppendKey(key) {
274        let keysStr = await getGM().getValue("VideoTogetherKeys");
275        let keys = new Set(JSON.parse(keysStr));
276        keys.add(key);
277        await getGM().setValue("VideoTogetherKeys", JSON.stringify(Array.from(keys)));
278    }
279
280    async function GetKeys() {
281        let keysStr = await getGM().getValue("VideoTogetherKeys");
282        try {
283            let keys = new Set(JSON.parse(keysStr));
284            return Array.from(keys);
285        } catch (e) {
286            await getGM().setValue("VideoTogetherKeys", "[]");
287            return [];
288        }
289    }
290
291    function InsertInlineScript(content) {
292        try {
293            let inlineScript = document.createElement("script");
294            inlineScript.textContent = content;
295            document.head.appendChild(inlineScript);
296        } catch { }
297        try {
298            if (isUserscript) {
299                GM_addElement('script', {
300                    textContent: content,
301                    type: 'text/javascript'
302                });
303            }
304        } catch { }
305        try {
306            if (isWebsite) {
307                eval(content);
308            }
309        } catch { }
310    }
311
312    function InsertInlineJs(url) {
313        try {
314            getGM().xmlHttpRequest({
315                method: "GET",
316                url: url,
317                onload: function (response) {
318                    InsertInlineScript(response.responseText);
319                }
320            })
321        } catch (e) { };
322    }
323
324    async function SetTabStorage(data) {
325        try {
326            let tabObj = await getGM().getTab();
327            tabObj.VideoTogetherTabStorage = data;
328            await getGM().saveTab(tabObj);
329            window.postMessage({
330                source: "VideoTogether",
331                type: 19,
332                data: tabObj.VideoTogetherTabStorage
333            })
334        } catch (e) { };
335    }
336
337    if (window.VideoTogetherLoading) {
338        return;
339    }
340    window.VideoTogetherLoading = true;
341    let ExtensionInitSuccess = false;
342
343    let isTrustPageCache = undefined;
344    function isTrustPage() {
345        if (isDevelopment) {
346            return true;
347        }
348        if (window.location.protocol != 'https:') {
349            return false
350        }
351
352        if (isTrustPageCache == undefined) {
353            const domains = [
354                '2gether.video', 'videotogether.github.io'
355            ];
356
357            const hostname = window.location.hostname;
358            isTrustPageCache = domains.some(domain => hostname === domain || hostname.endsWith(`.${domain}`));
359        }
360        return isTrustPageCache;
361    }
362    const indexedDbWriteHistory = {}
363    function needTrustPage() {
364        if (!isTrustPage()) {
365            throw "not trust page"
366        }
367    }
368
369    window.addEventListener("message", async e => {
370        if (e.data.source == "VideoTogether") {
371            switch (e.data.type) {
372                case 13: {
373                    let url = new URL(e.data.data.url);
374                    if (!url.hostname.endsWith("2gether.video")
375                        && !url.hostname.endsWith("xn--6kr25xemln66b.com")
376                        && !url.hostname.endsWith("panghair.com")
377                        && !url.hostname.endsWith("videotogether.github.io")
378                        && !url.hostname.endsWith("aliyuncs.com")) {
379                        console.error("permission error", e.data);
380                        return;
381                    }
382                    getGM().xmlHttpRequest({
383                        method: e.data.data.method,
384                        url: e.data.data.url,
385                        data: e.data.data.data,
386                        onload: function (response) {
387                            let data = null;
388                            try {
389                                data = JSON.parse(response.responseText);
390                            } catch (e) { };
391                            window.postMessage({
392                                source: "VideoTogether",
393                                type: 14,
394                                data: {
395                                    id: e.data.data.id,
396                                    data: data,
397                                    text: response.responseText
398                                }
399                            })
400                        },
401                        onerror: function (error) {
402                            window.postMessage({
403                                source: "VideoTogether",
404                                type: 14,
405                                data: {
406                                    id: e.data.data.id,
407                                    error: error,
408                                }
409                            })
410                        }
411                    })
412                    break;
413                }
414                case 15: {
415                    if (window.location.hostname.endsWith("videotogether.github.io")
416                        || window.location.hostname.endsWith("2gether.video")
417                        || e.data.data.key.startsWith("Public")
418                        || isWebsite
419                        || isDevelopment) {
420                        getGM().setValue(e.data.data.key, e.data.data.value)
421                        AppendKey(e.data.data.key);
422                        break;
423                    } else {
424                        console.error("permission error", e.data);
425                    }
426                    break;
427                }
428                case 17: {
429                    ExtensionInitSuccess = true;
430                    break;
431                }
432                case 18: {
433                    await SetTabStorage(e.data.data);
434                    break;
435                }
436                case 2001: {
437                    getBrowser().runtime.sendMessage(JSON.stringify(e.data), response => {
438                        const realTableName = getRealTableName(e.data.data.table)
439                        if (indexedDbWriteHistory[realTableName] == undefined) {
440                            indexedDbWriteHistory[realTableName] = {};
441                        }
442                        indexedDbWriteHistory[realTableName][e.data.data.key] = true;
443                        window.postMessage({
444                            source: "VideoTogether",
445                            type: 2003,
446                            data: {
447                                id: e.data.data.id,
448                                table: e.data.data.table,
449                                key: e.data.data.key,
450                                error: response.error
451                            }
452                        })
453                    })
454                    break;
455                }
456                case 2002: {
457                    try {
458                        const realTableName = getRealTableName(e.data.data.table)
459                        if (!indexedDbWriteHistory[realTableName][e.data.data.key]) {
460                            needTrustPage();
461                        }
462                    } catch {
463                        needTrustPage();
464                    }
465                    getBrowser().runtime.sendMessage(JSON.stringify(e.data), response => {
466                        window.postMessage({
467                            source: "VideoTogether",
468                            type: 2004,
469                            data: {
470                                id: e.data.data.id,
471                                table: e.data.data.table,
472                                key: e.data.data.key,
473                                data: response.data,
474                                error: response.error
475                            }
476                        })
477                    })
478                    break;
479                }
480                case 2005: {
481                    needTrustPage();
482                    getBrowser().runtime.sendMessage(JSON.stringify(e.data), response => {
483                        window.postMessage({
484                            source: "VideoTogether",
485                            type: 2006,
486                            data: {
487                                id: e.data.data.id,
488                                table: e.data.data.table,
489                                regex: e.data.data.regex,
490                                data: response.data,
491                                error: response.error
492                            }
493                        })
494                    })
495                    break;
496                }
497                case 2007: {
498                    needTrustPage();
499                    getBrowser().runtime.sendMessage(JSON.stringify(e.data), response => {
500                        window.postMessage({
501                            source: "VideoTogether",
502                            type: 2008,
503                            data: {
504                                id: e.data.data.id,
505                                table: e.data.data.table,
506                                key: e.data.data.key,
507                                error: response.error
508                            }
509                        })
510                    })
511                    break;
512                }
513                case 2009: {
514                    getBrowser().runtime.sendMessage(JSON.stringify(e.data), response => {
515                        window.postMessage({
516                            source: "VideoTogether",
517                            type: 2010,
518                            data: {
519                                data: JSON.parse(response)
520                            }
521                        })
522                    })
523                    break;
524                }
525                case 3009: {
526                    getBrowser().runtime.sendMessage(JSON.stringify(e.data))
527                    break;
528                }
529                case 3010: {
530                    needTrustPage();
531                    getBrowser().runtime.sendMessage(JSON.stringify(e.data), response => {
532                        window.postMessage({
533                            source: "VideoTogether",
534                            type: 3011,
535                            data: {
536                                id: e.data.data.id,
537                                error: response.error
538                            }
539                        })
540                    })
541                    break;
542                }
543            }
544        }
545    });
546
547    let isMain = window.self == window.top;
548
549    async function PostStorage() {
550        try {
551            let keys = await GetKeys();
552            let data = {}
553            for (let i = 0; i < keys.length; i++) {
554                data[keys[i]] = await getGM().getValue(keys[i]);
555                if (data[keys[i]] == 'true') {
556                    data[keys[i]] = true;
557                }
558                if (data[keys[i]] == 'false') {
559                    data[keys[i]] = false;
560                }
561            }
562            data["UserscriptType"] = type;
563            data["LoaddingVersion"] = version;
564            data["VideoTogetherTabStorageEnabled"] = true;
565            data['ExtensionLanguages'] = languages;
566            try {
567                data["VideoTogetherTabStorage"] = (await getGM().getTab()).VideoTogetherTabStorage;
568            } catch (e) {
569                data["VideoTogetherTabStorageEnabled"] = false;
570            }
571            window.postMessage({
572                source: "VideoTogether",
573                type: 16,
574                data: data
575            }, "*");
576        } catch { }
577    }
578
579    PostStorage();
580    setInterval(() => {
581        PostStorage();
582    }, 1000);
583
584    function insertJs(url) {
585        let script = document.createElement('script');
586        script.type = 'text/javascript';
587        script.src = url;
588        (document.body || document.documentElement).appendChild(script);
589    }
590
591    let wrapper = document.createElement("div");
592    wrapper.innerHTML = `<div id="videoTogetherLoading" style="display:none;"></div>`;
593
594    (document.body || document.documentElement).appendChild(wrapper);
595    let script = document.createElement('script');
596    script.type = 'text/javascript';
597    switch (type) {
598        case "userscript":
599            script.src = getCdnPath(encodeFastlyJsdelivrCdn, `release/vt.${language}.user.js?timestamp=${version}`);
600            break;
601        case "Chrome":
602        case "Safari":
603        case "Firefox":
604            let inlineDisabled = false;
605            let evalDisabled = false;
606            let urlDisabled = false;
607            let hotUpdated = false;
608            document.addEventListener("securitypolicyviolation", (e) => {
609                if (hotUpdated) {
610                    return;
611                }
612                for (let blockedStr of [e.blockedURI, e.sample]) {
613                    for (let extensionUrl of ["2gether.video", "jsdelivr.net"]) {
614                        try {
615                            if (blockedStr.indexOf(extensionUrl) != -1) {
616                                urlDisabled = true;
617                            }
618                        } catch { }
619
620                    }
621                }
622
623                if (urlDisabled) {
624                    console.log("hot update is not successful")
625                    insertJs(getBrowser().runtime.getURL(`vt.${language}.user.js`));
626                    hotUpdated = true;
627                }
628            });
629            if (isDevelopment) {
630                script.src = getBrowser().runtime.getURL(`vt.${language}.user.js`);
631            } else {
632                script.src = getBrowser().runtime.getURL(`load.${language}.js`);
633            }
634            script.setAttribute("cachedVt", cachedVt);
635            break;
636        case "userscript_debug":
637            script.src = `http://127.0.0.1:7000/release/vt.debug.${language}.user.js?timestamp=` + parseInt(Date.now());
638            break;
639        case "website":
640            script.src = '/play/vt.zh-cn.website.js?timestamp=' + version;
641            break;
642        case "website_debug":
643            script.src = `http://127.0.0.1:7000/release/vt.debug.${language}.website.js?timestamp=` + parseInt(Date.now());
644            break;
645    }
646
647    if (isWebsite || isUserscript) {
648        /*
649        if (cachedVt != null) {
650            InsertInlineScript(cachedVt);
651        }
652        */
653        setTimeout(() => {
654            if (!ExtensionInitSuccess) {
655                (document.body || document.documentElement).appendChild(script);
656                if (isWebsite) {
657                    // keep this inline inject because shark browser needs this
658                    InsertInlineJs(script.src);
659                }
660                try {
661                    GM_addElement('script', {
662                        src: script.src,
663                        type: 'text/javascript'
664                    })
665                } catch { }
666            }
667        }, 10);
668    } else {
669        (document.body || document.documentElement).appendChild(script);
670    }
671
672    // fallback to china service
673    setTimeout(async () => {
674        try {
675            document.querySelector("#videoTogetherLoading").remove()
676        } catch { }
677        if (type == "Chrome" || type == "Firefox" || type == "Safari") {
678            return;
679        }
680        if (!ExtensionInitSuccess) {
681            let script = document.createElement('script');
682            script.type = 'text/javascript';
683            const chinaCdnB = await getChinaCdnB();
684            script.src = getCdnPath(chinaCdnB, `release/vt.${language}.user.js`);
685            (document.body || document.documentElement).appendChild(script);
686            try {
687                if (isWebsite) {
688                    InsertInlineJs(script.src);
689                }
690
691                GM_addElement('script', {
692                    src: script.src,
693                    type: 'text/javascript'
694                })
695            } catch (e) { };
696        }
697    }, 5000);
698    function filter(e) {
699        let target = e.target;
700
701        if (target.id != "videoTogetherLoading") {
702            return;
703        }
704
705        target.moving = true;
706
707        if (e.clientX) {
708            target.oldX = e.clientX;
709            target.oldY = e.clientY;
710        } else {
711            target.oldX = e.touches[0].clientX;
712            target.oldY = e.touches[0].clientY;
713        }
714
715        target.oldLeft = window.getComputedStyle(target).getPropertyValue('left').split('px')[0] * 1;
716        target.oldTop = window.getComputedStyle(target).getPropertyValue('top').split('px')[0] * 1;
717
718        document.onmousemove = dr;
719        document.ontouchmove = dr;
720
721        function dr(event) {
722            if (!target.moving) {
723                return;
724            }
725            if (event.clientX) {
726                target.distX = event.clientX - target.oldX;
727                target.distY = event.clientY - target.oldY;
728            } else {
729                target.distX = event.touches[0].clientX - target.oldX;
730                target.distY = event.touches[0].clientY - target.oldY;
731            }
732
733            target.style.left = Math.min(document.documentElement.clientWidth - target.clientWidth, Math.max(0, target.oldLeft + target.distX)) + "px";
734            target.style.top = Math.min(document.documentElement.clientHeight - target.clientHeight, Math.max(0, target.oldTop + target.distY)) + "px";
735        }
736
737        function endDrag() {
738            target.moving = false;
739        }
740        target.onmouseup = endDrag;
741        target.ontouchend = endDrag;
742    }
743    document.onmousedown = filter;
744    document.ontouchstart = filter;
745})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.