您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Logs fetch, xhr, ws requests in console
当前为
// ==UserScript== // @name Site Measurements // @namespace http://tampermonkey.net/ // @version 2024-08-06.5 // @license MIT // @description Logs fetch, xhr, ws requests in console // @author Grosmar // @match https://*.livejasmin.com/* // @match https://livejasmin.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=livejasmin.com // @grant none // @run-at document-start // ==/UserScript== (function() { 'use strict'; // Measurement prep let measurements = []; const pushMeasurement = (msg) => { if (msg) { measurements.push( {time: Date.now(), msg: msg } ) } return msg; }; // CONFIG const enableLogs = true; const filteredWs = /jaws\./; // these websockets will be filtered out from logging const binaryWsPackageCountLimit = 3; //show only this amount of binary packages const wsEventFilter = { r: /onRandomAccessPoint|onMetaData/, limit: 2} // messages with this content will be shown only `limit` amount of time const injectWsSend = { pattern: /"join",\{(?!.*enableTrace)/, replacement: '"join",{"enableTrace":true,' }; // if message pattern matches, injects this into websocket command let wsBinaryCounter = 0; // Used to determine second binary package for keyframe const measurementConfig = [ { callType: /^(?:NETWORK_User_MouseUp|NETWORK_User_TouchStart)/, filter: null, filterArgIndex: null, func: (args) => "USER_START" }, { callType: /^NETWORK_Ws_Send/, filter: /"join"/, filterArgIndex: 1, func: (args) => "OC_JOIN" //measurements = measurements.concat(JSON.parse(args[0].substr(3)).map( e => }, { callType: /^NETWORK_Ws_Msg/, filter: /"getEdge"/, filterArgIndex: 1, func: (args) => "OC_JOINED" //measurements = measurements.concat(JSON.parse(args[0].substr(3)).map( e => }, { callType: /^NETWORK_Ws_Msg/, filter: /setVideoData/, filterArgIndex: 1, func: (args) => "OC_SET_VIDEO_DATA" //measurements = measurements.concat(JSON.parse(args[0].substr(3)).map( e => }, { callType: /^NETWORK_Ws_Conn/, filter: /ngs-edge/, filterArgIndex: 0, func: (args) => { wsBinaryCounter = 0; return "STREAM_ON_CONNECT" } }, { callType: /^NETWORK_Ws_Open/, filter: /ngs-edge/, filterArgIndex: 0, func: (args) => "STREAM_ON_CONNECTED" }, { callType: /^NETWORK_Ws_Msg/, filter: /"eventType":"onServerInfo"/, filterArgIndex: 1, func: (args) => "STREAM_ON_SERVER_INFO" }, { callType: /^NETWORK_Ws_Msg/, filter: /"onStreamStatus":/, filterArgIndex: 1, func: (args) => "STREAM_ON_STREAM_STATUS" }, { callType: /^NETWORK_Ws_Msg/, filter: /"eventType":"onStreamInfo"/, filterArgIndex: 1, func: (args) => "STREAM_ON_STREAM_INFO" }, { callType: /^NETWORK_Ws_Msg/, filter: /ngs-edge/, filterArgIndex: 0, func: (args) => args[2] && ++wsBinaryCounter == 2 ? "STREAM_RECEIVE_KEYFRAME" : null }, { callType: /^NETWORK_Video_LoadedData/, filter: null, filterArgIndex: null, func: (args) => "STREAM_MEDIA_LOADED_DATA" }, { callType: /^NETWORK_Video_Playing/, filter: null, filterArgIndex: null, func: (args) => "STREAM_MEDIA_PLAYING" } ]; // these steps will be measured and shown when reaches the last step // SCRIPT console.log("MEASUREMENT"); // Measurements const printMeasurements = () => { if (measurements.length < 2 ) { return; } //console.log(measurements); let result = ""; let sumTime = 0; for ( let i = 1; i < measurements.length; i++ ) { let diffTime = measurements[i].time - measurements[i-1].time; sumTime += diffTime; result += measurements[i].msg.padEnd(25) + " " + (diffTime).toString().padStart(4) + "\n"; } console.log('%cMEASUREMENTS\n____________\n' + result + '\n' + 'SUM'.padEnd(25) + ' ' + sumTime.toString().padStart(4), 'background: red; color: #bada55'); } const addMeasurement = (callType, ...args) => { if (enableLogs) { console.log( callType, ...args ); } for ( let i = 0; i < measurementConfig.length; i++ ) { if (measurementConfig[i].callType.test(callType) && (!measurementConfig[i].filter || !measurementConfig[i].filterArgIndex || measurementConfig[i].filter.test(args[measurementConfig[i].filterArgIndex]))) { let pushed = false; if ( (pushed = pushMeasurement( measurementConfig[i].func(args) )) && i == 0 ) { measurements = measurements.slice(-1); // reset if it was the first real action } if ( i == measurementConfig.length - 1 && pushed ) { printMeasurements(); // print if it was the last measurements = []; } } } } // Image listeners const OrigImage = window.Image; window.Image = function(...args) { const img = new OrigImage(...args); img.addEventListener('load', function() {addMeasurement('NETWORK_Img2', img.src);}); return img; }; function handleNewImages(images) { images.forEach(img => { addMeasurement('NETWORK_Img', img.src); }); } const loadedListener = (e) => { addMeasurement('NETWORK_Video_LoadedData', e.target.id); } const playingListener = (e) => { addMeasurement('NETWORK_Video_Playing', e.target.id); } function handleNewVideos(videos) { videos.forEach(video => { //video.style.opacity = 0.1; video.addEventListener("loadeddata", loadedListener); video.addEventListener("playing", playingListener ); }); } const observer = new MutationObserver(mutations => { // Create a MutationObserver to watch for added nodes mutations.forEach(mutation => { if (mutation.addedNodes.length > 0) { mutation.addedNodes.forEach(node => { if (node.tagName === 'IMG') { handleNewImages([node]); } else if (node.querySelectorAll) { // Check for images within added containers const imgs = node.querySelectorAll('img'); if (imgs.length > 0) { handleNewImages(imgs); } } if (node.tagName === 'VIDEO') { handleNewVideos([node]); } else if (node.querySelectorAll) { // Check for images within added containers const videos = node.querySelectorAll('video'); if (videos.length > 0) { handleNewVideos(videos); } } }); } }); }); observer.observe(document.body, { // Start observing the document body for changes childList: true, subtree: true }); // User listeners document.addEventListener("click", (event) => { addMeasurement("NETWORK_User_Click", event.target); }); document.addEventListener("mousedown", (event) => { addMeasurement("NETWORK_User_MouseDown", event.target); }); document.addEventListener("mouseup", (event) => { addMeasurement("NETWORK_User_MouseUp", event.target); }); document.addEventListener("touchstart", (event) => { addMeasurement("NETWORK_User_TouchStart", event.target); }); document.addEventListener("touchend", (event) => { addMeasurement("NETWORK_User_TouchEnd", event.target); }); // Request listeners const origFetch = fetch; window.fetch = (...args) => { addMeasurement("NETWORK_Fetch", ...args); return origFetch(...args); } const origOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (...args) { addMeasurement("NETWORK_Xhr", ...args); return origOpen.apply(this, args); } // Websocket listeners const origWsSend = WebSocket.prototype.send; WebSocket.prototype.send = function(data) { if (data != 3 && !filteredWs.test(this.url)) { addMeasurement("NETWORK_Ws_Send", this.url, data); } if ( !(data instanceof ArrayBuffer) && injectWsSend.pattern.test(data)) { data = data.replace(injectWsSend.pattern, injectWsSend.replacement); } return origWsSend.call(this, data); } const OrigWs = window.WebSocket; window.WebSocket = function (url, protocols) { let ws = new OrigWs(url, protocols); if (!filteredWs.test(url)) { let binaryCount = 0; let filteredMsgCount = 0; addMeasurement("NETWORK_Ws_Conn", url, protocols); ws.addEventListener("open", (event) => { addMeasurement("NETWORK_Ws_Open", url); }); ws.addEventListener("close", (event) => { addMeasurement("NETWORK_Ws_Close", url); }); ws.addEventListener("message", (event) => { const isBinary = event.data instanceof ArrayBuffer; const eventFilterTest = wsEventFilter.r.test(event.data); if (event.data != 2 && ((isBinary && binaryCount++ < binaryWsPackageCountLimit) || (!isBinary && (!eventFilterTest || filteredMsgCount++ < wsEventFilter.limit)))) { addMeasurement("NETWORK_Ws_Msg", url, event.data, isBinary); } } ); } return ws; } })();