Compare commits
10
Commits
main
...
67e872ca57
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67e872ca57 | ||
|
|
6bbe5b5155 | ||
|
|
dacb59437a | ||
|
|
6b9fb5d5ee | ||
|
|
81d04091cd | ||
|
|
aab4726e45 | ||
|
|
3c4972e5a2 | ||
|
|
6b648f5491 | ||
|
|
6ba64413d1 | ||
|
|
ad97310d7e |
No files matched your search
@@ -0,0 +1,3 @@
|
||||
[submodule "peertube/statnerd"]
|
||||
path = peertube/statnerd
|
||||
url = ssh://git@gitea.kobim.cloud:30009/kobim/peertube-collector.git
|
||||
@@ -0,0 +1 @@
|
||||
# TODO
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,5 +1,3 @@
|
||||
selenium
|
||||
beautifulsoup4
|
||||
pymongo
|
||||
pandas
|
||||
matplotlib
|
||||
Submodule
+1
Submodule peertube/statnerd added at 4aa164d148.
@@ -1,17 +0,0 @@
|
||||
## Statistiche per nerd da Peer Tube
|
||||
|
||||
spero funzioni
|
||||
|
||||
per installarlo baste creare un nuovo ambiente e installare le dipendenze con:
|
||||
|
||||
```
|
||||
python -m venv env
|
||||
source .\env\bin\activate
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
per eseguire il tutto basta lanciare:
|
||||
|
||||
```
|
||||
jupyter-lab
|
||||
```
|
||||
@@ -1,190 +0,0 @@
|
||||
import signal
|
||||
import json
|
||||
import time
|
||||
import socket
|
||||
import logging
|
||||
import os
|
||||
from functools import partial
|
||||
from http.server import HTTPServer
|
||||
from utils.PostHandler import Handler
|
||||
from utils.ColoredFormatter import ColoredFormatter
|
||||
from bs4 import BeautifulSoup as bs
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.chrome.options import Options
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver import ActionChains
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as ec
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def setupLogger():
|
||||
logging_format = "[%(asctime)s] (%(levelname)s) %(module)s - %(funcName)s: %(message)s"
|
||||
logging.basicConfig(level=logging.INFO, format=logging_format)
|
||||
(logger := logging.getLogger(__name__)).setLevel(logging.INFO)
|
||||
logger.propagate = False
|
||||
(logger_handler := logging.StreamHandler()).setFormatter(
|
||||
ColoredFormatter(fmt=logging_format)
|
||||
)
|
||||
logger.addHandler(logger_handler)
|
||||
|
||||
def interrupt_handler(signum, driver: webdriver.Chrome):
|
||||
logger.info(f'Handling signal {signum} ({signal.Signals(signum).name}).')
|
||||
|
||||
driver.quit()
|
||||
raise SystemExit
|
||||
|
||||
def setupChromeDriver():
|
||||
logger.log(logging.INFO, 'Setting up Chrome driver.')
|
||||
chrome_options = Options()
|
||||
#chrome_options.add_argument("--headless")
|
||||
chrome_options.add_argument("--no-sandbox")
|
||||
chrome_options.add_argument("--mute-audio")
|
||||
chrome_options.add_argument("--window-size=1280,720")
|
||||
chrome_options.add_argument("--disable-dev-shm-usage")
|
||||
chrome_options.add_argument("--no-default-browser-check")
|
||||
chrome_options.add_argument("--disable-features=WebRtcHideLocalIpsWithMdns")
|
||||
chrome_options.add_argument(f"--load-extension={os.path.abspath(os.path.join(os.path.dirname(__file__), 'webrtc-internals-exporter'))}")
|
||||
chrome_options.add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'})
|
||||
|
||||
driver = webdriver.Chrome(options=chrome_options)
|
||||
#driver = webdriver.Remote(command_executor='http://localhost:4444', options=chrome_options)
|
||||
logger.log(logging.INFO, 'Chrome driver setup complete.')
|
||||
|
||||
return driver
|
||||
|
||||
def saveStats(stats: list):
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
logger.log(logging.DEBUG, f'Saving stats: {json.dumps(stats, indent=4)}')
|
||||
sock.sendto(json.dumps(stats).encode(), ('localhost', 8094))
|
||||
sock.close()
|
||||
logger.log(logging.DEBUG, 'Sent stats to socket.')
|
||||
except socket.error as e:
|
||||
logger.error(f'Got socket error: {e}')
|
||||
|
||||
def downloadStats(driver: webdriver.Chrome, peersDict: dict):
|
||||
html = driver.find_element(By.CLASS_NAME ,'vjs-stats-list').get_attribute('innerHTML')
|
||||
if html is not None:
|
||||
htmlBS = bs(html, 'html.parser')
|
||||
else:
|
||||
raise ValueError("html is None")
|
||||
|
||||
stats = htmlBS.find_all('div', attrs={'style': 'display: block;'})
|
||||
|
||||
playerStats = {
|
||||
stat.div.text: stat.span.text.replace('\u21d3', 'down').replace('down/', 'down /').replace('\u21d1 ', 'up').replace('\u21d1', 'up').replace('\u00b7', '-').strip()
|
||||
for stat in stats
|
||||
}
|
||||
|
||||
keys = list(playerStats.keys())
|
||||
for stat in keys:
|
||||
if 'Viewport / Frames' == stat:
|
||||
viewport, frames = playerStats[stat].split(' / ')
|
||||
width, height = viewport.split('x')
|
||||
height, devicePixelRatio = height.split('*')
|
||||
dropped, total = frames.split(' of ')[0].split()[0], frames.split(' of ')[1].split()[0]
|
||||
playerStats[stat] = {'Width': int(width), 'Height': int(height), 'Pixel ratio': float(devicePixelRatio), 'Frames': {'Dropped': int(dropped), 'Total': int(total)}}
|
||||
|
||||
if 'Codecs' == stat:
|
||||
video, audio = playerStats[stat].split(' / ')
|
||||
playerStats[stat] = {'Video': video, 'Audio': audio}
|
||||
|
||||
if 'Volume' == stat:
|
||||
if ' (' in playerStats[stat]:
|
||||
volume, muted = playerStats[stat].split(' (')
|
||||
playerStats[stat] = {'Volume': int(volume), 'Muted': 'muted' in muted}
|
||||
else:
|
||||
playerStats[stat] = {'Volume': int(playerStats[stat]), 'Muted': False}
|
||||
|
||||
if 'Connection Speed' == stat:
|
||||
speed, unit = playerStats[stat].split()
|
||||
|
||||
speedBytes = int(speed) * (1024 ** {'B/s': 0, 'KB/s': 1, 'MB/s': 2, 'GB/s': 3}[unit])
|
||||
|
||||
playerStats[stat] = {'Speed': int(speedBytes), 'Granularity': 's'}
|
||||
|
||||
if 'Network Activity' == stat:
|
||||
downString, upString = playerStats[stat].split(' / ')
|
||||
|
||||
down, downUnit = downString.replace('down', '').strip().split()
|
||||
up, upUnit = upString.replace('up', '').strip().split()
|
||||
|
||||
downBytes = int(down) * (1024 ** {'B': 0, 'KB': 1, 'MB': 2, 'GB': 3}[downUnit])
|
||||
upBytes = int(up) * (1024 ** {'B': 0, 'KB': 1, 'MB': 2, 'GB': 3}[upUnit])
|
||||
|
||||
playerStats[stat] = {'Down': downBytes, 'Up': upBytes}
|
||||
|
||||
if 'Total Transfered' == stat:
|
||||
downString, upString = playerStats[stat].split(' / ')
|
||||
|
||||
down, downUnit = downString.replace('down', '').strip().split()
|
||||
up, upUnit = upString.replace('up', '').strip().split()
|
||||
|
||||
downBytes = int(down) * (1024 ** {'B': 0, 'KB': 1, 'MB': 2, 'GB': 3}[downUnit])
|
||||
upBytes = int(up) * (1024 ** {'B': 0, 'KB': 1, 'MB': 2, 'GB': 3}[upUnit])
|
||||
|
||||
playerStats[stat] = {'Down': downBytes, 'Up': upBytes}
|
||||
|
||||
if 'Download Breakdown' == stat:
|
||||
server, peer = playerStats[stat].split(' - ')
|
||||
|
||||
server, serverUnit = server.replace('from servers', '').strip().split()
|
||||
peer, peerUnit = peer.replace('from peers', '').strip().split()
|
||||
|
||||
serverBytes = int(server) * (1024 ** {'B': 0, 'KB': 1, 'MB': 2, 'GB': 3}[serverUnit])
|
||||
peerBytes = int(peer) * (1024 ** {'B': 0, 'KB': 1, 'MB': 2, 'GB': 3}[peerUnit])
|
||||
|
||||
playerStats[stat] = {'Server': serverBytes, 'Peers': peerBytes}
|
||||
|
||||
if 'Buffer State' == stat:
|
||||
del(playerStats[stat])
|
||||
|
||||
if 'Live Latency' == stat:
|
||||
latency, edge = playerStats[stat].split(' (from edge: ')
|
||||
latency = sum(int(x) * 60 ** i for i, x in enumerate(reversed([part for part in latency.replace('s', '').split('m') if part])))
|
||||
edge = sum(int(x) * 60 ** i for i, x in enumerate(reversed([part for part in edge.replace('s', '').replace(')', '').split('m') if part])))
|
||||
playerStats[stat] = {'Latency': latency, 'Edge': edge}
|
||||
|
||||
stats = {
|
||||
'player': playerStats,
|
||||
'peers': peersDict,
|
||||
'url': driver.current_url,
|
||||
'timestamp': int(time.time() * 1000),
|
||||
'session': driver.session_id
|
||||
}
|
||||
|
||||
saveStats([stats])
|
||||
|
||||
def setupStats(driver: webdriver.Chrome, url: str):
|
||||
logger.log(logging.INFO, 'Setting up stats.')
|
||||
actions = ActionChains(driver)
|
||||
wait = WebDriverWait(driver, 30, poll_frequency=0.2)
|
||||
|
||||
driver.get(url)
|
||||
|
||||
wait.until(ec.presence_of_element_located((By.CLASS_NAME, 'vjs-big-play-button')))
|
||||
actions.click(driver.find_element(By.CLASS_NAME ,'video-js')).perform()
|
||||
wait.until(ec.visibility_of_element_located((By.CLASS_NAME, 'vjs-control-bar')))
|
||||
actions.context_click(driver.find_element(By.CLASS_NAME ,'video-js')).perform()
|
||||
statsForNerds = driver.find_elements(By.CLASS_NAME ,'vjs-menu-item')
|
||||
actions.click(statsForNerds[-1]).perform()
|
||||
wait.until(ec.text_to_be_present_in_element((By.CLASS_NAME, 'vjs-stats-list'), 'Player'))
|
||||
actions.move_to_element(driver.find_element(By.CLASS_NAME ,'vjs-control-bar')).perform()
|
||||
logger.log(logging.INFO, 'Stats setup complete.')
|
||||
|
||||
return driver
|
||||
|
||||
if __name__ == '__main__':
|
||||
setupLogger()
|
||||
|
||||
driver = setupChromeDriver()
|
||||
|
||||
signal.signal(signal.SIGINT, lambda signum, frame: interrupt_handler(signum, driver))
|
||||
|
||||
setupStats(driver, "https://tube.kobim.cloud/w/iN2T8PmbSb4HJTDA2rV3sg")
|
||||
|
||||
logger.log(logging.INFO, 'Starting server collector.')
|
||||
httpd = HTTPServer(('localhost', 9092), partial(Handler, downloadStats, driver, logger))
|
||||
logger.info('Server collector started.')
|
||||
httpd.serve_forever()
|
||||
@@ -1,33 +0,0 @@
|
||||
[[processors.dedup]]
|
||||
dedup_interval = "600s"
|
||||
|
||||
[[inputs.socket_listener]]
|
||||
service_address = "udp://:8094"
|
||||
data_format = "xpath_json"
|
||||
[[inputs.socket_listener.xpath]]
|
||||
metric_name = "'peertube'"
|
||||
metric_selection = "/*"
|
||||
|
||||
timestamp = "timestamp"
|
||||
timestamp_format = "unix_ms"
|
||||
|
||||
[inputs.socket_listener.xpath.tags]
|
||||
url = "url"
|
||||
session = "session"
|
||||
#id = ??
|
||||
#state = ??
|
||||
|
||||
[inputs.socket_listener.xpath.fields]
|
||||
player = "player"
|
||||
peers = "peers"
|
||||
|
||||
[[outputs.file]]
|
||||
files = ["stdout"]
|
||||
data_format = "json"
|
||||
|
||||
[[outputs.mongodb]]
|
||||
dsn = "mongodb://stats_user:%40z%5EVFhN7q%25vzit@192.168.86.120:27017/?authSource=statistics"
|
||||
database = "statistics"
|
||||
granularity = "seconds"
|
||||
|
||||
# docker run --rm -v .\peertube\statnerd\telegraf.conf:/etc/telegraf/telegraf.conf:ro -p 8094:8094/udp telegraf
|
||||
@@ -1,43 +0,0 @@
|
||||
import logging
|
||||
|
||||
class ColoredFormatter(logging.Formatter):
|
||||
"""Colored formatter for the logging package."""
|
||||
|
||||
def __init__(
|
||||
self, fmt=None, datefmt=None, style="%", validate=True, *, defaults=None
|
||||
):
|
||||
"""Colored formatter for the logging package."""
|
||||
fmt = fmt or "%(levelname)s: %(message)s"
|
||||
super().__init__(fmt, datefmt, style, validate, defaults=defaults)
|
||||
colors = {
|
||||
"red": "\x1b[31;20m",
|
||||
"bold_red": "\x1b[31;1m",
|
||||
"green": "\x1b[32;20m",
|
||||
"bold_green": "\x1b[32;1m",
|
||||
"yellow": "\x1b[33;20m",
|
||||
"bold_yellow": "\x1b[33;1m",
|
||||
"blue": "\x1b[34;20m",
|
||||
"bold_blue": "\x1b[34;1m",
|
||||
"grey": "\x1b[37;20m",
|
||||
"bold_grey": "\x1b[37;1m",
|
||||
"reset": "\x1b[0m",
|
||||
}
|
||||
self._default_formatter = logging.Formatter(fmt)
|
||||
self._formatters = {
|
||||
100: logging.Formatter(colors["bold_blue"] + fmt + colors["reset"]),
|
||||
logging.DEBUG: logging.Formatter(colors["grey"] + fmt + colors["reset"]),
|
||||
logging.INFO: logging.Formatter(colors["green"] + fmt + colors["reset"]),
|
||||
logging.WARNING: logging.Formatter(
|
||||
colors["yellow"] + fmt + colors["reset"]
|
||||
),
|
||||
logging.ERROR: logging.Formatter(colors["red"] + fmt + colors["reset"]),
|
||||
logging.CRITICAL: logging.Formatter(
|
||||
colors["bold_red"] + fmt + colors["reset"]
|
||||
),
|
||||
}
|
||||
|
||||
def format(self, record):
|
||||
"""Override of logging.Formatter.format"""
|
||||
return self._formatters.get(record.levelno, self._default_formatter).format(
|
||||
record
|
||||
)
|
||||
@@ -1,24 +0,0 @@
|
||||
import json
|
||||
import logging
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
def __init__(self, custom_func, driver, logger, *args, **kwargs):
|
||||
self._custom_func = custom_func
|
||||
self.logger = logger
|
||||
self.driver = driver
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def do_POST(self):
|
||||
if self.path == '/webrtc-internals-exporter':
|
||||
content_length = int(self.headers['Content-Length'])
|
||||
post_data = self.rfile.read(content_length)
|
||||
self.logger.log(logging.DEBUG, f"POST request,\nPath: {self.path}\nHeaders:\n{self.headers}\n\nBody:\n{post_data.decode('utf-8')}")
|
||||
self._custom_func(self.driver, json.loads(post_data.decode('utf-8')))
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'POST request received')
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'404 Not Found')
|
||||
@@ -1,37 +0,0 @@
|
||||
# WebRTC Internals Exporter
|
||||
A Chromium browser extension that allows to collect WebRTC stats and export them to a Prometheus PushGateway service.
|
||||
|
||||
## Install
|
||||
|
||||
### Using the Chrome Web Store
|
||||
[Link](https://chromewebstore.google.com/detail/webrtc-internals-exporter/jbgkajlogkmfemdjhiiicelanbipacpa)
|
||||
|
||||
### Using the packed extension
|
||||
Download the `.crx` file from the [releases page](https://github.com/vpalmisano/webrtc-internals-exporter/releases) and drop it
|
||||
into the [chrome://extensions/](chrome://extensions/) page.
|
||||
Alternatively, you can download a `.zip` or `tar.gz` file from the releases page
|
||||
and load the decompressed folder as an unpacked extension.
|
||||
|
||||
Ref. https://developer.chrome.com/docs/extensions/mv3/hosting/
|
||||
|
||||
### From sources
|
||||
Run the `./build.sh` script and load the `build` folder as an unpacked extension
|
||||
in your Chromium browser after enabling the developer mode.
|
||||
|
||||
## Usage
|
||||
1. Visit the extension options page, set the PushGateway URL and, optionally, the username and password.
|
||||
2. Load the page where you want to collect the stats and click on the extension icon to enable the stats collection on that URL (disabled by default).
|
||||
3. The stats will be collected and sent to the PushGateway service. You can use the provided [Grafana dashboard](https://github.com/vpalmisano/webrtc-internals-exporter/tree/main/grafana) to visualize them.
|
||||
|
||||
## Debugging
|
||||
The extension logs are available in the browser console after setting:
|
||||
```js
|
||||
localStorage.setItem("webrtc-internal-exporter:debug", "true")
|
||||
```
|
||||
|
||||
The running PeerConnections objects can be manually inspected using the following
|
||||
command in the browser console:
|
||||
```js
|
||||
> webrtcInternalExporter.peerConnections
|
||||
Map(1) {'b03c3616-3f91-42b5-85df-7dbebefae8bd' => RTCPeerConnection}
|
||||
```
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))l(a);new MutationObserver(a=>{for(const o of a)if(o.type==="childList")for(const i of o.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&l(i)}).observe(document,{childList:!0,subtree:!0});function n(a){const o={};return a.integrity&&(o.integrity=a.integrity),a.referrerPolicy&&(o.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?o.credentials="include":a.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function l(a){if(a.ep)return;a.ep=!0;const o=n(a);fetch(a.href,o)}})();function Ui(e,t){const n=Object.create(null),l=e.split(",");for(let a=0;a<l.length;a++)n[l[a]]=!0;return t?a=>!!n[a.toLowerCase()]:a=>!!n[a]}const Re={},gl=[],Ut=()=>{},Av=()=>!1,Ev=/^on[^a-z]/,lo=e=>Ev.test(e),Ki=e=>e.startsWith("onUpdate:"),Xe=Object.assign,Gi=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},Bv=Object.prototype.hasOwnProperty,Ae=(e,t)=>Bv.call(e,t),be=Array.isArray,hl=e=>oo(e)==="[object Map]",Wu=e=>oo(e)==="[object Set]",we=e=>typeof e=="function",Ge=e=>typeof e=="string",ao=e=>typeof e=="symbol",Ne=e=>e!==null&&typeof e=="object",Uu=e=>(Ne(e)||we(e))&&we(e.then)&&we(e.catch),Ku=Object.prototype.toString,oo=e=>Ku.call(e),$v=e=>oo(e).slice(8,-1),Gu=e=>oo(e)==="[object Object]",Yi=e=>Ge(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,La=Ui(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),io=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Lv=/-(\w)/g,$t=io(e=>e.replace(Lv,(t,n)=>n?n.toUpperCase():"")),Ov=/\B([A-Z])/g,_l=io(e=>e.replace(Ov,"-$1").toLowerCase()),fn=io(e=>e.charAt(0).toUpperCase()+e.slice(1)),Fo=io(e=>e?`on${fn(e)}`:""),qn=(e,t)=>!Object.is(e,t),Oa=(e,t)=>{for(let n=0;n<e.length;n++)e[n](t)},za=(e,t,n)=>{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},Qo=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Fv=e=>{const t=Ge(e)?Number(e):NaN;return isNaN(t)?e:t};let lr;const ei=()=>lr||(lr=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function qi(e){if(be(e)){const t={};for(let n=0;n<e.length;n++){const l=e[n],a=Ge(l)?Nv(l):qi(l);if(a)for(const o in a)t[o]=a[o]}return t}else if(Ge(e)||Ne(e))return e}const Mv=/;(?![^(]*\))/g,Dv=/:([^]+)/,Rv=/\/\*[^]*?\*\//g;function Nv(e){const t={};return e.replace(Rv,"").split(Mv).forEach(n=>{if(n){const l=n.split(Dv);l.length>1&&(t[l[0].trim()]=l[1].trim())}}),t}function Xi(e){let t="";if(Ge(e))t=e;else if(be(e))for(let n=0;n<e.length;n++){const l=Xi(e[n]);l&&(t+=l+" ")}else if(Ne(e))for(const n in e)e[n]&&(t+=n+" ");return t.trim()}const zv="itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly",Hv=Ui(zv);function Yu(e){return!!e||e===""}const jv=e=>Ge(e)?e:e==null?"":be(e)||Ne(e)&&(e.toString===Ku||!we(e.toString))?JSON.stringify(e,qu,2):String(e),qu=(e,t)=>t&&t.__v_isRef?qu(e,t.value):hl(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[l,a])=>(n[`${l} =>`]=a,n),{})}:Wu(t)?{[`Set(${t.size})`]:[...t.values()]}:Ne(t)&&!be(t)&&!Gu(t)?String(t):t;let pt;class Xu{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=pt,!t&&pt&&(this.index=(pt.scopes||(pt.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const n=pt;try{return pt=this,t()}finally{pt=n}}}on(){pt=this}off(){pt=this.parent}stop(t){if(this._active){let n,l;for(n=0,l=this.effects.length;n<l;n++)this.effects[n].stop();for(n=0,l=this.cleanups.length;n<l;n++)this.cleanups[n]();if(this.scopes)for(n=0,l=this.scopes.length;n<l;n++)this.scopes[n].stop(!0);if(!this.detached&&this.parent&&!t){const a=this.parent.scopes.pop();a&&a!==this&&(this.parent.scopes[this.index]=a,a.index=this.index)}this.parent=void 0,this._active=!1}}}function Zi(e){return new Xu(e)}function Wv(e,t=pt){t&&t.active&&t.effects.push(e)}function Uv(){return pt}function bt(e){pt&&pt.cleanups.push(e)}const Ji=e=>{const t=new Set(e);return t.w=0,t.n=0,t},Zu=e=>(e.w&Vn)>0,Ju=e=>(e.n&Vn)>0,Kv=({deps:e})=>{if(e.length)for(let t=0;t<e.length;t++)e[t].w|=Vn},Gv=e=>{const{deps:t}=e;if(t.length){let n=0;for(let l=0;l<t.length;l++){const a=t[l];Zu(a)&&!Ju(a)?a.delete(e):t[n++]=a,a.w&=~Vn,a.n&=~Vn}t.length=n}},Ha=new WeakMap;let Wl=0,Vn=1;const ti=30;let zt;const Un=Symbol(""),ni=Symbol("");class Qi{constructor(t,n=null,l){this.fn=t,this.scheduler=n,this.active=!0,this.deps=[],this.parent=void 0,Wv(this,l)}run(){if(!this.active)return this.fn();let t=zt,n=wn;for(;t;){if(t===this)return;t=t.parent}try{return this.parent=zt,zt=this,wn=!0,Vn=1<<++Wl,Wl<=ti?Kv(this):ar(this),this.fn()}finally{Wl<=ti&&Gv(this),Vn=1<<--Wl,zt=this.parent,wn=n,this.parent=void 0,this.deferStop&&this.stop()}}stop(){zt===this?this.deferStop=!0:this.active&&(ar(this),this.onStop&&this.onStop(),tLine truncated
|
||||
Expected #hex, #hexa, rgb(), rgba(), hsl(), hsla(), object or number`)}function rn(e){const{h:t,s:n,v:l,a}=e,o=s=>{const r=(s+t/60)%6;return l-l*n*Math.max(Math.min(r,4-r,1),0)},i=[o(5),o(3),o(1)].map(s=>Math.round(s*255));return{r:i[0],g:i[1],b:i[2],a}}function cu(e){return rn(ks(e))}function yo(e){if(!e)return{h:0,s:1,v:1,a:1};const t=e.r/255,n=e.g/255,l=e.b/255,a=Math.max(t,n,l),o=Math.min(t,n,l);let i=0;a!==o&&(a===t?i=60*(0+(n-l)/(a-o)):a===n?i=60*(2+(l-t)/(a-o)):a===l&&(i=60*(4+(t-n)/(a-o)))),i<0&&(i=i+360);const s=a===0?0:(a-o)/a,r=[i,s,a];return{h:r[0],s:r[1],v:r[2],a:e.a}}function ud(e){const{h:t,s:n,v:l,a}=e,o=l-l*n/2,i=o===1||o===0?0:(l-o)/Math.min(o,1-o);return{h:t,s:i,l:o,a}}function ks(e){const{h:t,s:n,l,a}=e,o=l+n*Math.min(l,1-l),i=o===0?0:2-2*l/o;return{h:t,s:i,v:o,a}}function cd(e){let{r:t,g:n,b:l,a}=e;return a===void 0?`rgb(${t}, ${n}, ${l})`:`rgba(${t}, ${n}, ${l}, ${a})`}function dd(e){return cd(rn(e))}function $a(e){const t=Math.round(e).toString(16);return("00".substr(0,2-t.length)+t).toUpperCase()}function fd(e){let{r:t,g:n,b:l,a}=e;return`#${[$a(t),$a(n),$a(l),a!==void 0?$a(Math.round(a*255)):""].join("")}`}function vd(e){e=Dh(e);let[t,n,l,a]=gh(e,2).map(o=>parseInt(o,16));return a=a===void 0?a:a/255,{r:t,g:n,b:l,a}}function Mh(e){const t=vd(e);return yo(t)}function md(e){return fd(rn(e))}function Dh(e){return e.startsWith("#")&&(e=e.slice(1)),e=e.replace(/([^0-9a-f])/gi,"F"),(e.length===3||e.length===4)&&(e=e.split("").map(t=>t+t).join("")),e.length!==6&&(e=Yr(Yr(e,6),8,"F")),e}function Rh(e,t){const n=id(ws(e));return n[0]=n[0]+t*10,rd(sd(n))}function Nh(e,t){const n=id(ws(e));return n[0]=n[0]-t*10,rd(sd(n))}function yi(e){const t=At(e);return ws(t)[1]}function zh(e,t){const n=yi(e),l=yi(t),a=Math.max(n,l),o=Math.min(n,l);return(a+.05)/(o+.05)}function gd(e){const t=Math.abs(ru(At(0),At(e)));return Math.abs(ru(At(16777215),At(e)))>Math.min(t,50)?"#fff":"#000"}function O(e,t){return n=>Object.keys(e).reduce((l,a)=>{const i=typeof e[a]=="object"&&e[a]!=null&&!Array.isArray(e[a])?e[a]:{type:e[a]};return n&&a in n?l[a]={...i,default:n[a]}:l[a]=i,t&&!l[a].source&&(l[a].source=t),l},{})}const se=O({class:[String,Array],style:{type:[String,Array,Object],default:null}},"component");function Ot(e){if(e._setup=e._setup??e.setup,!e.name)return e;if(e._setup){e.props=O(e.props??{},e.name)();const t=Object.keys(e.props);e.filterProps=function(l){return Jn(l,t,["class","style"])},e.props._as=String,e.setup=function(l,a){const o=Ps();if(!o.value)return e._setup(l,a);const{props:i,provideSubDefaults:s}=Xh(l,l._as??e.name,o),r=e._setup(i,a);return s(),r}}return e}function H(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!0;return t=>(e?Ot:Dm)(t)}function Hh(e,t){return t.props=e,t}function Gt(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:"div",n=arguments.length>2?arguments[2]:void 0;return H()({name:n??fn($t(e.replace(/__/g,"-"))),props:{tag:{type:String,default:t},...se()},setup(l,a){let{slots:o}=a;return()=>{var i;return Pn(l.tag,{class:[e,l.class],style:l.style},(i=o.default)==null?void 0:i.call(o))}}})}function hd(e){if(typeof e.getRootNode!="function"){for(;e.parentNode;)e=e.parentNode;return e!==document?null:document}const t=e.getRootNode();return t!==document&&t.getRootNode({composed:!0})!==document?null:t}const ia="cubic-bezier(0.4, 0, 0.2, 1)",jh="cubic-bezier(0.0, 0, 0.2, 1)",Wh="cubic-bezier(0.4, 0, 1, 1)";function Je(e,t){const n=gs();if(!n)throw new Error(`[Vuetify] ${e} ${t||"must be called from inside a setup function"}`);return n}function mn(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:"composables";const t=Je(e).type;return Gn((t==null?void 0:t.aliasName)||(t==null?void 0:t.name))}let yd=0,Ra=new WeakMap;function ut(){const e=Je("getUid");if(Ra.has(e))return Ra.get(e);{const t=yd++;return Ra.set(e,t),t}}ut.reset=()=>{yd=0,Ra=new WeakMap};function Vs(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!1;for(;e;){if(t?Uh(e):_s(e))return e;e=e.parentElement}return document.scrollingElement}function Qa(e,t){const n=[];if(t&&e&&!t.contains(e))return n;for(;e&&(_s(e)&&n.push(e),e!==t);)e=e.parentElement;return n}function _s(e){if(!e||e.nodeType!==Node.ELEMENT_NODE)return!1;const t=window.getComputedStyle(e);return t.overflowY==="scroll"||t.overflowY==="auto"&&e.scrollHeight>e.clientHeight}function Uh(e){if(!e||e.nodeType!==Node.ELEMENT_NODE)return!1;const t=window.getComputedStyle(e);return["scroll","auto"].includes(t.overflowY)}function Kh(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:Je("injectSelf");const{provides:n}=t;if(n&&e in n)return n[e]}function Gh(e){for(;e;){if(window.getComputedStyle(e).position==="fixed")return!0;e=e.offsetParent}return!1}function Z(e){const t=Je("useRender");t.render=e}const kl=Symbol.for("vuetify:defaults");function Yh(e){return Q(e)}function Ps(){const e=Pe(kl);if(!e)throw new Error("[Vuetify] Could not find defaults instance");return e}function Ye(e,t){const n=Ps(),l=Q
Line truncated
|
||||
`,...n.map(l=>` ${l};
|
||||
`),`}
|
||||
`)}function yu(e){const t=e.dark?2:1,n=e.dark?1:2,l=[];for(const[a,o]of Object.entries(e.colors)){const i=At(o);l.push(`--v-theme-${a}: ${i.r},${i.g},${i.b}`),a.startsWith("on-")||l.push(`--v-theme-${a}-overlay-multiplier: ${yi(o)>.18?t:n}`)}for(const[a,o]of Object.entries(e.variables)){const i=typeof o=="string"&&o.startsWith("#")?At(o):void 0,s=i?`${i.r}, ${i.g}, ${i.b}`:void 0;l.push(`--v-${a}: ${s??o}`)}return l}const pi={"001":1,AD:1,AE:6,AF:6,AG:0,AI:1,AL:1,AM:1,AN:1,AR:1,AS:0,AT:1,AU:1,AX:1,AZ:1,BA:1,BD:0,BE:1,BG:1,BH:6,BM:1,BN:1,BR:0,BS:0,BT:0,BW:0,BY:1,BZ:0,CA:0,CH:1,CL:1,CM:1,CN:1,CO:0,CR:1,CY:1,CZ:1,DE:1,DJ:6,DK:1,DM:0,DO:0,DZ:6,EC:1,EE:1,EG:6,ES:1,ET:0,FI:1,FJ:1,FO:1,FR:1,GB:1,"GB-alt-variant":0,GE:1,GF:1,GP:1,GR:1,GT:0,GU:0,HK:0,HN:0,HR:1,HU:1,ID:0,IE:1,IL:0,IN:0,IQ:6,IR:6,IS:1,IT:1,JM:0,JO:6,JP:0,KE:0,KG:1,KH:0,KR:0,KW:6,KZ:1,LA:0,LB:1,LI:1,LK:1,LT:1,LU:1,LV:1,LY:6,MC:1,MD:1,ME:1,MH:0,MK:1,MM:0,MN:1,MO:0,MQ:1,MT:0,MV:5,MX:0,MY:1,MZ:0,NI:0,NL:1,NO:1,NP:0,NZ:1,OM:6,PA:0,PE:0,PH:0,PK:0,PL:1,PR:0,PT:0,PY:0,QA:6,RE:1,RO:1,RS:1,RU:1,SA:0,SD:6,SE:1,SG:0,SI:1,SK:1,SM:1,SV:0,SY:6,TH:0,TJ:1,TM:1,TR:1,TT:0,TW:0,UA:1,UM:0,US:0,UY:1,UZ:1,VA:1,VE:0,VI:0,VN:1,WS:0,XK:1,YE:0,ZA:0,ZW:0};function my(e,t){const n=[];let l=[];const a=wd(e),o=kd(e),i=(a.getDay()-pi[t.slice(-2).toUpperCase()]+7)%7,s=(o.getDay()-pi[t.slice(-2).toUpperCase()]+7)%7;for(let r=0;r<i;r++){const u=new Date(a);u.setDate(u.getDate()-(i-r)),l.push(u)}for(let r=1;r<=o.getDate();r++){const u=new Date(e.getFullYear(),e.getMonth(),r);l.push(u),l.length===7&&(n.push(l),l=[])}for(let r=1;r<7-s;r++){const u=new Date(o);u.setDate(u.getDate()+r),l.push(u)}return n.push(l),n}function wd(e){return new Date(e.getFullYear(),e.getMonth(),1)}function kd(e){return new Date(e.getFullYear(),e.getMonth()+1,0)}function gy(e){const t=e.split("-").map(Number);return new Date(t[0],t[1]-1,t[2])}const hy=/([12]\d{3}-([1-9]|0[1-9]|1[0-2])-([1-9]|0[1-9]|[12]\d|3[01]))/;function Vd(e){if(e==null)return new Date;if(e instanceof Date)return e;if(typeof e=="string"){let t;if(hy.test(e))return gy(e);if(t=Date.parse(e),!isNaN(t))return new Date(t)}return null}const bu=new Date(2e3,0,2);function yy(e){const t=pi[e.slice(-2).toUpperCase()];return jt(7).map(n=>{const l=new Date(bu);return l.setDate(bu.getDate()+t+n),new Intl.DateTimeFormat(e,{weekday:"narrow"}).format(l)})}function by(e,t,n){let l={};switch(t){case"fullDateWithWeekday":l={weekday:"long",day:"numeric",month:"long",year:"numeric"};break;case"normalDateWithWeekday":l={weekday:"short",day:"numeric",month:"short"};break;case"keyboardDate":l={day:"2-digit",month:"2-digit",year:"numeric"};break;case"monthAndDate":l={month:"long",day:"numeric"};break;case"monthAndYear":l={month:"long",year:"numeric"};break;case"dayOfMonth":l={day:"numeric"};break;case"shortDate":l={year:"numeric"};break;default:l={timeZone:"UTC",timeZoneName:"short"}}return new Intl.DateTimeFormat(n,l).format(Vd(e)??void 0)}function Sy(e,t){const n=e.toJsDate(t),l=n.getFullYear(),a=qr(String(n.getMonth()+1),2,"0"),o=qr(String(n.getDate()),2,"0");return`${l}-${a}-${o}`}function Cy(e){const[t,n,l]=e.split("-").map(Number);return new Date(t,n-1,l)}function py(e,t){const n=new Date(e);return n.setDate(n.getDate()+t),n}function xy(e,t){const n=new Date(e);return n.setMonth(n.getMonth()+t),n}function wy(e){return e.getFullYear()}function ky(e){return e.getMonth()}function Vy(e){return new Date(e.getFullYear(),0,1)}function _y(e){return new Date(e.getFullYear(),11,31)}function Py(e,t){return xi(e,t[0])&&Ty(e,t[1])}function Iy(e){if(!e||e==null)return!1;const t=new Date(e);return t instanceof Date&&!isNaN(t.getTime())}function xi(e,t){return e.getTime()>t.getTime()}function Ty(e,t){return e.getTime()<t.getTime()}function Su(e,t){return e.getTime()===t.getTime()}function Ay(e,t){return e.getDate()===t.getDate()&&e.getMonth()===t.getMonth()&&e.getFullYear()===t.getFullYear()}function Ey(e,t){return e.getMonth()===t.getMonth()&&e.getFullYear()===t.getFullYear()}function By(e,t,n){const l=new Date(e),a=new Date(t);return n==="month"?l.getMonth()-a.getMonth()+(l.getFullYear()-a.getFullYear())*12:Math.floor((l.getTime()-a.getTime())/(1e3*60*60*24))}function $y(e,t){const n=new Date(e);return n.setFullYear(t),n}function Ly(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate())}function Oy(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate(),23,59,59,999)}class Fy{constructor(t){this.locale=t.locale}date(t){return Vd(t)}toJsDate(t){return t}toISO(t){return Sy(this,t)}parseISO(t){return Cy(t)}addDays(t,n){return py(t,n)}addMonths(t,n){return xy(t,n)}getWeekArray(t){return my(t,this.locale)}startOfMonth(t){return wd(t)}endOfMonth(t){return kd(t)}format(t,n){return by(t,n,this.locale)}isEqual(t,n){return Su(t,n)}isValid(t){return Iy(t)}isWithinRange(t,n){return Py(t,n)}isAfter(t,n){return xi(t,n)}isBefore(t,n){return!xi(t,n)&&!Su(t,n)}isSameDay(t,n){return Ay(t,n)}isSameMonth(t,n){return Ey(t,n)}setYear(t,n){return $y(t,n)}getDiff(t,n,l){return By(t,n,l)}g
Line truncated
|
||||
@@ -1,5 +0,0 @@
|
||||
@keyframes v-shake{59%{margin-left:0}60%,80%{margin-left:2px}70%,90%{margin-left:-2px}}.bg-black{background-color:#000!important;color:#fff!important}.bg-white{background-color:#fff!important;color:#000!important}.bg-transparent{background-color:transparent!important;color:currentColor!important}.bg-red{background-color:#f44336!important;color:#fff!important}.bg-red-lighten-5{background-color:#ffebee!important;color:#000!important}.bg-red-lighten-4{background-color:#ffcdd2!important;color:#000!important}.bg-red-lighten-3{background-color:#ef9a9a!important;color:#000!important}.bg-red-lighten-2{background-color:#e57373!important;color:#fff!important}.bg-red-lighten-1{background-color:#ef5350!important;color:#fff!important}.bg-red-darken-1{background-color:#e53935!important;color:#fff!important}.bg-red-darken-2{background-color:#d32f2f!important;color:#fff!important}.bg-red-darken-3{background-color:#c62828!important;color:#fff!important}.bg-red-darken-4{background-color:#b71c1c!important;color:#fff!important}.bg-red-accent-1{background-color:#ff8a80!important;color:#000!important}.bg-red-accent-2{background-color:#ff5252!important;color:#fff!important}.bg-red-accent-3{background-color:#ff1744!important;color:#fff!important}.bg-red-accent-4{background-color:#d50000!important;color:#fff!important}.bg-pink{background-color:#e91e63!important;color:#fff!important}.bg-pink-lighten-5{background-color:#fce4ec!important;color:#000!important}.bg-pink-lighten-4{background-color:#f8bbd0!important;color:#000!important}.bg-pink-lighten-3{background-color:#f48fb1!important;color:#000!important}.bg-pink-lighten-2{background-color:#f06292!important;color:#fff!important}.bg-pink-lighten-1{background-color:#ec407a!important;color:#fff!important}.bg-pink-darken-1{background-color:#d81b60!important;color:#fff!important}.bg-pink-darken-2{background-color:#c2185b!important;color:#fff!important}.bg-pink-darken-3{background-color:#ad1457!important;color:#fff!important}.bg-pink-darken-4{background-color:#880e4f!important;color:#fff!important}.bg-pink-accent-1{background-color:#ff80ab!important;color:#fff!important}.bg-pink-accent-2{background-color:#ff4081!important;color:#fff!important}.bg-pink-accent-3{background-color:#f50057!important;color:#fff!important}.bg-pink-accent-4{background-color:#c51162!important;color:#fff!important}.bg-purple{background-color:#9c27b0!important;color:#fff!important}.bg-purple-lighten-5{background-color:#f3e5f5!important;color:#000!important}.bg-purple-lighten-4{background-color:#e1bee7!important;color:#000!important}.bg-purple-lighten-3{background-color:#ce93d8!important;color:#fff!important}.bg-purple-lighten-2{background-color:#ba68c8!important;color:#fff!important}.bg-purple-lighten-1{background-color:#ab47bc!important;color:#fff!important}.bg-purple-darken-1{background-color:#8e24aa!important;color:#fff!important}.bg-purple-darken-2{background-color:#7b1fa2!important;color:#fff!important}.bg-purple-darken-3{background-color:#6a1b9a!important;color:#fff!important}.bg-purple-darken-4{background-color:#4a148c!important;color:#fff!important}.bg-purple-accent-1{background-color:#ea80fc!important;color:#fff!important}.bg-purple-accent-2{background-color:#e040fb!important;color:#fff!important}.bg-purple-accent-3{background-color:#d500f9!important;color:#fff!important}.bg-purple-accent-4{background-color:#a0f!important;color:#fff!important}.bg-deep-purple{background-color:#673ab7!important;color:#fff!important}.bg-deep-purple-lighten-5{background-color:#ede7f6!important;color:#000!important}.bg-deep-purple-lighten-4{background-color:#d1c4e9!important;color:#000!important}.bg-deep-purple-lighten-3{background-color:#b39ddb!important;color:#fff!important}.bg-deep-purple-lighten-2{background-color:#9575cd!important;color:#fff!important}.bg-deep-purple-lighten-1{background-color:#7e57c2!important;color:#fff!important}.bg-deep-purple-darken-1{background-color:#5e35b1!important;color:#fff!important}.bg-deep-purple-darken-2{background-color:#512da8!important;color:#fff!important}.bg-deep-purple-darken-3{background-color:#4527a0!important;color:#fff!important}.bg-deep-purple-darken-4{background-color:#311b92!important;color:#fff!important}.bg-deep-purple-accent-1{background-color:#b388ff!important;color:#fff!important}.bg-deep-purple-accent-2{background-color:#7c4dff!important;color:#fff!important}.bg-deep-purple-accent-3{background-color:#651fff!important;color:#fff!important}.bg-deep-purple-accent-4{background-color:#6200ea!important;color:#fff!important}.bg-indigo{background-color:#3f51b5!important;color:#fff!important}.bg-indigo-lighten-5{background-color:#e8eaf6!important;color:#000!important}.bg-indigo-lighten-4{background-color:#c5cae9!important;color:#000!important}.bg-indigo-lighten-3{background-color:#9fa8da!important;color:#fff!important}.bg-indigo-lighten-2{background-color:#7986cb!important;color:#fff!important}.bg-indigo-lighten-1{background-color:#5c6bc0!important;color:#fff!important}.bg-indigo-daLine truncated
|
||||
* ress.css • v2.0.4
|
||||
* MIT License
|
||||
* github.com/filipelinhares/ress
|
||||
*/html{box-sizing:border-box;overflow-y:scroll;-webkit-text-size-adjust:100%;word-break:normal;-moz-tab-size:4;tab-size:4}*,:before,:after{background-repeat:no-repeat;box-sizing:inherit}:before,:after{text-decoration:inherit;vertical-align:inherit}*{padding:0;margin:0}hr{overflow:visible;height:0}details,main{display:block}summary{display:list-item}small{font-size:80%}[hidden]{display:none}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}a{background-color:transparent}a:active,a:hover{outline-width:0}code,kbd,pre,samp{font-family:monospace,monospace}pre{font-size:1em}b,strong{font-weight:bolder}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}input{border-radius:0}[disabled]{cursor:default}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}textarea{overflow:auto;resize:vertical}button,input,optgroup,select,textarea{font:inherit}optgroup{font-weight:700}button{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit],[role=button]{cursor:pointer;color:inherit}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{outline:1px dotted ButtonText}button,html [type=button],[type=reset],[type=submit]{-webkit-appearance:button}button,input,select,textarea{background-color:transparent;border-style:none}select{-moz-appearance:none;-webkit-appearance:none}select::-ms-expand{display:none}select::-ms-value{color:currentColor}legend{border:0;color:inherit;display:table;white-space:normal;max-width:100%}::-webkit-file-upload-button{-webkit-appearance:button;color:inherit;font:inherit}::-ms-clear,::-ms-reveal{display:none}img{border-style:none}progress{vertical-align:baseline}@media screen{[hidden~=screen]{display:inherit}[hidden~=screen]:not(:active):not(:focus):not(:target){position:absolute!important;clip:rect(0 0 0 0)!important}}[aria-busy=true]{cursor:progress}[aria-controls]{cursor:pointer}[aria-disabled=true]{cursor:default}.dialog-transition-enter-active,.dialog-bottom-transition-enter-active,.dialog-top-transition-enter-active{transition-duration:225ms!important;transition-timing-function:cubic-bezier(0,0,.2,1)!important}.dialog-transition-leave-active,.dialog-bottom-transition-leave-active,.dialog-top-transition-leave-active{transition-duration:125ms!important;transition-timing-function:cubic-bezier(.4,0,1,1)!important}.dialog-transition-enter-active,.dialog-transition-leave-active,.dialog-bottom-transition-enter-active,.dialog-bottom-transition-leave-active,.dialog-top-transition-enter-active,.dialog-top-transition-leave-active{transition-property:transform,opacity!important;pointer-events:none}.dialog-transition-enter-from,.dialog-transition-leave-to{transform:scale(.9);opacity:0}.dialog-transition-enter-to,.dialog-transition-leave-from{opacity:1}.dialog-bottom-transition-enter-from,.dialog-bottom-transition-leave-to{transform:translateY(calc(50vh + 50%))}.dialog-top-transition-enter-from,.dialog-top-transition-leave-to{transform:translateY(calc(-50vh - 50%))}.picker-transition-enter-active,.picker-reverse-transition-enter-active,.picker-transition-leave-active,.picker-reverse-transition-leave-active{transition-duration:.3s!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important}.picker-transition-move,.picker-reverse-transition-move{transition-duration:.5s!important;transition-property:transform!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important}.picker-transition-enter-from,.picker-transition-leave-to,.picker-reverse-transition-enter-from,.picker-reverse-transition-leave-to{opacity:0}.picker-transition-leave-from,.picker-transition-leave-active,.picker-transition-leave-to,.picker-reverse-transition-leave-from,.picker-reverse-transition-leave-active,.picker-reverse-transition-leave-to{position:absolute!important}.picker-transition-enter-active,.picker-transition-leave-active,.picker-reverse-transition-enter-active,.picker-reverse-transition-leave-active{transition-property:transform,opacity!important}.picker-transition-enter-active,.picker-transition-leave-active{transition-duration:.3s!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important}.picker-transition-move{transition-duration:.5s!important;transition-property:transform!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important}.picker-transition-enter-from{transform:translateY(100%)}.picker-transition-leave-to{transform:translateY(-100%)}.picker-reverse-transition-enter-active,.picker-reverse-transition-leave-active{transition-duraLine truncated
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
import{g as Re,r as j,o as je,w as T,i as K,a as G,b as Ve,c as w,u as $,d as Pe,e as Ee,f as Y,p as ee,n as te,_ as Ce,h as R,j as re,k as ze,l as _,m as g,q as N,t as U,s as D,v as Se,x as Ae,y as Ie,z as ke}from"./_plugin-vue_export-helper-deb87276.js";function ne(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(s){return Object.getOwnPropertyDescriptor(e,s).enumerable})),n.push.apply(n,r)}return n}function z(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]!=null?arguments[t]:{};t%2?ne(Object(n),!0).forEach(function(r){Te(e,r,n[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):ne(Object(n)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(n,r))})}return e}function Te(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function ae(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:[];return Object.keys(e).reduce((n,r)=>(t.includes(r)||(n[r]=$(e[r])),n),{})}function B(e){return typeof e=="function"}function Le(e){return Pe(e)||Ee(e)}function ce(e,t,n){let r=e;const s=t.split(".");for(let i=0;i<s.length;i++){if(!r[s[i]])return n;r=r[s[i]]}return r}function Z(e,t,n){return w(()=>e.some(r=>ce(t,r,{[n]:!1})[n]))}function oe(e,t,n){return w(()=>e.reduce((r,s)=>{const i=ce(t,s,{[n]:!1})[n]||[];return r.concat(i)},[]))}function de(e,t,n,r){return e.call(r,$(t),$(n),r)}function fe(e){return e.$valid!==void 0?!e.$valid:!e}function Ne(e,t,n,r,s,i,v){let{$lazy:d,$rewardEarly:u}=s,o=arguments.length>7&&arguments[7]!==void 0?arguments[7]:[],m=arguments.length>8?arguments[8]:void 0,p=arguments.length>9?arguments[9]:void 0,h=arguments.length>10?arguments[10]:void 0;const f=j(!!r.value),a=j(0);n.value=!1;const l=T([t,r].concat(o,h),()=>{if(d&&!r.value||u&&!p.value&&!n.value)return;let c;try{c=de(e,t,m,v)}catch(y){c=Promise.reject(y)}a.value++,n.value=!!a.value,f.value=!1,Promise.resolve(c).then(y=>{a.value--,n.value=!!a.value,i.value=y,f.value=fe(y)}).catch(y=>{a.value--,n.value=!!a.value,i.value=y,f.value=!0})},{immediate:!0,deep:typeof t=="object"});return{$invalid:f,$unwatch:l}}function Ue(e,t,n,r,s,i,v,d){let{$lazy:u,$rewardEarly:o}=r;const m=()=>({}),p=w(()=>{if(u&&!n.value||o&&!d.value)return!1;let h=!0;try{const f=de(e,t,v,i);s.value=f,h=fe(f)}catch(f){s.value=f}return h});return{$unwatch:m,$invalid:p}}function De(e,t,n,r,s,i,v,d,u,o,m){const p=j(!1),h=e.$params||{},f=j(null);let a,l;e.$async?{$invalid:a,$unwatch:l}=Ne(e.$validator,t,p,n,r,f,s,e.$watchTargets,u,o,m):{$invalid:a,$unwatch:l}=Ue(e.$validator,t,n,r,f,s,u,o);const c=e.$message;return{$message:B(c)?w(()=>c(ae({$pending:p,$invalid:a,$params:ae(h),$model:t,$response:f,$validator:i,$propertyPath:d,$property:v}))):c||"",$params:h,$pending:p,$invalid:a,$response:f,$unwatch:l}}function Fe(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};const t=$(e),n=Object.keys(t),r={},s={},i={};let v=null;return n.forEach(d=>{const u=t[d];switch(!0){case B(u.$validator):r[d]=u;break;case B(u):r[d]={$validator:u};break;case d==="$validationGroups":v=u;break;case d.startsWith("$"):i[d]=u;break;default:s[d]=u}}),{rules:r,nestedValidators:s,config:i,validationGroups:v}}const Be="__root";function Me(e,t,n,r,s,i,v,d,u){const o=Object.keys(e),m=r.get(s,e),p=j(!1),h=j(!1),f=j(0);if(m){if(!m.$partial)return m;m.$unwatch(),p.value=m.$dirty.value}const a={$dirty:p,$path:s,$touch:()=>{p.value||(p.value=!0)},$reset:()=>{p.value&&(p.value=!1)},$commit:()=>{}};return o.length?(o.forEach(l=>{a[l]=De(e[l],t,a.$dirty,i,v,l,n,s,u,h,f)}),a.$externalResults=w(()=>d.value?[].concat(d.value).map((l,c)=>({$propertyPath:s,$property:n,$validator:"$externalResults",$uid:`${s}-externalResult-${c}`,$message:l,$params:{},$response:null,$pending:!1})):[]),a.$invalid=w(()=>{const l=o.some(c=>$(a[c].$invalid));return h.value=l,!!a.$externalResults.value.length||l}),a.$pending=w(()=>o.some(l=>$(a[l].$pending))),a.$error=w(()=>a.$dirty.value?a.$pending.value||a.$invalid.value:!1),a.$silentErrors=w(()=>o.filter(l=>$(a[l].$invalid)).map(l=>{const c=a[l];return G({$propertyPath:s,$property:n,$validator:l,$uid:`${s}-${l}`,$message:c.$message,$params:c.$params,$response:c.$response,$pending:c.$pending})}).concat(a.$externalResults.value)),a.$errors=w(()=>a.$dirty.value?a.$silentErrors.value:[]),a.$unwatch=()=>o.forEach(l=>{a[l].$unwatch()}),a.$commit=()=>{h.value=!0,f.value=Date.now()},r.set(s,e,a),a):(m&&r.set(s,e,a),a)}function Ge(e,t,n,r,s,i,v){const d=Object.keys(e);return d.length?d.reduce((u,o)=>(u[o]=J({validations:e[o],state:t,key:o,parentKey:n,resultsCache:r,globalConfig:s,instance:i,externalResults:v}),u),{}):{}}function qe(e,t,n){const r=w(()=>[t,n].filter(a=>a).reduce((a,l)=>a.concat(Object.values($(l))),[])),s=w({get(){return e.$dirty.value||(r.value.length?r.value.every(a=>a.$dirty):!1)},set(a){e.$dirty.value=a}}),i=w(()=>{const a=$(e.$silentErrors)||[],Line truncated
|
||||
@@ -1 +0,0 @@
|
||||
.version[data-v-4cce5501]{text-decoration:none}
|
||||
@@ -1,2 +0,0 @@
|
||||
/*! pako 2.1.0 https://github.com/nodeca/pako @license (MIT AND Zlib) */
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).pako={})}(this,(function(t){"use strict";function e(t){let e=t.length;for(;--e>=0;)t[e]=0}const a=256,i=286,n=30,s=15,r=new Uint8Array([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]),o=new Uint8Array([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]),l=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]),h=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),d=new Array(576);e(d);const _=new Array(60);e(_);const f=new Array(512);e(f);const c=new Array(256);e(c);const u=new Array(29);e(u);const w=new Array(n);function m(t,e,a,i,n){this.static_tree=t,this.extra_bits=e,this.extra_base=a,this.elems=i,this.max_length=n,this.has_stree=t&&t.length}let b,g,p;function k(t,e){this.dyn_tree=t,this.max_code=0,this.stat_desc=e}e(w);const v=t=>t<256?f[t]:f[256+(t>>>7)],y=(t,e)=>{t.pending_buf[t.pending++]=255&e,t.pending_buf[t.pending++]=e>>>8&255},x=(t,e,a)=>{t.bi_valid>16-a?(t.bi_buf|=e<<t.bi_valid&65535,y(t,t.bi_buf),t.bi_buf=e>>16-t.bi_valid,t.bi_valid+=a-16):(t.bi_buf|=e<<t.bi_valid&65535,t.bi_valid+=a)},z=(t,e,a)=>{x(t,a[2*e],a[2*e+1])},A=(t,e)=>{let a=0;do{a|=1&t,t>>>=1,a<<=1}while(--e>0);return a>>>1},E=(t,e,a)=>{const i=new Array(16);let n,r,o=0;for(n=1;n<=s;n++)o=o+a[n-1]<<1,i[n]=o;for(r=0;r<=e;r++){let e=t[2*r+1];0!==e&&(t[2*r]=A(i[e]++,e))}},R=t=>{let e;for(e=0;e<i;e++)t.dyn_ltree[2*e]=0;for(e=0;e<n;e++)t.dyn_dtree[2*e]=0;for(e=0;e<19;e++)t.bl_tree[2*e]=0;t.dyn_ltree[512]=1,t.opt_len=t.static_len=0,t.sym_next=t.matches=0},Z=t=>{t.bi_valid>8?y(t,t.bi_buf):t.bi_valid>0&&(t.pending_buf[t.pending++]=t.bi_buf),t.bi_buf=0,t.bi_valid=0},U=(t,e,a,i)=>{const n=2*e,s=2*a;return t[n]<t[s]||t[n]===t[s]&&i[e]<=i[a]},S=(t,e,a)=>{const i=t.heap[a];let n=a<<1;for(;n<=t.heap_len&&(n<t.heap_len&&U(e,t.heap[n+1],t.heap[n],t.depth)&&n++,!U(e,i,t.heap[n],t.depth));)t.heap[a]=t.heap[n],a=n,n<<=1;t.heap[a]=i},D=(t,e,i)=>{let n,s,l,h,d=0;if(0!==t.sym_next)do{n=255&t.pending_buf[t.sym_buf+d++],n+=(255&t.pending_buf[t.sym_buf+d++])<<8,s=t.pending_buf[t.sym_buf+d++],0===n?z(t,s,e):(l=c[s],z(t,l+a+1,e),h=r[l],0!==h&&(s-=u[l],x(t,s,h)),n--,l=v(n),z(t,l,i),h=o[l],0!==h&&(n-=w[l],x(t,n,h)))}while(d<t.sym_next);z(t,256,e)},T=(t,e)=>{const a=e.dyn_tree,i=e.stat_desc.static_tree,n=e.stat_desc.has_stree,r=e.stat_desc.elems;let o,l,h,d=-1;for(t.heap_len=0,t.heap_max=573,o=0;o<r;o++)0!==a[2*o]?(t.heap[++t.heap_len]=d=o,t.depth[o]=0):a[2*o+1]=0;for(;t.heap_len<2;)h=t.heap[++t.heap_len]=d<2?++d:0,a[2*h]=1,t.depth[h]=0,t.opt_len--,n&&(t.static_len-=i[2*h+1]);for(e.max_code=d,o=t.heap_len>>1;o>=1;o--)S(t,a,o);h=r;do{o=t.heap[1],t.heap[1]=t.heap[t.heap_len--],S(t,a,1),l=t.heap[1],t.heap[--t.heap_max]=o,t.heap[--t.heap_max]=l,a[2*h]=a[2*o]+a[2*l],t.depth[h]=(t.depth[o]>=t.depth[l]?t.depth[o]:t.depth[l])+1,a[2*o+1]=a[2*l+1]=h,t.heap[1]=h++,S(t,a,1)}while(t.heap_len>=2);t.heap[--t.heap_max]=t.heap[1],((t,e)=>{const a=e.dyn_tree,i=e.max_code,n=e.stat_desc.static_tree,r=e.stat_desc.has_stree,o=e.stat_desc.extra_bits,l=e.stat_desc.extra_base,h=e.stat_desc.max_length;let d,_,f,c,u,w,m=0;for(c=0;c<=s;c++)t.bl_count[c]=0;for(a[2*t.heap[t.heap_max]+1]=0,d=t.heap_max+1;d<573;d++)_=t.heap[d],c=a[2*a[2*_+1]+1]+1,c>h&&(c=h,m++),a[2*_+1]=c,_>i||(t.bl_count[c]++,u=0,_>=l&&(u=o[_-l]),w=a[2*_],t.opt_len+=w*(c+u),r&&(t.static_len+=w*(n[2*_+1]+u)));if(0!==m){do{for(c=h-1;0===t.bl_count[c];)c--;t.bl_count[c]--,t.bl_count[c+1]+=2,t.bl_count[h]--,m-=2}while(m>0);for(c=h;0!==c;c--)for(_=t.bl_count[c];0!==_;)f=t.heap[--d],f>i||(a[2*f+1]!==c&&(t.opt_len+=(c-a[2*f+1])*a[2*f],a[2*f+1]=c),_--)}})(t,e),E(a,d,t.bl_count)},O=(t,e,a)=>{let i,n,s=-1,r=e[1],o=0,l=7,h=4;for(0===r&&(l=138,h=3),e[2*(a+1)+1]=65535,i=0;i<=a;i++)n=r,r=e[2*(i+1)+1],++o<l&&n===r||(o<h?t.bl_tree[2*n]+=o:0!==n?(n!==s&&t.bl_tree[2*n]++,t.bl_tree[32]++):o<=10?t.bl_tree[34]++:t.bl_tree[36]++,o=0,s=n,0===r?(l=138,h=3):n===r?(l=6,h=3):(l=7,h=4))},I=(t,e,a)=>{let i,n,s=-1,r=e[1],o=0,l=7,h=4;for(0===r&&(l=138,h=3),i=0;i<=a;i++)if(n=r,r=e[2*(i+1)+1],!(++o<l&&n===r)){if(o<h)do{z(t,n,t.bl_tree)}while(0!=--o);else 0!==n?(n!==s&&(z(t,n,t.bl_tree),o--),z(t,16,t.bl_tree),x(t,o-3,2)):o<=10?(z(t,17,t.bl_tree),x(t,o-3,3)):(z(t,18,t.bl_tree),x(t,o-11,7));o=0,s=n,0===r?(l=138,h=3):n===r?(l=6,h=3):(l=7,h=4)}};let F=!1;const L=(t,e,a,i)=>{x(t,0+(i?1:0),3),Z(t),y(t,a),y(t,~a),a&&t.pending_buf.set(t.window.subarray(e,e+a),t.pending),t.pending+=a};var N=(t,e,i,n)=>{let s,r,o=0;t.level>0?(2===t.strm.data_type&&(t.strm.data_type=(t=>{let e,i=4093624447;for(e=0;e<=31;e++,i>>>=1)if(1&i&&0!==t.dyn_ltree[2*e])return 0;if(0!==t.dyn_ltree[18]||0!==t.dyn_ltree[20]||0!==t.dyn_ltree[26])return 1;for(e=32;e<a;e++)if(0!==t.dyn_ltree[2*e])return 1;return 0})(t)),T(t,t.l_desc),T(t,t.d_desc),o=(t=>{let e;for(O(t,t.dyn_ltree,t.l_desc.max_code),O(t,t.dyn_dtree,t.d_desc.max_code),T(t,t.bl_desc),e=18;e>=3&&0===t.blLine truncated
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as y,a as O,h as o,j as c,k as _,l as a,m as t,x,q as w,t as k,z as V}from"./_plugin-vue_export-helper-deb87276.js";const C={class:"version",href:"https://github.com/vpalmisano/webrtc-internals-exporter",target:"_blank",title:"Homepage"},B={__name:"Popup",setup(N){var i;const e=O({version:"0.1.9",error:"",info:"",enabled:!1,origin:"",enabledOrigins:{}});async function d(){const{enabledOrigins:n}=await chrome.storage.sync.get("enabledOrigins");e.enabledOrigins=n,e.enabled=!!e.enabledOrigins[e.origin]}async function p(n){if(console.log("saveOptions",e.origin,n),n)e.enabledOrigins={...e.enabledOrigins,[e.origin]:n};else{const r={...e.enabledOrigins};delete r[e.origin],e.enabledOrigins=r}chrome.storage&&await chrome.storage.sync.set({enabledOrigins:e.enabledOrigins})}return(i=chrome.tabs)==null||i.query({active:!0,currentWindow:!0}).then(n=>{const r=n[0];return chrome.scripting.executeScript({target:{tabId:r.id},function:()=>window.location.origin})}).then(n=>{e.origin=n[0].result}).then(()=>d()).catch(n=>{console.error("Load options error:",n),e.error=`Load options error: ${n.message}`}),(n,r)=>{const u=o("v-app-bar"),g=o("v-alert"),m=o("v-checkbox"),s=o("v-col"),l=o("v-row"),b=o("v-container"),v=o("v-main"),f=o("v-layout");return c(),_(f,null,{default:a(()=>[t(u,{title:"WebRTC Internals Exporter",color:"primary",density:"compact"}),t(v,{class:"d-flex align-center justify-left",style:{"min-width":"20rem"}},{default:a(()=>[t(b,null,{default:a(()=>[t(l,null,{default:a(()=>[t(s,{cols:"12",md:"12"},{default:a(()=>[e.error.length>0?(c(),_(g,{key:0,text:e.error,type:"error"},null,8,["text"])):x("",!0),t(m,{color:"indigo",modelValue:e.enabled,"onUpdate:modelValue":[r[0]||(r[0]=h=>e.enabled=h),p],label:"Enable for "+e.origin,"hide-details":""},null,8,["modelValue","label"])]),_:1})]),_:1}),t(l,null,{default:a(()=>[t(s,{cols:"12",md:"12"},{default:a(()=>[w("a",C,"v"+k(e.version),1)]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})}}},I=y(B,[["__scopeId","data-v-16634bc6"]]);V(I);
|
||||
@@ -1 +0,0 @@
|
||||
.version[data-v-16634bc6]{font-size:smaller;text-decoration:none}
|
||||
@@ -1,373 +0,0 @@
|
||||
/* global chrome, pako */
|
||||
|
||||
function log(...args) {
|
||||
console.log.apply(null, ["[webrtc-internal-exporter:background]", ...args]);
|
||||
}
|
||||
|
||||
log("loaded");
|
||||
|
||||
import "/assets/pako.min.js";
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
url: "http://localhost:9092",
|
||||
username: "",
|
||||
password: "",
|
||||
updateInterval: 2,
|
||||
gzip: false,
|
||||
job: "webrtc-internals-exporter",
|
||||
enabledOrigins: {
|
||||
"https://tube.kobim.cloud": true,
|
||||
},
|
||||
enabledStats: ["data-channel", "local-candidate", "remote-candidate"]
|
||||
};
|
||||
|
||||
const options = {};
|
||||
|
||||
// Handle install/update.
|
||||
chrome.runtime.onInstalled.addListener(async ({ reason }) => {
|
||||
log("onInstalled", reason);
|
||||
if (reason === "install") {
|
||||
await chrome.storage.sync.set(DEFAULT_OPTIONS);
|
||||
} else if (reason === "update") {
|
||||
const options = await chrome.storage.sync.get();
|
||||
await chrome.storage.sync.set({
|
||||
...DEFAULT_OPTIONS,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
await chrome.alarms.create("webrtc-internals-exporter-alarm", {
|
||||
delayInMinutes: 1,
|
||||
periodInMinutes: 1,
|
||||
});
|
||||
});
|
||||
|
||||
async function updateTabInfo(tab) {
|
||||
const tabId = tab.id;
|
||||
const origin = new URL(tab.url || tab.pendingUrl).origin;
|
||||
|
||||
if (options.enabledOrigins && options.enabledOrigins[origin] === true) {
|
||||
const { peerConnectionsPerOrigin } = await chrome.storage.local.get(
|
||||
"peerConnectionsPerOrigin",
|
||||
);
|
||||
const peerConnections =
|
||||
(peerConnectionsPerOrigin && peerConnectionsPerOrigin[origin]) || 0;
|
||||
|
||||
chrome.action.setTitle({
|
||||
title: `WebRTC Internals Exporter\nActive Peer Connections: ${peerConnections}`,
|
||||
tabId,
|
||||
});
|
||||
chrome.action.setBadgeText({ text: `${peerConnections}`, tabId });
|
||||
chrome.action.setBadgeBackgroundColor({ color: "rgb(63, 81, 181)", tabId });
|
||||
} else {
|
||||
chrome.action.setTitle({
|
||||
title: `WebRTC Internals Exporter (disabled)`,
|
||||
tabId,
|
||||
});
|
||||
chrome.action.setBadgeText({ text: "", tabId });
|
||||
}
|
||||
}
|
||||
|
||||
async function optionsUpdated() {
|
||||
const [tab] = await chrome.tabs.query({
|
||||
active: true,
|
||||
lastFocusedWindow: true,
|
||||
});
|
||||
await updateTabInfo(tab);
|
||||
}
|
||||
|
||||
chrome.storage.sync.get().then((ret) => {
|
||||
Object.assign(options, ret);
|
||||
log("options loaded");
|
||||
optionsUpdated();
|
||||
});
|
||||
|
||||
chrome.storage.onChanged.addListener((changes, areaName) => {
|
||||
if (areaName !== "sync") return;
|
||||
|
||||
for (let [key, { newValue }] of Object.entries(changes)) {
|
||||
options[key] = newValue;
|
||||
}
|
||||
log("options changed");
|
||||
optionsUpdated();
|
||||
});
|
||||
|
||||
chrome.tabs.onActivated.addListener(async ({ tabId }) => {
|
||||
try {
|
||||
const tab = await chrome.tabs.get(tabId);
|
||||
await updateTabInfo(tab);
|
||||
} catch (err) {
|
||||
log(`get tab error: ${err.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo) => {
|
||||
if (!changeInfo.url) return;
|
||||
await updateTabInfo({ id: tabId, url: changeInfo.url });
|
||||
});
|
||||
|
||||
chrome.alarms.onAlarm.addListener((alarm) => {
|
||||
if (alarm.name === "webrtc-internals-exporter-alarm") {
|
||||
cleanupPeerConnections().catch((err) => {
|
||||
log(`cleanup peer connections error: ${err.message}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
async function setPeerConnectionLastUpdate({ id, origin }, lastUpdate = 0) {
|
||||
let { peerConnectionsLastUpdate } = await chrome.storage.local.get(
|
||||
"peerConnectionsLastUpdate",
|
||||
);
|
||||
if (!peerConnectionsLastUpdate) {
|
||||
peerConnectionsLastUpdate = {};
|
||||
}
|
||||
if (lastUpdate) {
|
||||
peerConnectionsLastUpdate[id] = { origin, lastUpdate };
|
||||
} else {
|
||||
delete peerConnectionsLastUpdate[id];
|
||||
}
|
||||
await chrome.storage.local.set({ peerConnectionsLastUpdate });
|
||||
|
||||
const peerConnectionsPerOrigin = {};
|
||||
Object.values(peerConnectionsLastUpdate).forEach(({ origin: o }) => {
|
||||
if (!peerConnectionsPerOrigin[o]) {
|
||||
peerConnectionsPerOrigin[o] = 0;
|
||||
}
|
||||
peerConnectionsPerOrigin[o]++;
|
||||
});
|
||||
await chrome.storage.local.set({ peerConnectionsPerOrigin });
|
||||
await optionsUpdated();
|
||||
}
|
||||
|
||||
async function cleanupPeerConnections() {
|
||||
let { peerConnectionsLastUpdate } = await chrome.storage.local.get(
|
||||
"peerConnectionsLastUpdate",
|
||||
);
|
||||
if (
|
||||
!peerConnectionsLastUpdate ||
|
||||
!Object.keys(peerConnectionsLastUpdate).length
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
log(
|
||||
`checking stale peer connections (${
|
||||
Object.keys(peerConnectionsLastUpdate).length
|
||||
} total)`,
|
||||
);
|
||||
const now = Date.now();
|
||||
await Promise.allSettled(
|
||||
Object.entries(peerConnectionsLastUpdate)
|
||||
.map(([id, { origin, lastUpdate }]) => {
|
||||
if (
|
||||
now - lastUpdate >
|
||||
Math.max(2 * options.updateInterval, 30) * 1000
|
||||
) {
|
||||
return { id, origin };
|
||||
}
|
||||
})
|
||||
.filter((ret) => !!ret?.id)
|
||||
.map(({ id, origin }) => {
|
||||
log(`removing stale peer connection metrics: ${id} ${origin}`);
|
||||
return sendData("DELETE", { id, origin });
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// Send data to pushgateway.
|
||||
async function sendData(method, { id, origin }, data) {
|
||||
const { url, username, password, gzip, job } = options;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
if (username && password) {
|
||||
headers.Authorization = "Basic " + btoa(`${username}:${password}`);
|
||||
}
|
||||
if (data && gzip) {
|
||||
headers["Content-Encoding"] = "gzip";
|
||||
data = await pako.gzip(data);
|
||||
}
|
||||
log(`sendData: ${data} \n ${data.length} bytes (gzip: ${gzip}) url: ${url} job: ${job}`);
|
||||
const start = Date.now();
|
||||
const response = await fetch(
|
||||
`${url}/metrics/job/${job}/peerConnectionId/${id}`,
|
||||
{
|
||||
method,
|
||||
headers,
|
||||
body: method === "POST" ? data : undefined,
|
||||
},
|
||||
);
|
||||
|
||||
const stats = await chrome.storage.local.get([
|
||||
"messagesSent",
|
||||
"bytesSent",
|
||||
"totalTime",
|
||||
"errors",
|
||||
]);
|
||||
if (data) {
|
||||
stats.messagesSent = (stats.messagesSent || 0) + 1;
|
||||
stats.bytesSent = (stats.bytesSent || 0) + data.length;
|
||||
stats.totalTime = (stats.totalTime || 0) + Date.now() - start;
|
||||
}
|
||||
if (!response.ok) {
|
||||
stats.errors = (stats.errors || 0) + 1;
|
||||
}
|
||||
await chrome.storage.local.set(stats);
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
throw new Error(`Response status: ${response.status} error: ${text}`);
|
||||
}
|
||||
|
||||
await setPeerConnectionLastUpdate(
|
||||
{ id, origin },
|
||||
method === "POST" ? start : undefined,
|
||||
);
|
||||
|
||||
return response.text();
|
||||
}
|
||||
|
||||
async function sendJsonData(method, { id, origin }, data) {
|
||||
const { url, username, password, gzip, job } = options;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
if (username && password) {
|
||||
headers.Authorization = "Basic " + btoa(`${username}:${password}`);
|
||||
}
|
||||
if (data && gzip) {
|
||||
headers["Content-Encoding"] = "gzip";
|
||||
data = await pako.gzip(data);
|
||||
}
|
||||
log(`sendData: ${data} \n ${data.length} bytes (gzip: ${gzip}) url: ${url} job: ${job}`);
|
||||
const start = Date.now();
|
||||
const response = await fetch(
|
||||
`${url}/${job}`,
|
||||
{
|
||||
method,
|
||||
headers,
|
||||
body: method === "POST" ? data : undefined,
|
||||
},
|
||||
);
|
||||
|
||||
const stats = await chrome.storage.local.get([
|
||||
"messagesSent",
|
||||
"bytesSent",
|
||||
"totalTime",
|
||||
"errors",
|
||||
]);
|
||||
if (data) {
|
||||
stats.messagesSent = (stats.messagesSent || 0) + 1;
|
||||
stats.bytesSent = (stats.bytesSent || 0) + data.length;
|
||||
stats.totalTime = (stats.totalTime || 0) + Date.now() - start;
|
||||
}
|
||||
if (!response.ok) {
|
||||
stats.errors = (stats.errors || 0) + 1;
|
||||
}
|
||||
await chrome.storage.local.set(stats);
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
throw new Error(`Response status: ${response.status} error: ${text}`);
|
||||
}
|
||||
|
||||
await setPeerConnectionLastUpdate(
|
||||
{ id, origin },
|
||||
method === "POST" ? start : undefined,
|
||||
);
|
||||
|
||||
return response.text();
|
||||
}
|
||||
|
||||
const QualityLimitationReasons = {
|
||||
none: 0,
|
||||
bandwidth: 1,
|
||||
cpu: 2,
|
||||
other: 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* sendPeerConnectionStats
|
||||
* @param {string} url
|
||||
* @param {string} id
|
||||
* @param {RTCPeerConnectionState} state
|
||||
* @param {any} values
|
||||
*/
|
||||
async function sendPeerConnectionStats(url, id, state, values) {
|
||||
const origin = new URL(url).origin;
|
||||
|
||||
if (state === "closed") {
|
||||
return sendData("DELETE", { id, origin });
|
||||
}
|
||||
|
||||
let data = "";
|
||||
const sentTypes = new Set();
|
||||
|
||||
values.forEach((value) => {
|
||||
const type = value.type.replace(/-/g, "_");
|
||||
const labels = [`pageUrl="${url}"`];
|
||||
const metrics = [];
|
||||
|
||||
if (value.type === "peer-connection") {
|
||||
labels.push(`state="${state}"`);
|
||||
}
|
||||
|
||||
Object.entries(value).forEach(([key, v]) => {
|
||||
if (typeof v === "number") {
|
||||
metrics.push([key, v]);
|
||||
} else if (typeof v === "object") {
|
||||
Object.entries(v).forEach(([subkey, subv]) => {
|
||||
if (typeof subv === "number") {
|
||||
metrics.push([`${key}_${subkey}`, subv]);
|
||||
}
|
||||
});
|
||||
} else if (
|
||||
key === "qualityLimitationReason" &&
|
||||
QualityLimitationReasons[v] !== undefined
|
||||
) {
|
||||
metrics.push([key, QualityLimitationReasons[v]]);
|
||||
} else if (key === "googTimingFrameInfo") {
|
||||
// TODO
|
||||
} else {
|
||||
labels.push(`${key}="${v}"`);
|
||||
}
|
||||
});
|
||||
|
||||
metrics.forEach(([key, v]) => {
|
||||
const name = `${type}_${key.replace(/-/g, "_")}`;
|
||||
let typeDesc = "";
|
||||
|
||||
if (!sentTypes.has(name)) {
|
||||
typeDesc = `# TYPE ${name} gauge\n`;
|
||||
sentTypes.add(name);
|
||||
}
|
||||
data += `${typeDesc}${name}{${labels.join(",")}} ${v}\n`;
|
||||
});
|
||||
});
|
||||
|
||||
if (data.length > 0) {
|
||||
return sendData("POST", { id, origin }, data + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.event === "peer-connection-stats") {
|
||||
const { url, id, state, values } = message.data;
|
||||
|
||||
sendData("POST", { id, origin: new URL(url).origin }, JSON.stringify(message.data))
|
||||
.then(() => {
|
||||
sendResponse({});
|
||||
})
|
||||
.catch((err) => {
|
||||
sendResponse({ error: err.message });
|
||||
});
|
||||
} else if (message.event === "peer-connections-stats") {
|
||||
const { stats } = message.data;
|
||||
|
||||
sendJsonData("POST", { id: "all", origin: "all" }, JSON.stringify(message.data))
|
||||
} else {
|
||||
sendResponse({ error: "unknown event" });
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
@@ -1,123 +0,0 @@
|
||||
/* global chrome */
|
||||
|
||||
if (window.location.protocol.startsWith("http")) {
|
||||
const log = (...args) => {
|
||||
try {
|
||||
if (localStorage.getItem("webrtc-internal-exporter:debug") === "true") {
|
||||
console.log.apply(null, [
|
||||
"[webrtc-internal-exporter:content-script]",
|
||||
...args,
|
||||
]);
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore localStorage errors.
|
||||
}
|
||||
};
|
||||
|
||||
const injectScript = (file_path) => {
|
||||
const head = document.querySelector("head");
|
||||
const script = document.createElement("script");
|
||||
script.setAttribute("type", "text/javascript");
|
||||
script.setAttribute("src", file_path);
|
||||
head.appendChild(script);
|
||||
};
|
||||
|
||||
setTimeout(() => injectScript(chrome.runtime.getURL("override.js")));
|
||||
|
||||
// Handle options.
|
||||
const options = {
|
||||
url: "",
|
||||
enabled: false,
|
||||
updateInterval: 2000,
|
||||
enabledStats: [],
|
||||
};
|
||||
|
||||
const sendOptions = () => {
|
||||
window.postMessage({
|
||||
event: "webrtc-internal-exporter:options",
|
||||
options,
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
chrome.storage.sync
|
||||
.get(["url", "enabledOrigins", "updateInterval", "enabledStats"])
|
||||
.then((ret) => {
|
||||
log(`options loaded:`, ret);
|
||||
options.url = ret.url || "";
|
||||
options.enabled =
|
||||
ret.enabledOrigins &&
|
||||
ret.enabledOrigins[window.location.origin] === true;
|
||||
options.updateInterval = (ret.updateInterval || 2) * 1000;
|
||||
options.enabledStats = Object.values(ret.enabledStats || {});
|
||||
sendOptions();
|
||||
});
|
||||
|
||||
chrome.storage.onChanged.addListener((changes, area) => {
|
||||
if (area !== "sync") return;
|
||||
|
||||
let changed = false;
|
||||
for (let [key, { newValue }] of Object.entries(changes)) {
|
||||
if (key === "url") {
|
||||
options.url = newValue;
|
||||
changed = true;
|
||||
} else if (key === "enabledOrigins") {
|
||||
options.enabled = newValue[window.location.origin] === true;
|
||||
changed = true;
|
||||
} else if (key === "updateInterval") {
|
||||
options.updateInterval = newValue * 1000;
|
||||
changed = true;
|
||||
} else if (key === "enabledStats") {
|
||||
options.enabledStats = Object.values(newValue);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
log(`options changed:`, options);
|
||||
sendOptions();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle stats messages.
|
||||
window.addEventListener("message", async (message) => {
|
||||
const { event, url, id, state, values, stats } = message.data;
|
||||
if (event === "webrtc-internal-exporter:ready") {
|
||||
sendOptions();
|
||||
} else if (event === "webrtc-internal-exporter:peer-connection-stats") {
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
event: "peer-connection-stats",
|
||||
data: {
|
||||
url,
|
||||
id,
|
||||
state,
|
||||
values,
|
||||
},
|
||||
});
|
||||
if (response.error) {
|
||||
log(`error: ${response.error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
log(`error: ${error.message}`);
|
||||
}
|
||||
} else if (event === "webrtc-internal-exporter:peer-connections-stats") {
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
event: "peer-connections-stats",
|
||||
data: stats,
|
||||
});
|
||||
if (response.error) {
|
||||
log(`error: ${response.error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
log(`error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[webrtc-internal-exporter:content-script] error: ${error.message}`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 718 B |
Binary file not shown.
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"name": "WebRTC Internals Exporter",
|
||||
"description": "WebRTC Internals Exporter",
|
||||
"author": "Vittorio Palmisano",
|
||||
"version": "0.1.9",
|
||||
"manifest_version": 3,
|
||||
"icons": {
|
||||
"16": "images/icon16.png",
|
||||
"48": "images/icon48.png",
|
||||
"128": "images/icon128.png"
|
||||
},
|
||||
"permissions": [
|
||||
"storage",
|
||||
"activeTab",
|
||||
"tabs",
|
||||
"scripting",
|
||||
"alarms"
|
||||
],
|
||||
"host_permissions": [],
|
||||
"action": {
|
||||
"default_title": "WebRTC Internals Exporter",
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
"options_ui": {
|
||||
"page": "options.html",
|
||||
"open_in_tab": true
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
"https://*/*",
|
||||
"http://*/*"
|
||||
],
|
||||
"js": [
|
||||
"content-script.js"
|
||||
],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true,
|
||||
"match_about_blank": true
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "background.js",
|
||||
"type": "module"
|
||||
},
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": [
|
||||
"override.js"
|
||||
],
|
||||
"matches": [
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>WebRTC Internals Exporter</title>
|
||||
<script type="module" crossorigin src="/assets/options-8c2aaa1b.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-deb87276.js">
|
||||
<link rel="stylesheet" href="/assets/_plugin-vue_export-helper.css">
|
||||
<link rel="stylesheet" href="/assets/options.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,135 +0,0 @@
|
||||
function log(...args) {
|
||||
console.log.apply(null, ["[webrtc-internal-exporter:override]", ...args]);
|
||||
}
|
||||
|
||||
log("Override RTCPeerConnection.");
|
||||
|
||||
class WebrtcInternalExporter {
|
||||
peerConnections = new Map();
|
||||
|
||||
url = "";
|
||||
enabled = false;
|
||||
updateInterval = 2000;
|
||||
enabledStats = [];
|
||||
|
||||
constructor() {
|
||||
window.addEventListener("message", async (message) => {
|
||||
const { event, options } = message.data;
|
||||
if (event === "webrtc-internal-exporter:options") {
|
||||
log("options updated:", options);
|
||||
Object.assign(this, options);
|
||||
}
|
||||
});
|
||||
|
||||
window.postMessage({ event: "webrtc-internal-exporter:ready" });
|
||||
this.collectAllStats();
|
||||
}
|
||||
|
||||
randomId() {
|
||||
return (
|
||||
window.crypto?.randomUUID() || (2 ** 64 * Math.random()).toString(16)
|
||||
);
|
||||
}
|
||||
|
||||
add(pc) {
|
||||
const id = this.randomId();
|
||||
this.peerConnections.set(id, pc);
|
||||
pc.addEventListener("connectionstatechange", () => {
|
||||
if (pc.connectionState === "closed") {
|
||||
this.peerConnections.delete(id);
|
||||
}
|
||||
});
|
||||
//this.collectAndPostStats(id);
|
||||
}
|
||||
|
||||
async collectAndPostSingleStat(id) {
|
||||
const stats = await this.collectStats(id, this.collectAndPostSingleStat);
|
||||
if (Object.keys(stats).length === 0 || !stats) return;
|
||||
|
||||
window.postMessage(
|
||||
{
|
||||
event: "webrtc-internal-exporter:peer-connection-stats",
|
||||
...stats,
|
||||
},
|
||||
[stats],
|
||||
);
|
||||
}
|
||||
|
||||
async collectAllStats() {
|
||||
const stats = [];
|
||||
|
||||
for (const [id, pc] of this.peerConnections) {
|
||||
if (this.url && this.enabled && pc.connectionState === "connected") {
|
||||
const pcStats = await this.collectStats(id, pc);
|
||||
stats.push(pcStats);
|
||||
}
|
||||
}
|
||||
|
||||
//if (stats.length !== 0) {
|
||||
window.postMessage(
|
||||
{
|
||||
event: "webrtc-internal-exporter:peer-connections-stats",
|
||||
stats,
|
||||
},
|
||||
[stats],
|
||||
);
|
||||
|
||||
log(`Stats collected:`, stats);
|
||||
//}
|
||||
|
||||
setTimeout(this.collectAllStats.bind(this), this.updateInterval);
|
||||
return stats;
|
||||
}
|
||||
|
||||
async collectStats(id, pc, binding) {
|
||||
var completeStats = {};
|
||||
|
||||
if (!pc) {
|
||||
pc = this.peerConnections.get(id);
|
||||
if (!pc) return;
|
||||
}
|
||||
|
||||
if (this.url && this.enabled && pc.connectionState === "connected") {
|
||||
try {
|
||||
const stats = await pc.getStats();
|
||||
const values = [...stats.values()].filter(
|
||||
(v) =>
|
||||
["peer-connection", ...this.enabledStats].indexOf(v.type) !== -1
|
||||
);
|
||||
|
||||
completeStats = {
|
||||
url: window.location.href,
|
||||
id,
|
||||
state: pc.connectionState,
|
||||
values,
|
||||
};
|
||||
} catch (error) {
|
||||
log(`collectStats error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (pc.connectionState === "closed") {
|
||||
this.peerConnections.delete(id);
|
||||
} else {
|
||||
if (binding) {
|
||||
setTimeout(binding.bind(this), this.updateInterval, id);
|
||||
}
|
||||
}
|
||||
|
||||
return completeStats;
|
||||
}
|
||||
}
|
||||
|
||||
const webrtcInternalExporter = new WebrtcInternalExporter();
|
||||
|
||||
window.RTCPeerConnection = new Proxy(window.RTCPeerConnection, {
|
||||
construct(target, argumentsList) {
|
||||
log(`RTCPeerConnection`, argumentsList);
|
||||
|
||||
const pc = new target(...argumentsList);
|
||||
|
||||
webrtcInternalExporter.add(pc);
|
||||
|
||||
return pc;
|
||||
},
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>WebRTC Internals Exporter</title>
|
||||
<script type="module" crossorigin src="/assets/popup-7cc154e5.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-deb87276.js">
|
||||
<link rel="stylesheet" href="/assets/_plugin-vue_export-helper.css">
|
||||
<link rel="stylesheet" href="/assets/popup.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user