Compare commits

..

41 Commits

Author SHA1 Message Date
d7ca87d9e5 feat: add xurl package to allow long URL breaks in the document
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 4m0s
2025-04-02 17:13:27 +02:00
f5bb59f433 feat: add new references to bibliography, fix lexical errors
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m55s
2025-04-01 15:13:26 +02:00
09745960cc fix: update academic year in title page of thesis document
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 4m7s
2025-03-31 20:17:23 +02:00
ae3023d688 feat: add dedication and acknowledgments sections to the thesis document
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 4m42s
2025-03-31 18:19:35 +02:00
6f4cd93528 feat: add new images for unicast, multicast, broadcast, and P2P network, and update bibliography with relevant citations
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 5m26s
2025-03-31 16:08:20 +02:00
f9e8345eba fix: update P2P traffic statistics and improve clarity in the report
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m25s
2025-03-30 19:58:41 +02:00
d63589fe73 feat: add player and webrtc statistics aggregation scripts, update subproject reference, and include latency images 2025-03-30 19:55:01 +02:00
28b84eaa77 chore: update subproject commit reference in statnerd
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m29s
2025-03-30 16:13:33 +02:00
27c6369014 refactor: remove outdated bibliography entry and reorganize bibliography sections 2025-03-29 19:26:50 +01:00
f01dd6fd19 fix: remove unnecessary line breaks and add citations in peer selection algorithm section
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m11s
2025-03-28 19:12:39 +01:00
8be1ee8d40 chore: fixed mispellings, added small peertube plugin section, color formatted code
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m7s
2025-03-28 19:08:38 +01:00
b09c3c50f2 feat: add small report on work done
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m17s
2025-03-28 18:36:03 +01:00
775d7ceb6f feat: add images for NAT traversal and STUN concepts, and update bibliography with new references
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m17s
2025-03-25 21:56:42 +01:00
0d4bf28092 feat: chapter 3 init with images and biblio
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m14s
2025-03-24 18:26:49 +01:00
b8df6b773c feat: add introduction to PeerTube architecture and technologies chapter
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m18s
2025-03-24 16:06:20 +01:00
4fd838f401 feat: enhance test scenarios description and update statistics collected for PeerTube use cases
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m10s
2025-03-23 19:34:30 +01:00
26a7c9d733 feat: add JSON example for WebRTC Internals Exporter metrics and enhance data collection description
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m11s
2025-03-22 23:37:13 +01:00
bec17dae38 feat: add new mermaid diagram and update bibliography with additional references
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 4m4s
2025-03-22 21:41:51 +01:00
0c5b36fb61 wip: collector section
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m7s
2025-03-20 22:06:14 +01:00
29cb7105af chore: refactor double quotes
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m11s
2025-03-20 21:25:00 +01:00
ccc298f328 feat: update submodule URL and enhance system architecture description in Tesi.tex
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m7s
2025-03-20 19:17:02 +01:00
968ef8e454 feat: add WebRTC Internals Exporter and Hetzner cloud section with detailed explanations and images; update bibliography with new reference 2025-03-20 18:56:00 +01:00
403d2eb03d feat: enhance Selenium section with detailed explanations, images, and new references; update bibliography
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m3s
2025-03-20 16:24:12 +01:00
f63962516f feat: add Python section with overview, example code, and logo; update bibliography with Python reference 2025-03-20 15:28:30 +01:00
216d9ec624 feat: add MongoDB section with detailed explanations and images; update bibliography with new references
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m1s
2025-03-19 22:57:12 +01:00
a4d9689e2d feat: telegraf sectin 2025-03-19 21:10:47 +01:00
4f44cf5c0f feat: update bibliography with new references and enhance Docker section in Tesi.tex
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 2m55s
2025-03-18 22:49:08 +01:00
686a272064 feat: update bibliography with new references and chapter 4.0
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 2m52s
2025-03-18 18:22:21 +01:00
c37719b88f feat: init for refactoring entire document to change base narrative
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 2m39s
2025-03-16 23:34:49 +01:00
b57abd5738 fix: update .gitignore to include all bibliography auxiliary files and correct student term in use case
Some checks failed
Build LaTeX Document / build_latex (push) Has been cancelled
2025-03-15 21:38:02 +01:00
89ca38cd85 feat: replace pdflatex build steps with latexmk for improved document compilation
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m27s
2025-03-14 17:52:42 +01:00
b485d2ada4 fix: standardize month formatting in bibliography and update PDF/A compliance settings
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 2m47s
2025-03-12 00:05:12 +01:00
be79e142f1 chore: update subproject commit reference in statnerd
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 3m33s
2025-03-06 23:21:57 +01:00
c6b98ac9c6 feat: enhance GitHub Actions workflow for LaTeX document processing and add Italian hyphenation patterns 2025-03-06 23:21:57 +01:00
ba97edff35 chore: update subproject commit reference in statnerd and update MiKTex debian source version 2025-03-06 23:21:57 +01:00
374b1bcca5 chore: update subproject commit reference in statnerd and update notebook 2025-03-06 23:21:57 +01:00
ef6d8cf903 chore: update .gitignore to include Node.js files and update subproject commit reference in statnerd 2025-03-06 23:21:57 +01:00
fda9b5f2db chore: update copyright year in LICENSE and subproject commit reference in statnerd 2025-03-06 23:21:56 +01:00
0bb5db4e98 feat: update MiKTeX package database and remove latest release before new release 2025-03-06 23:21:38 +01:00
a74f6093ec feat: Dockerize collector / CI-CD for PDF release (#1)
All checks were successful
Build LaTeX Document / build_latex (push) Successful in 1m35s
Reviewed-on: #1
Co-authored-by: Mirko Milovanovic <mir_ko@me.com>
Co-committed-by: Mirko Milovanovic <mir_ko@me.com>
2025-02-09 23:17:27 +00:00
0a25f67dd3 Add ipykernel to requirements.txt for Jupyter notebook support 2025-02-05 16:35:12 +01:00
75 changed files with 2642 additions and 1671 deletions

55
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: Build LaTeX Document
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
TEX_NAME: Tesi
PDF_NAME: Tesi.pdf
jobs:
build_latex:
runs-on: ubuntu-latest
steps:
- name: Set up Git repository
uses: actions/checkout@v4.2.2
- name: Install MiKTeX
run: |
curl -fsSL https://miktex.org/download/key | tee /usr/share/keyrings/miktex-keyring.asc > /dev/null
echo "deb [signed-by=/usr/share/keyrings/miktex-keyring.asc] https://miktex.org/download/ubuntu jammy universe" | sudo tee /etc/apt/sources.list.d/miktex.list
apt-get update
apt-get dist-upgrade -y
apt-get install miktex -y
miktexsetup --shared=yes finish
- name: Update MiKTeX package database
run: miktex packages update-package-database && miktex packages update
- name: Enable automatic package installation
run: initexmf --set-config-value=[MPM]AutoInstall=yes
- name: Build LaTeX document using latexmk
run: latexmk -pdf -interaction=nonstopmode -halt-on-error ${{ env.TEX_NAME }}.tex
- name: Check for PDF artifact
run: file ${{ env.PDF_NAME }} || grep -q ' PDF '
- name: Delete latest release and tag
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
curl -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" -X DELETE https://gitea.kobim.cloud/api/v1/repos/${{ github.repository }}/releases/tags/latest
curl -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" -X DELETE https://gitea.kobim.cloud/api/v1/repos/${{ github.repository }}/tags/latest
- name: Release PDF artifact
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: softprops/action-gh-release@v2
with:
files: ${{ env.PDF_NAME }}
tag_name: latest
body: |
PDF document built from latest LaTeX source.
token: ${{ secrets.RELEASE_TOKEN }}

12
.gitignore vendored
View File

@@ -28,8 +28,8 @@ Tesi.pdf
.vscode
## Bibliography auxiliary files (bibtex/biblatex/biber):
*.bbl
*.bcf
*.bbl*
*.bcf*
*.blg
*-blx.aux
*-blx.bib
@@ -295,4 +295,10 @@ env/
__pycache__/
test/
venv/
.venv/
.venv/
# Node.js
**/node_modules/
npm-debug.log
yarn-error.log
yarn-debug.log*

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "peertube/statnerd"]
path = peertube/statnerd
url = ssh://git@ssh.kobim.cloud:50697/kobim/peertube-collector.git

View File

@@ -1,3 +1,42 @@
@book{10.5555/1593511,
author = {Van Rossum, Guido and Drake, Fred L.},
title = {Python 3 Reference Manual},
year = {2009},
isbn = {1441412697},
publisher = {CreateSpace},
address = {Scotts Valley, CA},
abstract = {PYTHON 3 Reference Manual (Python Documentation MANUAL Part 2).Python is an easy to learn object-oriented programming language, which combines power with clear syntax. It has modules, classes, exceptions, very high level data types, and dynamic typing. Python is free software. It can be used with GNU (GNU/Linux), Unix, Microsoft Windows and many other systems.This is a printed softcover copy of the official Python documentation from the latest Python 3.0 distribution. For each copy sold $1 will be donated to the Python Software Foundation by the publisher.This book is part of a brand new six-part series of Python documentation books. Searching for "Python Documentation Manual" will show all six available books.ABOUT THE AUTHOR: Guido van Rossum, is the inventor of Python. Fred L. Drake, Jr. is the official editor of the Python documentation.}
}
@book{10.5555/2544030,
author = {Chodorow, Kristina},
title = {MongoDB: The Definitive Guide},
year = {2013},
isbn = {1449344682},
publisher = {O'Reilly Media, Inc.},
abstract = {Manage the huMongous amount of data collected through your web application with MongoDb. This authoritative introductionwritten by a core contributor to the projectshows you the many advantages of using document-oriented databases, and demonstrates how this reliable, high-performance system allows for almost infinite horizontal scalability. This updated second edition provides guidance for database developers, advanced configuration for system administrators, and an overview of the concepts and use cases for other people on your project. Ideal for NoSql newcomers and experienced MongoDb users alike, this guide provides numerous real-world schema design examples.Get started with MongoDb core concepts and vocabulary Perform basic write operations at different levels of safety and speed Create complex queries, with options for limiting, skipping, and sorting results Design an application that works well with MongoDb Aggregate data, including counting, finding distinct values, grouping documents, and using MapReduce Gather and interpret statistics about your collections and databases Set up replica sets and automatic failover in MongoDb Use sharding to scale horizontally, and learn how it impacts applications Delve into monitoring, security and authentication, backup/restore, and other administrative tasks}
}
@inproceedings{10617017,
author = {Pant, Sakshi and Yadav, Er. Narinder and Milan and Sharma, Monnie and Bedi, Yash and Raturi, Anshuman},
booktitle = {2024 International Conference on Knowledge Engineering and Communication Systems (ICKECS)},
title = {Web Scraping Using Beautiful Soup},
year = {2024},
volume = {1},
number = {},
pages = {1-6},
keywords = {Knowledge engineering;Ethics;Communication systems;Data acquisition;Data collection;Information retrieval;Libraries;Web scraping;Beautiful Soup;Python;data acquisition;HTML parsing;case studies;ethical considerations},
doi = {10.1109/ICKECS61492.2024.10617017}
}
@misc{acestreamAnnouncementStream,
author = {},
title = {{A}nnouncement! {A}{C}{E} {S}tream; {N}ew era of {T}{V} and {I}nternet broadcasting --- oldforum.acestream.media},
url = {http://oldforum.acestream.media/index.php?topic=1479.0},
year = {},
note = {[Accessed 28-09-2023]}
}
@misc{activitypubActivityPubRocks,
author = {},
title = {{A}ctivity{P}ub {R}ocks! --- activitypub.rocks},
@@ -6,7 +45,7 @@
note = {[Accessed 04-Apr-2023]}
}
@misc{ai4businessDataTroppo,
@misc{ai4businessDataTroppo,
author = {Federica Maria Rita Livelli},
title = {{A}{I} e big data, troppo potere nelle mani di pochi: il dibattito - {A}{I}4{B}usiness --- ai4business.it},
howpublished = {\url{https://www.ai4business.it/intelligenza-artificiale/ai-e-big-data-favoriscono-la-concentrazione-del-potere/}},
@@ -14,7 +53,23 @@
note = {[Accessed 01-Sep-2022]}
}
@misc{ARPANET_2022,
@misc{aiknowTimeSeries,
author = {Angelo Lazzari},
title = {{T}ime series: una piccola, ma dettagliata, introduzione - {A}{I}{K}now --- aiknow.io},
howpublished = {\url{https://www.aiknow.io/cosa-sono-le-time-series/?doing_wp_cron=1742414242.9129240512847900390625}},
year = {},
note = {[Accessed 19-03-2025]}
}
@misc{archiveStream,
author = {},
title = {{A}ce {S}tream --- web.archive.org},
url = {https://web.archive.org/web/20180618052904/http://info.acestream.org/#/about/acestream},
year = {},
note = {[Accessed 28-09-2023]}
}
@misc{ARPANET_2022,
title = {{ARPANET} --- {W}ikipedia{,} The Free Encyclopedia},
rights = {Creative Commons Attribution-ShareAlike License},
url = {https://en.wikipedia.org/w/index.php?title=ARPANET&oldid=1105956998},
@@ -23,7 +78,7 @@
note = {Page Version ID: 1105956998},
journal = {Wikipedia},
year = {2022},
month = {Aug},
month = {8},
language = {en}
}
@@ -49,6 +104,29 @@
journal = {{IEEE} Communications Surveys {\&} Tutorials}
}
@misc{AzureFunctions,
author = {Franco Palermo},
title = {{A}zure {F}unctions with {D}ocker --- franco.palermo812},
howpublished = {\url{https://medium.com/@franco.palermo812/azure-functions-with-docker-47e22866330}},
year = {},
note = {[Accessed 18-03-2025]}
}
@article{Blum2021,
title = {WebRTC - Realtime Communication for the Open Web Platform: What was once a way to bring audio and video to the web has expanded into more use cases we could ever imagine.},
volume = {19},
issn = {1542-7749},
url = {http://dx.doi.org/10.1145/3454122.3457587},
doi = {10.1145/3454122.3457587},
number = {1},
journal = {Queue},
publisher = {Association for Computing Machinery (ACM)},
author = {Blum, Niklas and Lachapelle, Serge and Alvestrand, Harald},
year = {2021},
month = feb,
pages = {7793}
}
@article{Budhkar2019,
doi = {10.1007/s12083-019-00755-x},
url = {https://doi.org/10.1007/s12083-019-00755-x},
@@ -63,6 +141,110 @@
journal = {Peer-to-Peer Networking and Applications}
}
@misc{framablogPeerTubeOut,
author = {},
title = {{P}eer{T}ube v6 is out, and powered by your ideas ! --- framablog.org},
howpublished = {\url{https://framablog.org/2023/11/28/peertube-v6-is-out-and-powered-by-your-ideas/\#-and-there-s-always-more-}},
year = {},
note = {[Accessed 18-03-2025]}
}
@misc{framablogPeerTubeV7,
author = {},
title = {{P}eer{T}ube v7 : offer a complete makeover to your video platform ! --- framablog.org},
howpublished = {\url{https://framablog.org/2024/12/17/peertube-v7-offer-a-complete-makeover-to-your-video-platform/}},
year = {},
note = {[Accessed 18-03-2025]}
}
@misc{framagitFramasoftPeerTube,
author = {},
title = {{F}ramasoft / {P}eer{T}ube / {S}elenium stack · {G}it{L}ab --- framagit.org},
howpublished = {\url{https://framagit.org/framasoft/peertube/selenium-stack}},
year = {},
note = {[Accessed 20-03-2025]}
}
@misc{geeksforgeeksAggregationMongoDB,
author = {},
title = {{A}ggregation in {M}ongo{D}{B} - {G}eeksfor{G}eeks --- geeksforgeeks.org},
howpublished = {\url{https://www.geeksforgeeks.org/aggregation-in-mongodb/}},
year = {},
note = {[Accessed 01-04-2025]}
}
@misc{geeksforgeeksContainerizationUsing,
author = {},
title = {{C}ontainerization using {D}ocker - {G}eeksfor{G}eeks --- geeksforgeeks.org},
howpublished = {\url{https://www.geeksforgeeks.org/containerization-using-docker/}},
year = {},
note = {[Accessed 18-03-2025]}
}
@misc{geeksforgeeksDockerNetworking,
author = {},
title = {{D}ocker {N}etworking - {G}eeksfor{G}eeks --- geeksforgeeks.org},
howpublished = {\url{https://www.geeksforgeeks.org/basics-of-docker-networking/}},
year = {},
note = {[Accessed 18-03-2025]}
}
@misc{githubGitHubChocobozzzPeerTube,
author = {},
title = {{G}it{H}ub - {C}hocobozzz/{P}eer{T}ube: {A}ctivity{P}ub-federated video streaming platform using {P}2{P} directly in your web browser --- github.com},
howpublished = {\url{https://github.com/Chocobozzz/PeerTube}},
year = {},
note = {[Accessed 24-03-2025]}
}
@misc{githubGitHubHetznercloudcli,
author = {},
title = {{G}it{H}ub - hetznercloud/cli: {A} command-line interface for {H}etzner {C}loud --- github.com},
howpublished = {\url{https://github.com/hetznercloud/cli}},
year = {},
note = {[Accessed 20-03-2025]}
}
@misc{githubGitHubInfluxdatatelegraf,
author = {},
title = {{G}it{H}ub - influxdata/telegraf: {A}gent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data. --- github.com},
howpublished = {\url{https://github.com/influxdata/telegraf}},
year = {},
note = {[Accessed 19-03-2025]}
}
@misc{githubGitHubWebtorrentbittorrenttracker,
author = {},
title = {{G}it{H}ub - webtorrent/bittorrent-tracker: {S}imple, robust, {B}it{T}orrent tracker (client \& server) implementation --- github.com},
howpublished = {\url{https://github.com/webtorrent/bittorrent-tracker?tab=readme-ov-file}},
year = {},
note = {[Accessed 18-03-2025]}
}
@misc{githubWebtorrentdocsfaqmdMaster,
author = {},
title = {webtorrent/docs/faq.md at master · webtorrent/webtorrent --- github.com},
howpublished = {\url{https://github.com/webtorrent/webtorrent/blob/master/docs/faq.md\#how-does-webtorrent-work}},
year = {},
note = {[Accessed 18-03-2025]}
}
@misc{influxdataInfrastructureMonitoring,
author = {},
title = {{I}nfrastructure {M}onitoring {B}asics with {T}elegraf, {I}nflux{D}{B}, and {G}rafana --- influxdata.com},
howpublished = {\url{https://www.influxdata.com/blog/infrastructure-monitoring-basics-telegraf-influxdb-grafana/}},
year = {},
note = {[Accessed 01-04-2025]}
}
@misc{innovationyourselfDockerDocker,
author = {},
title = {{D}ocker vs {D}ocker {C}ompose vs {D}ocker {S}warm s docker engine --- innovationyourself.com},
howpublished = {\url{https://innovationyourself.com/docker-vs-docker-compose-vs-docker-swarm/}},
year = {},
note = {[Accessed 01-04-2025]}
}
@misc{ipfsIPFSWhitepaper,
doi = {10.48550/ARXIV.1407.3561},
url = {https://arxiv.org/abs/1407.3561},
@@ -82,6 +264,30 @@
note = {[Accessed 04-Apr-2023]}
}
@misc{joinpeertubePeerTubeStress,
author = {},
title = {{P}eer{T}ube stress tests: resilience lies in your peers! | {J}oin{P}eer{T}ube --- joinpeertube.org},
howpublished = {\url{https://joinpeertube.org/news/stress-test-2023}},
year = {},
note = {[Accessed 18-03-2025]}
}
@misc{knowiBestIntroduction,
author = {},
title = {{T}he {B}est {I}ntroduction to {M}ongo{D}{B} {Q}uery {L}anguage ({M}{Q}{L}) --- knowi.com},
howpublished = {\url{https://www.knowi.com/blog/the-best-introduction-to-mongodb-query-language-mql/}},
year = {},
note = {[Accessed 19-03-2025]}
}
@misc{mongodbDocumentsMongoDB,
author = {},
title = {{D}ocuments - {M}ongo{D}{B} {M}anual v8.0 - {M}ongo{D}{B} {D}ocs --- mongodb.com},
howpublished = {\url{https://www.mongodb.com/docs/manual/core/document/}},
year = {},
note = {[Accessed 19-03-2025]}
}
@article{Multi-CDN:TowardsPrivacyinContentDeliveryNetworks,
doi = {10.1109/tdsc.2018.2833110},
url = {https://doi.org/10.1109/tdsc.2018.2833110},
@@ -96,6 +302,29 @@
journal = {{IEEE} Transactions on Dependable and Secure Computing}
}
@misc{noauthor_telegraf_nodate,
title = {Telegraf documentation},
url = {https://docs.influxdata.com/telegraf/v1/},
abstract = {Documentation for Telegraf, the plugin-driven server agent of the InfluxData time series platform, used to collect and report metrics. Telegraf supports four categories of plugins input, output, aggregator, and processor.},
urldate = {2025-03-19}
}
@misc{novageMediaLoader,
author = {},
title = {{P}2{P} {M}edia {L}oader --- novage.com.ua},
howpublished = {\url{https://novage.com.ua/p2p-media-loader/technical-overview}},
year = {},
note = {[Accessed 18-03-2025]}
}
@misc{opentelemetryWhatOpenTelemetry,
author = {},
title = {{W}hat is {O}pen{T}elemetry? --- opentelemetry.io},
howpublished = {\url{https://opentelemetry.io/docs/what-is-opentelemetry/}},
year = {},
note = {[Accessed 25-03-2025]}
}
@article{Parameswaran2001,
doi = {10.1109/2.933501},
url = {https://doi.org/10.1109/2.933501},
@@ -126,14 +355,35 @@
note = {[Accessed 07-Mar-2023]}
}
@misc{Redazione_2020,
title = {“Cittadinanza digitale e tecnocivismo. In un mondo digitale la cittadinanza inizia dai bit di Andrea Trentini", Giovanni Biscuolo e Andrea Rossi},
url = {https://www.letture.org/cittadinanza-digitale-e-tecnocivismo-andrea-trentini-giovanni-biscuolo-andrea-rossi},
abstractnote = {Prof. Andrea Trentini, Lei è autore con Giovanni Biscuolo e Andrea Rossi del libro Cittadinanza digitale e tecnocivismo. In un mondo digitale la cittadinanza inizia dai bit edito da Ledizioni: quali aspetti costituiscono la Cittadinanza Digitale? Risposta breve Ovunque ci sia tecnologia cè influenza sulla cittadinanza… e vale anche il viceversa. Risposta articolata In una [...]Leggi di più... from “Cittadinanza digitale e tecnocivismo. In un mondo digitale la cittadinanza inizia dai bit” di Andrea Trentini, Giovanni Biscuolo e Andrea Rossi},
journal = {Letture.org},
year = {2020},
month = {Dec},
language = {it-IT}
@misc{qabrainsSeleniumGrid,
author = {},
title = {{S}elenium {G}rid 4 - {A}rchitecture and {C}ommunication --- qabrains.com},
howpublished = {\url{https://qabrains.com/selenium-grid-4-architecture-and-communication}},
year = {},
note = {[Accessed 01-04-2025]}
}
@phdthesis{ResearchBasedDataRightsManagementUsingBlockchainOverEthereumNetwork,
author = {Naz, Muqaddas and Javaid, Nadeem and Iqbal, Sohail},
year = {2019},
month = {09},
pages = {},
title = {Research Based Data Rights Management Using Blockchain Over Ethereum Network}
}
@misc{rfc2663,
series = {Request for Comments},
number = 2663,
howpublished = {RFC 2663},
publisher = {RFC Editor},
doi = {10.17487/RFC2663},
url = {https://www.rfc-editor.org/info/rfc2663},
author = {Matt Holdrege and Pyda Srisuresh},
title = {{IP Network Address Translator (NAT) Terminology and Considerations}},
pagetotal = 30,
year = 1999,
month = aug,
abstract = {This document attempts to describe the operation of NAT devices and the associated considerations in general, and to define the terminology used to identify various flavors of NAT. This memo provides information for the Internet community.}
}
@misc{rfc5128,
@@ -151,6 +401,52 @@
abstract = {This memo documents the various methods known to be in use by applications to establish direct communication in the presence of Network Address Translators (NATs) at the current time. Although this memo is intended to be mainly descriptive, the Security Considerations section makes some purely advisory recommendations about how to deal with security vulnerabilities the applications could inadvertently create when using the methods described. This memo covers NAT traversal approaches used by both TCP- and UDP-based applications. This memo is not an endorsement of the methods described, but merely an attempt to capture them in a document. This memo provides information for the Internet community.}
}
@misc{rfc5389,
series = {Request for Comments},
number = 5389,
howpublished = {RFC 5389},
publisher = {RFC Editor},
doi = {10.17487/RFC5389},
url = {https://www.rfc-editor.org/info/rfc5389},
author = {Philip Matthews and Jonathan Rosenberg and Dan Wing and Rohan Mahy},
title = {{Session Traversal Utilities for NAT (STUN)}},
pagetotal = 51,
year = 2008,
month = oct,
abstract = {Session Traversal Utilities for NAT (STUN) is a protocol that serves as a tool for other protocols in dealing with Network Address Translator (NAT) traversal. It can be used by an endpoint to determine the IP address and port allocated to it by a NAT. It can also be used to check connectivity between two endpoints, and as a keep-alive protocol to maintain NAT bindings. STUN works with many existing NATs, and does not require any special behavior from them. STUN is not a NAT traversal solution by itself. Rather, it is a tool to be used in the context of a NAT traversal solution. This is an important change from the previous version of this specification (RFC 3489), which presented STUN as a complete solution. This document obsoletes RFC 3489. {[}STANDARDS-TRACK{]}}
}
@misc{rfc8216,
series = {Request for Comments},
number = 8216,
howpublished = {RFC 8216},
publisher = {RFC Editor},
doi = {10.17487/RFC8216},
url = {https://www.rfc-editor.org/info/rfc8216},
author = {Roger Pantos and William May},
title = {{HTTP Live Streaming}},
pagetotal = 60,
year = 2017,
month = aug,
abstract = {This document describes a protocol for transferring unbounded streams of multimedia data. It specifies the data format of the files and the actions to be taken by the server (sender) and the clients (receivers) of the streams. It describes version 7 of this protocol.}
}
@misc{scalerWhichBrowser,
author = {Simran Kumari},
title = {{W}hich {B}rowser {S}upports {S}elenium ? - {S}caler {T}opics --- scaler.com},
howpublished = {\url{https://www.scaler.com/topics/selenium-tutorial/which-browser-supports-selenium/}},
year = {},
note = {[Accessed 20-03-2025]}
}
@misc{seleniumSeleniumBrowser,
author = {},
title = {{T}he {S}elenium {B}rowser {A}utomation {P}roject --- selenium.dev},
howpublished = {\url{https://www.selenium.dev/documentation/}},
year = {},
note = {[Accessed 20-03-2025]}
}
@misc{tahoelafsAbouttahoerstTrunkdocs,
author = {},
title = {About-tahoe; {T}ahoe-{L}{A}{F}{S} --- tahoe-lafs.org},
@@ -159,6 +455,38 @@
note = {[Accessed 02-Apr-2023]}
}
@misc{tailscaleTraversalWorks,
author = {},
title = {{H}ow {N}{A}{T} traversal works --- tailscale.com},
howpublished = {\url{https://tailscale.com/blog/how-nat-traversal-works}},
year = {},
note = {[Accessed 18-03-2025]}
}
@misc{TestGrid_2024,
title = {Selenium WebDriver Step-by-Step Tutorial},
url = {https://testgrid.io/blog/selenium-webdriver/},
journal = {TestGrid},
year = {2024},
month = {9}
}
@online{The360Degree,
author = {The 360 Degree},
title = {Own work},
year = {2022},
url = {https://commons.wikimedia.org/w/index.php?curid=116056511},
note = {Licensed under CC BY-SA 4.0}
}
@misc{theoryBitTorrentSpecificationTheoryOrg,
author = {},
title = {{B}it{T}orrent{S}pecification - {T}heory{O}rg --- wiki.theory.org},
howpublished = {\url{https://wiki.theory.org/BitTorrentSpecification}},
year = {},
note = {[Accessed 18-03-2025]}
}
@inproceedings{theTwitchCase,
author = {Deng, Jie and Tyson, Gareth and Cuadrado, Félix and Uhlig, Steve},
year = {2017},
@@ -195,6 +523,22 @@
note = {[Accessed 11-Feb-2023]}
}
@misc{uclvDockerOverview,
author = {},
title = {{D}ocker overview --- docker-docs.uclv.cu},
howpublished = {\url{https://docker-docs.uclv.cu/get-started/overview/}},
year = {},
note = {[Accessed 01-04-2025]}
}
@online{VignoniCalimo2009,
author = {David Vignoni and Calimo},
title = {By Gnome-fs-client.svg: David Vignoni, Gnome-fs-server.svg: David Vignoni, derivative work: Calimo},
year = {2009},
url = {https://commons.wikimedia.org/w/index.php?curid=15782858},
note = {Licensed under LGPL}
}
@misc{w3c,
author = {W3C},
title = {What is the difference between the Web and the Internet?},
@@ -203,7 +547,31 @@
note = {[Accessed 31-Aug-2022]}
}
@misc{wiki:ActivityPub,
@misc{webBuildBackend,
author = {},
title = {{B}uild the backend services needed for a {W}eb{R}{T}{C} app  |  {A}rticles  |  web.dev --- web.dev},
howpublished = {\url{https://web.dev/articles/webrtc-infrastructure\#what-is-signaling}},
year = {},
note = {[Accessed 18-03-2025]}
}
@misc{webpackConceptsWebpack,
author = {},
title = {{C}oncepts | webpack --- webpack.js.org},
howpublished = {\url{https://webpack.js.org/concepts/}},
year = {},
note = {[Accessed 22-03-2025]}
}
@misc{WebRTCDebugging,
author = {Vittorio Palmisano},
title = {{W}eb{R}{T}{C} debugging with {P}rometheus/{G}rafana --- vpalmisano},
howpublished = {\url{https://medium.com/@vpalmisano/webrtc-debugging-with-prometheus-grafana-254b6ac71063}},
year = {},
note = {[Accessed 20-03-2025]}
}
@misc{wiki:ActivityPub,
author = {Wikipedia},
title = {{ActivityPub} --- {W}ikipedia{,} The Free Encyclopedia},
year = {2023},
@@ -211,7 +579,55 @@
note = {[Online; accessed 05-April-2023]}
}
@misc{wikipediaLicenzainformatica,
@misc{wiki:Broadcasting_networking,
author = {Wikipedia},
title = {{Broadcasting (networking)} --- {W}ikipedia{,} The Free Encyclopedia},
year = {2025},
howpublished = {\url{http://en.wikipedia.org/w/index.php?title=Broadcasting\%20(networking)&oldid=1238402634}},
note = {[Online; accessed 31-March-2025]}
}
@misc{wiki:Content_delivery_network,
author = {Wikipedia},
title = {{Content delivery network} --- {W}ikipedia{,} The Free Encyclopedia},
year = {2025},
howpublished = {\url{http://en.wikipedia.org/w/index.php?title=Content\%20delivery\%20network&oldid=1279155231}},
note = {[Online; accessed 31-March-2025]}
}
@misc{wiki:Multicast,
author = {Wikipedia},
title = {{Multicast} --- {W}ikipedia{,} The Free Encyclopedia},
year = {2025},
howpublished = {\url{http://en.wikipedia.org/w/index.php?title=Multicast&oldid=1270123732}},
note = {[Online; accessed 31-March-2025]}
}
@misc{wiki:Network_address_translation,
author = {Wikipedia},
title = {{Network address translation} --- {W}ikipedia{,} The Free Encyclopedia},
year = {2025},
howpublished = {\url{http://en.wikipedia.org/w/index.php?title=Network\%20address\%20translation&oldid=1283227709}},
note = {[Online; accessed 01-April-2025]}
}
@misc{wiki:PeerTube,
author = {Wikipedia},
title = {{PeerTube} --- {W}ikipedia{,} The Free Encyclopedia},
year = {2025},
howpublished = {\url{http://en.wikipedia.org/w/index.php?title=PeerTube&oldid=1281511347}},
note = {[Online; accessed 24-March-2025]}
}
@misc{wiki:Unicast,
author = {Wikipedia},
title = {{Unicast} --- {W}ikipedia{,} The Free Encyclopedia},
year = {2025},
howpublished = {\url{http://en.wikipedia.org/w/index.php?title=Unicast&oldid=1222709440}},
note = {[Online; accessed 31-March-2025]}
}
@misc{wikipediaLicenzainformatica,
author = {Wikipedia},
title = {{L}icenza (informatica) - {W}ikipedia --- it.wikipedia.org},
howpublished = {\url{https://it.wikipedia.org/wiki/Licenza_(informatica)}},
@@ -235,26 +651,10 @@
note = {[Accessed 09-Feb-2023]}
}
@phdthesis{ResearchBasedDataRightsManagementUsingBlockchainOverEthereumNetwork,
author = {Naz, Muqaddas and Javaid, Nadeem and Iqbal, Sohail},
year = {2019},
month = {09},
pages = {},
title = {Research Based Data Rights Management Using Blockchain Over Ethereum Network}
}
@misc{acestreamAnnouncementStream,
author = {},
title = {{A}nnouncement! {A}{C}{E} {S}tream; {N}ew era of {T}{V} and {I}nternet broadcasting --- oldforum.acestream.media},
url = {http://oldforum.acestream.media/index.php?topic=1479.0},
year = {},
note = {[Accessed 28-09-2023]},
}
@misc{archiveStream,
author = {},
title = {{A}ce {S}tream --- web.archive.org},
url = {https://web.archive.org/web/20180618052904/http://info.acestream.org/#/about/acestream},
year = {},
note = {[Accessed 28-09-2023]},
@misc{wikipediaWebRTCWikipedia,
author = {},
title = {{W}eb{R}{T}{C} - {W}ikipedia --- en.wikipedia.org},
howpublished = {\url{https://en.wikipedia.org/wiki/WebRTC}},
year = {},
note = {[Accessed 18-03-2025]}
}

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 Mirko Milovanovic
Copyright (c) 2025 Mirko Milovanovic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,5 +1,9 @@
# Titolo: Streaming decentralizzato di contenuti audiovisivi
## Release
[PDF aggiornato all'ultimo commit](https://gitea.kobim.cloud/kobim/Tesi/releases/tag/latest)
## TODO
* [ ] Scrivere meglio gli use cases nel dettaglio, facendo delle user stories, concretizzando meglio le situazioni nelle quali gli utenti possono trovarsi

130
Relazione Stage.tex Normal file
View File

@@ -0,0 +1,130 @@
\documentclass[12pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[italian]{babel}
\usepackage[hyphens]{url}
\usepackage{graphicx}
\usepackage{geometry}
\geometry{a4paper, margin=2.5cm}
% PDF/A compliance - using either pdfx or direct pdflatex configuration
% Ensuring all fonts are embedded
\pdfminorversion=4
\pdfobjcompresslevel=0
\pdfcompresslevel=0
\usepackage[a-1b]{pdfx}
\usepackage{xcolor}
\usepackage{datetime}
\title{{\huge{\bf Streaming decentralizzato di}}\\
\vspace{4mm}
{\huge{\bf contenuti audiovisivi}}\\}
\author{Mirko Milovanovic - 870671}
\date{21 marzo 2025}
\begin{document}
\maketitle
\section{Contesto iniziale}
Lo stage è stato svolto nell'ambito della ricerca sulle tecnologie per lo streaming decentralizzato di contenuti audiovisivi, con particolare attenzione alla piattaforma PeerTube. Il contesto di partenza vedeva la crescente problematica della centralizzazione dei servizi di streaming video, con poche grandi piattaforme (come YouTube e Twitch) che dominano il mercato, comportando problemi di scalabilità, costi elevati di infrastruttura, controllo centralizzato dei contenuti e potenziali limitazioni alla libertà di espressione.
PeerTube si propone come alternativa decentralizzata, utilizzando tecnologie peer-to-peer (P2P) per distribuire il carico di banda tra gli utenti. In particolare, il team di PeerTube aveva pubblicato uno studio che affermava una riduzione del carico sui server fino all'80\% grazie alla tecnologia P2P integrata, ma senza fornire dettagli sufficienti sulla metodologia utilizzata né strumenti specifici per riprodurre i test in modo indipendente.
\section{Obiettivi del lavoro}
Gli obiettivi principali dello stage erano:
\begin{itemize}
\item Verificare empiricamente le affermazioni degli sviluppatori di PeerTube riguardo all'efficienza del loro approccio P2P nel contesto dello streaming video
\item Creare un sistema di test automatizzato che potesse riprodurre in modo indipendente i test descritti nell'articolo originale di PeerTube
\item Raccogliere metriche più dettagliate rispetto a quelle presentate nell'articolo originale
\item Analizzare i dati raccolti per valutare le reali prestazioni del sistema P2P di PeerTube
\item Contribuire alla comprensione delle tecnologie decentralizzate per lo streaming video
\end{itemize}
\section{Descrizione lavoro svolto}
Il lavoro è stato articolato nelle seguenti fasi:
\begin{enumerate}
\item \textbf{Analisi del panorama tecnologico}: Studio approfondito delle tecnologie di streaming esistenti, sia centralizzate (Twitch, YouTube) che decentralizzate (IPFS, Tahoe-LAFS, PeerTube, Ace Stream), con particolare attenzione ai loro punti di forza e debolezza.
\item \textbf{Approfondimento dell'architettura di PeerTube}: Analisi dettagliata dell'architettura di PeerTube, dei suoi componenti (Node.js, Angular, PostgreSQL) e delle tecnologie P2P utilizzate (Novage P2P Media Loader, WebRTC).
\item \textbf{Progettazione del sistema di test}: Creazione di un'architettura di test che potesse simulare utenti reali distribuiti geograficamente, raccogliendo metriche dettagliate sulle prestazioni della rete e del sistema P2P.
\item \textbf{Implementazione del sistema}: Sviluppo di un sistema basato su Docker, Selenium, Python e WebRTC Internals Exporter per simulare client reali che guardano video su PeerTube e raccolgono dati sulle connessioni P2P.
\item \textbf{Modifica di WebRTC Internals Exporter}: Adattamento di uno strumento esistente per la raccolta di metriche WebRTC, con modifiche sostanziali per adattarlo alle esigenze specifiche del test.
\item \textbf{Creazione di un sistema di raccolta dati}: Utilizzo di Telegraf e MongoDB per raccogliere, elaborare e archiviare le metriche raccolte dai test.
\item \textbf{Automazione del deployment}: Utilizzo degli script CLI di Hetzner Cloud per distribuire automaticamente i test su macchine virtuali in diverse regioni geografiche.
\item \textbf{Esecuzione dei test}: Riproduzione di due scenari di test principali: live streaming con Normal Latency e live streaming con High Latency.
\item \textbf{Analisi dei risultati}: Elaborazione dei dati raccolti per verificare le affermazioni di PeerTube riguardo all'efficienza del loro approccio P2P.
\end{enumerate}
\section{Tecnologie coinvolte}
Durante lo stage sono state utilizzate e approfondite numerose tecnologie:
\begin{itemize}
\item \textbf{Docker}: Per creare ambienti isolati e facilmente riproducibili
\item \textbf{Python}: Come linguaggio principale per l'automazione e l'analisi dei dati
\item \textbf{WebRTC}: Per comprendere il funzionamento delle connessioni peer-to-peer nei browser
\item \textbf{Selenium}: Per simulare browser reali che guardano stream video
\item \textbf{Telegraf}: Per la raccolta di metriche di sistema e di rete
\item \textbf{MongoDB}: Per l'archiviazione strutturata dei dati raccolti
\item \textbf{WebPack}: Per la compilazione e la gestione delle dipendenze del frontend
\item \textbf{Hetzner Cloud}: Per il deployment distribuito dei test
\item \textbf{Beautiful Soup 4}: Per l'estrazione di dati dalle pagine web di PeerTube
\item \textbf{Novage P2P Media Loader}: Libreria JavaScript per la distribuzione P2P dei video
\end{itemize}
\section{Competenze e risultati raggiunti}
\textbf{Risultati rispetto agli obiettivi}:
\begin{itemize}
\item È stato creato con successo un sistema di test automatizzato per verificare le prestazioni di PeerTube
\item Sono state raccolte metriche dettagliate sulle connessioni P2P e sulle prestazioni del sistema
\item I dati raccolti hanno confermato in gran parte le affermazioni degli sviluppatori di PeerTube, pur con alcune limitazioni dovute alla scala ridotta dei test (5 client anziché 1000)
\item Il sistema sviluppato è disponibile come progetto open source, permettendo ad altri ricercatori di riprodurre e ampliare i test
\end{itemize}
\textbf{Insegnamenti tratti dall'esperienza}:
\begin{itemize}
\item Importanza dell'automazione per test di sistema complessi
\item Valore di un approccio modulare e flessibile nello sviluppo di strumenti di test
\item Comprensione approfondita delle dinamiche delle reti P2P e delle sfide nella distribuzione di contenuti video
\item Acquisizione di competenze avanzate nell'instrumentazione di browser web per la raccolta di metriche
\end{itemize}
\textbf{Problemi incontrati}:
\begin{itemize}
\item \textbf{Accesso agli indirizzi IP forniti da WebRTC} (Risolto): Le recenti versioni di Chrome hanno implementato misure di sicurezza che offuscano gli indirizzi IP locali. Questo è stato risolto utilizzando una versione specifica di Chromium con opportuni flag di configurazione.
\item \textbf{Limitazioni delle API di PeerTube} (Risolto): Mancanza di API specifiche per le metriche dei video. Risolto tramite web scraping con BeautifulSoup.
\item \textbf{Gestione delle variabili d'ambiente nell'estensione Chrome} (Risolto): Difficoltà nella configurazione dinamica dell'estensione WebRTC. Risolto utilizzando WebPack per la sostituzione delle variabili in fase di compilazione.
\item \textbf{Scala dei test} (Parzialmente risolto): Non è stato possibile replicare la scala dei test originali (1000 client) a causa di limitazioni di risorse. I test sono stati eseguiti con 5 client, che hanno comunque fornito dati significativi sulle dinamiche P2P.
\item \textbf{Raccolta dei dati su macchine distribuite} (Risolto): Sfida nel raccogliere dati da macchine geograficamente distribuite. Risolto con un'architettura centralizzata di raccolta dati basata su Telegraf e MongoDB.
\end{itemize}
\section{Bibliografia}
\begin{itemize}
\item RFC 2663: IP Network Address Translator (NAT) Terminology and Considerations
\item RFC 8216: HTTP Live Streaming
\item W3C WebRTC
\item The Selenium Browser Automation Project
\item PeerTube
\end{itemize}
\end{document}

1718
Tesi.tex

File diff suppressed because it is too large Load Diff

5
Tesi.xmpdata Normal file
View File

@@ -0,0 +1,5 @@
\Title{Streaming decentralizzato di contenuti audiovisivi}
\Author{Mirko Milovanovic}
\Language{it-IT}
\Subject{Tesi triennale in Informatica per la Comunicazione Digitale - Analisi di soluzioni per lo streaming decentralizzato di contenuti audiovisivi}
\Keywords{streaming, decentralizzato, audiovisivo, video, peer-to-peer, WebRTC, IPFS, DTube, BitTorrent, blockchain, Ethereum, smart contract, IPFS, DTube, BitTorrent, blockchain, Ethereum, smart contract, P2P}

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -1,50 +0,0 @@
<?xml version="1.0"?>
<svg
width="360"
height="87.039368"
viewBox="0 0 360 87.039367"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<g
transform="matrix(1.2293722,0,0,1.2293722,29.999995,7.2531291)">
<g
clip-path="url(#clip0_205_625)">
<path
d="M 55.4998,2.93744 C 50.6758,-0.0519845 46.0011,-0.485235 41.754,0.421702 37.6819,1.2882 33.7619,3.45734 30.4359,6.01929 29.575,5.46762 28.7428,4.91595 27.9249,4.38161 c -0.7461,1.62613 -1.5123,3.28981 -2.2957,4.97371 -0.7921,1.73298 -1.5975,3.49588 -2.4163,5.28858 2.1781,-0.7048 4.3877,-1.424 6.6462,-2.149 l 7.025,-2.2644 -3.2313,-2.10561 c 2.9759,-2.13448 6.2502,-3.94258 9.757,-4.97082 3.9343,-1.15262 8.103,-1.2274 12.0756,-0.21663 z"
fill="#f15e4b"/>
<path
d="M 63.2681,29.8018 C 67.013,21.7434 66.574,16.4577 63.9912,13.191 61.5893,10.1525 57.3479,8.8383 52.8712,8.59857 53.4997,7.18328 54.1252,5.78533 54.7279,4.41626 l -7.4124,3.466 -7.1743,3.35914 c 1.2397,1.1765 2.5235,2.3954 3.8512,3.6566 l 4.3045,4.0928 c 0.7652,-1.733 1.5142,-3.441 2.247,-5.1239 3.6703,0.5199 7.0766,1.7041 9.4699,4.0263 2.5282,2.4696 3.9344,6.2504 3.2629,11.9289 z"
fill="#8a5da7"/>
<path
d="m 31.2939,59.0029 c 9.8173,-1.8313 16.2913,-6.184 19.9616,-11.4205 1.577,-2.2256 2.683,-4.7535 3.2492,-7.4271 0.5662,-2.6735 0.5806,-5.4357 0.0423,-8.115 2.2527,-0.7539 4.5169,-1.5222 6.8156,-2.3107 -2.5541,-1.398 -5.001,-2.7459 -7.3407,-4.0437 -2.2345,-1.2131 -4.3849,-2.3838 -6.4511,-3.5122 -1.211,2.3473 -2.4469,4.7475 -3.7076,7.2006 -1.2799,2.5032 -2.5866,5.0633 -3.92,7.6801 2.2498,-0.7741 4.5312,-1.5539 6.8471,-2.3627 0.3422,4.4025 -0.5982,8.8099 -2.7061,12.6827 C 41.6908,51.7531 37.6073,55.843 31.2939,59 Z"
fill="#4a8da0" />
<path
d="m 2.24982,47.3167 c 2.53106,5.5283 6.54002,7.914 11.11718,8.0411 4.8211,0.1329 9.998,-2.2413 14.535,-5.6351 1.2167,1.3363 2.4679,2.7121 3.7535,4.1274 0.9815,-2.8739 1.9514,-5.6698 2.8869,-8.3935 0.9202,-2.6342 1.8156,-5.2077 2.6861,-7.7205 -2.9472,0.6701 -5.8341,1.3113 -8.655,1.9496 l -8.1241,1.8399 c 1.1048,1.2015 2.2355,2.4464 3.4063,3.723 -3.7076,2.5389 -7.6075,4.4307 -11.4357,4.942 C 8.72097,50.696 5.21708,49.9018 2.24982,47.3022 Z"
fill="#26a75a"/>
<path
d="M 5.00762,20.8682 C 1.10197,26.6015 -0.258264,30.8849 0.0373139,34.0159 0.35298,37.3288 2.54542,39.3795 5.5586,40.4107 c -0.90683,1.7128 -1.83947,3.4458 -2.78073,5.199 2.28427,-1.034 4.59628,-2.0844 6.93604,-3.1512 2.36459,-1.06 4.75219,-2.1518 7.17419,-3.2551 -0.9843,-1.8081 -1.9227,-3.544 -2.841,-5.2164 -0.8723,-1.6097 -1.717,-3.1637 -2.5339,-4.6618 C 10.6026,31.0236 9.6766,32.747 8.73534,34.4954 6.3248,33.6 4.47098,32.1703 3.722,29.8971 3.01031,27.7396 3.28293,24.8108 5.00188,20.8624 Z"
fill="#fec827"/>
<path
d="M 29.2163,1.95544 C 22.9346,3.77221 18.7333,6.22729 15.9268,8.91633 13.3887,11.2767 11.6132,14.3497 10.8302,17.7373 l -4.26434,1.4182 c 0.92021,0.9878 1.87008,2.0054 2.8496,3.0529 1.01774,1.0861 2.07004,2.2096 3.15664,3.3707 1.4808,-1.9583 2.9414,-3.8838 4.382,-5.7766 1.4148,-1.8486 2.8066,-3.6827 4.1697,-5.4879 -1.6912,0.5604 -3.3662,1.112 -5.0248,1.655 0.673,-2.7427 1.9941,-5.2813 3.8511,-7.39987 2.0088,-2.31067 4.9502,-4.55779 9.2662,-6.6114 z"
fill="#f58120"/>
</g>
<g
transform="translate(25)"
style="fill:#000000">
<path
d="M 86.078,37 78.3011,24.0818 c -0.2784,-0.464 -0.6125,-0.761 -1.1322,-0.761 -0.5197,0 -0.8723,0.297 -1.1507,0.761 L 68.2598,37 h 2.6913 l 6.125,-10.3568 3.0069,5.0485 h -4.1391 l -1.1136,1.9117 h 6.3849 L 83.2383,37 Z m 13.1331,0 v -2.3572 h -7.3129 c -2.4314,0 -4.1947,-1.8746 -4.1947,-4.4174 0,-2.5614 1.7819,-4.3618 4.1947,-4.3618 h 7.3129 v -2.3572 h -7.3129 c -3.7678,0 -6.5519,2.6356 -6.5519,6.6076 0,3.9163 2.7841,6.886 6.5519,6.886 z m 13.9779,0 v -2.3572 h -9.726 v -8.7792 h 9.708 V 23.5064 H 101.125 V 37 Z m -0.52,-5.8652 v -1.9859 h -7.479 v 1.9859 z M 130.78,37 c 2.821,0 4.194,-1.689 4.194,-3.9534 0,-2.6171 -1.466,-3.8977 -4.194,-3.8977 h -6.181 c -1.039,0 -1.689,-0.6125 -1.689,-1.6334 0,-1.0208 0.631,-1.6519 1.708,-1.6519 h 9.688 v -2.3572 h -9.688 c -2.692,0 -4.158,1.6148 -4.158,3.7864 0,2.2273 1.578,3.842 4.12,3.842 h 6.2 c 1.262,0 1.967,0.594 1.967,1.7447 0,1.058 -0.705,1.7633 -1.967,1.7633 H 120.664 V 37 Z m 12.897,0 V 25.8636 h 5.123 v -2.3572 h -12.603 v 2.3572 h 5.123 V 37 Z m 22.546,0 -4.065,-4.1019 c 1.745,-0.6682 2.784,-2.2458 2.784,-4.4731 0,-3.0068 -1.818,-4.9186 -4.9,-4.9186 h -9.688 V 37 h 2.338 V 25.8636 h 7.35 c 1.615,0 2.58,0.8538 2.58,2.5985 0,1.7447 -0.891,2.7099 -2.58,2.7099 h -5.623 v 2.0788 h 5.048 L 162.882,37 Z m 13.288,0 v -2.3572 h -9.726 v -8.7792 h 9.707 V 23.5064 H 167.446 V 37 Z m -0.52,-5.8652 v -1.9859 h -7.48 v 1.9859 z M 198.439,37 190.662,24.0818 c -0.279,-0.464 -0.613,-0.761 -1.133,-0.761 -0.519,0 -0.872,0.297 -1.15,0.761 L 180.62,37 h 2.692 l 6.125,-10.3568 3.006,5.0485 h -4.139 l -1.113,1.9117 h 6.385 L 195.599,37 Z m 20.588,0 -3.452,-12.6955 c -0.186,-0.6496 -0.557,-0.9651 -1.132,-0.9651 -0.502,0 -0.91,0.2784 -1.133,0.7981 l -4.157,9.5402 -4.158,-9.5402 c -0.222,-0.5197 -0.687,-0.7981 -1.188,-0.7981 -0.631,0 -1.002,0.3155 -1.169,0.9651 L 199.167,37 h 2.32 l 2.58,-9.4845 3.805,8.7792 c 0.241,0.5754 0.65,0.8909 1.207,0.8909 0.575,0 0.946,-0.3155 1.206,-0.8909 l 3.823,-8.7792 2.58,9.4845 z"
fill="#F2F2F2"
style="fill:#000000"/>
</g>
</g>
<defs
id="defs23">
<clipPath
id="clip0_205_625">
<rect
width="66"
height="59"
fill="#ffffff"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 5.5 KiB

BIN
images/Broadcast.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
images/Docker-network-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

BIN
images/Multicast.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
images/NAT_Concept-en.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
images/Unicast.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
images/docker-engine.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

BIN
images/nat-traversal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
images/p2p-network.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
images/selenium-grid.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
images/stun-server.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View File

@@ -1,119 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="436.33716mm"
height="115.63236mm"
viewBox="0 0 436.33716 115.63236"
version="1.1"
id="svg8">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
transform="translate(124.123,6.0733085)">
<g
id="g1151"
transform="matrix(6.0868818,0,0,6.0868818,-732.64487,-599.37985)">
<path
d="m 106.18342,105.84175 -6.206843,-0.014 -0.0039,1.38039 2.311913,-0.0118 0.0551,8.95356 1.50888,0.004 0.0567,-8.94074 2.27595,-0.0157 z"
style="font-size:14.8884px;line-height:1.25;font-family:SansSerifFLF;-inkscape-font-specification:SansSerifFLF;fill:#000000;stroke-width:0.372211"
id="path118" />
<path
d="m 110.34872,115.0306 c -1.13152,0 -1.74862,-0.91223 -1.74862,-2.04375 0,-1.1464 0.59805,-2.14886 1.72957,-2.14886 1.14641,0 1.94857,0.98264 1.94857,2.12905 0,1.13152 -0.78311,2.06356 -1.92952,2.06356 z m -3.39272,-2.0085 c 0,1.81638 1.45951,3.39031 3.14189,3.39031 1.25063,0 1.56823,-0.50841 1.98511,-0.94018 l 0.002,0.69436 1.61201,0.0147 10e-4,-6.52031 -1.54718,-10e-5 -0.009,0.82355 c -0.3871,-0.43176 -0.85501,-0.93861 -2.18008,-0.93861 -1.72705,0 -3.00491,1.64503 -3.00491,3.4763 z"
style="font-size:14.8884px;line-height:1.25;font-family:SansSerifFLF;-inkscape-font-specification:SansSerifFLF;fill:#000000;stroke-width:0.372211"
id="path120" />
<path
d="m 115.52271,116.2074 1.45658,0.004 -0.0112,-3.63171 c -0.003,-0.81885 0.66275,-1.73405 1.57094,-1.73405 0.87841,0 1.16366,0.8754 1.1648,1.70967 l 0.0149,3.65424 1.36565,0.004 2.4e-4,-3.89183 c 9e-5,-1.38462 -0.48318,-2.75908 -2.2549,-2.75908 -0.95285,0 -1.5389,0.24611 -1.74733,0.70765 l -0.0905,-5.39541 -1.46273,-6e-5 z"
style="font-size:14.8884px;line-height:1.25;font-family:SansSerifFLF;-inkscape-font-specification:SansSerifFLF;fill:#000000;stroke-width:0.372211"
id="path122" />
<g
aria-label="e"
id="text104"
style="font-size:14.8884px;line-height:1.25;font-family:SansSerifFLF;-inkscape-font-specification:SansSerifFLF;fill:#000000;stroke-width:0.372211"
transform="translate(0,20.10834)">
<path
d="m 135.77525,91.943817 -2.96622,-0.0168 c 0.16377,-0.595536 0.64709,-1.155374 1.48084,-1.155374 0.87841,0 1.24717,0.546865 1.48538,1.172178 z m -4.69876,0.837883 c 0,1.816384 1.32897,3.493078 3.17513,3.493078 1.4144,0 2.94259,-1.236045 3.02569,-1.876867 l -1.1049,-0.644681 c -0.23822,0.297768 -1.15358,1.149409 -1.83845,1.149409 -1.20596,0 -1.65729,-0.781712 -1.65729,-1.779235 l 4.70278,0.02339 c 0,-1.995046 -1.22489,-3.863459 -3.04127,-3.863459 -1.83127,0 -3.26169,1.815975 -3.26169,3.498365 z"
style="fill:#000000;stroke-width:0.372211"
id="path125" />
</g>
<path
d="m 143.99337,114.6648 -0.14168,-8.87341 -1.52294,0.0141 -0.006,10.37368 4.54697,0.0624 -0.004,-1.57754 z"
style="font-size:14.8884px;line-height:1.25;font-family:SansSerifFLF;-inkscape-font-specification:SansSerifFLF;fill:#000000;stroke-width:0.372211"
id="path131" />
<path
d="m 152.26578,105.18986 -5.02661,10.95056 1.77251,0.004 0.89789,-2.25292 h 4.53055 l 1.0435,2.24762 1.79729,-6.4e-4 z m 0.0602,3.49642 1.59306,3.51807 -3.18612,0.0503 z"
style="font-size:14.8884px;line-height:1.25;font-family:SansSerifFLF;-inkscape-font-specification:SansSerifFLF;fill:#000000;stroke-width:0.372211"
id="path133" />
<path
d="m 163.81296,106.92399 v -1.51863 h -5.10672 v 10.63037 h 1.51862 v -4.63031 h 3.5881 v -1.51863 h -3.5881 l -0.0169,-2.95443 z"
style="font-size:14.8884px;line-height:1.25;font-family:SansSerifFLF;-inkscape-font-specification:SansSerifFLF;fill:#000000;stroke-width:0.372211"
id="path135" />
<path
d="m 170.19028,108.0662 1.2443,-0.72871 c -0.25077,-0.75392 -1.62879,-1.74534 -2.77519,-1.74534 -1.69728,0 -3.15731,0.98874 -3.15731,2.55203 0,1.72706 0.71475,2.32326 2.11426,2.88902 1.38462,0.55088 2.29961,1.07985 2.43537,1.88315 0.24596,1.45537 -0.99727,2.16559 -1.92136,2.00482 -1.06332,-0.18499 -1.49181,-1.21306 -1.53913,-1.87861 l -1.49173,0.31661 c 0,0.89704 1.17066,2.91752 2.85466,2.98186 2.13863,0.0817 3.70337,-1.31771 3.70337,-3.2681 0,-1.99506 -2.16524,-2.91815 -3.47542,-3.42435 -1.10174,-0.43177 -1.02351,-0.78545 -1.02351,-1.52987 0,-0.65509 0.45097,-1.02986 1.37405,-1.02986 0.69975,0 1.27055,0.60514 1.65764,0.97735 z"
style="font-size:14.8884px;line-height:1.25;font-family:SansSerifFLF;-inkscape-font-specification:SansSerifFLF;fill:#000000;stroke-width:0.372211"
id="path137" />
<g
aria-label="-"
id="text116"
style="font-size:14.8884px;line-height:1.25;font-family:SansSerifFLF;-inkscape-font-specification:SansSerifFLF;fill:#000000;stroke-width:0.372211"
transform="translate(0,20.10834)">
<path
d="m 141.06065,92.121323 h -2.90455 v 1.312631 h 2.90455 z"
style="fill:#000000;stroke-width:0.384512"
id="path128" />
</g>
<circle
style="fill:#ff0000;fill-opacity:1;stroke-width:0.236763"
id="path162"
cx="126.39951"
cy="112.9655"
r="3.5044639" />
<path
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 124.91938,109.77401 -0.80485,-2.07788 -0.56046,0.0391 -0.12405,-2.01283 1.46712,1.61746 -0.37275,0.20586 0.89001,2.09631 z"
id="path164" />
<path
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 127.35787,109.53011 0.78896,-1.96229 -0.67822,-0.17271 1.42086,-1.68544 0.036,2.13358 -0.27114,-0.12937 -0.88072,2.11195 z"
id="path989" />
<path
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 125.90599,109.56222 0.0869,-5.48256 -0.55471,-0.15529 0.73255,-1.98112 0.84234,2.08361 -0.48508,0.0646 0.03,5.50009 z"
id="path991" />
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.233764"
id="path993"
cx="122.51501"
cy="103.5195"
r="2.0734317" />
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.233764"
id="circle995"
cx="126.19068"
cy="99.546417"
r="2.0734317" />
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.233764"
id="circle997"
cx="129.90472"
cy="103.51728"
r="2.0734317" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 KiB

View File

@@ -1,17 +1 @@
## 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
```
# TODO

View File

@@ -0,0 +1,103 @@
/* global use, db */
// MongoDB Playground
// To disable this template go to Settings | MongoDB | Use Default Template For Playground.
// Make sure you are connected to enable completions and to be able to run a playground.
// Use Ctrl+Space inside a snippet or a string literal to trigger completions.
// The result of the last command run in a playground is shown on the results panel.
// By default the first 20 documents will be returned with a cursor.
// Use 'console.log()' to print to the debug output.
// For more documentation on playgrounds please refer to
// https://www.mongodb.com/docs/mongodb-vscode/playgrounds/
// Select the database to use.
use('statistics');
// Insert a few documents into the sales collection.
db.getCollection('peertube_hetzner_high_latency').aggregate(
[
{
$group: {
_id: "$tags.session",
maxDownPeers: {
$max: "$player.Download Breakdown.Peers"
},
maxDownServer: {
$max: "$player.Download Breakdown.Server"
},
}
},
{
$group: {
_id: null,
totalDownPeers: {
$sum: "$maxDownPeers"
},
totalDownServer: {
$sum: "$maxDownServer"
}
}
},
{
$project: {
_id: 0,
totalDownPeers: 1,
totalDownServer: 1,
totalComputedDown: {
$sum: [
"$totalDownPeers",
"$totalDownServer"
]
}
}
},
{
$project: {
totalDownPeers: 1,
totalDownServer: 1,
totalComputedDown: 1,
percentageOfTotalPeers: {
$round: {
$multiply: [
{
$divide: [
"$totalDownPeers",
"$totalComputedDown"
]
},
100
]
}
},
percentageOfTotalServer: {
$round: {
$multiply: [
{
$divide: [
"$totalDownServer",
"$totalComputedDown"
]
},
100
]
}
},
ratio: {
$round: {
$divide: [
"$totalDownPeers",
"$totalDownServer"
]
}
}
}
},
{
$sort: {
ratio: -1
}
},
{
$limit: 10
}
]
)

View File

@@ -0,0 +1,255 @@
/* global use, db */
// MongoDB Playground
// To disable this template go to Settings | MongoDB | Use Default Template For Playground.
// Make sure you are connected to enable completions and to be able to run a playground.
// Use Ctrl+Space inside a snippet or a string literal to trigger completions.
// The result of the last command run in a playground is shown on the results panel.
// By default the first 20 documents will be returned with a cursor.
// Use 'console.log()' to print to the debug output.
// For more documentation on playgrounds please refer to
// https://www.mongodb.com/docs/mongodb-vscode/playgrounds/
use("statistics");
let formattedDate = (date) => ({
unix: { $toLong: date },
iso: { $toString: date },
});
// Find the minimum timestamp and calculate the maximum timestamp (one hour later) for a collection
function getTimeWindow(collectionName) {
const minTimestamp = db.getCollection(collectionName).aggregate([
{ $sort: { timestamp: 1 } },
{ $limit: 1 },
{ $project: { _id: 0, timestamp: 1 } }
]).toArray()[0]?.timestamp;
if (!minTimestamp) return null;
const maxTimestamp = new Date(minTimestamp.getTime() + 60 * 60 * 1000);
return { minTimestamp, maxTimestamp };
}
// Get time windows for both collections
const highLatencyTimeWindow = getTimeWindow("peertube_hetzner_high_latency");
const defaultLatencyTimeWindow = getTimeWindow("peertube_hetzner_default_latency");
// Function to perform the aggregation for a given collection
function getAggregationResult(collectionName, timeWindow) {
if (!timeWindow) return [];
return db.getCollection(collectionName).aggregate([
// Filter documents within the collection's specific time window
{
$match: {
timestamp: {
$gte: timeWindow.minTimestamp,
$lt: timeWindow.maxTimestamp
}
}
},
{
$unwind: "$peers"
},
{
$match: {
"peers.iceConnectionState": "connected"
}
},
{
$unwind: "$peers.values"
},
{
$match: {
"peers.values.type": "data-channel",
}
},
{
$group: {
_id: "$peers.id",
maxBytesReceived: {
$max: "$peers.values.bytesReceived"
},
maxBytesSent: {
$max: "$peers.values.bytesSent"
}
}
},
{
$group: {
_id: null,
totalMaxBytesReceived: {
$sum: "$maxBytesReceived"
},
totalMaxBytesSent: {
$sum: "$maxBytesSent"
}
}
},
{
$project: {
_id: 0,
collection: collectionName,
totalBytesReceived: "$totalMaxBytesReceived",
totalBytesSent: "$totalMaxBytesSent",
formattedBytesReceived: {
$switch: {
branches: [
{
case: {
$gte: [
"$totalMaxBytesReceived",
1073741824 // 1024^3
]
},
then: {
$concat: [
{
$toString: {
$divide: [
"$totalMaxBytesReceived",
1073741824 // 1024^3
]
}
},
" GiB"
]
}
},
{
case: {
$gte: [
"$totalMaxBytesReceived",
1048576 // 1024^2
]
},
then: {
$concat: [
{
$toString: {
$divide: [
"$totalMaxBytesReceived",
1048576 // 1024^2
]
}
},
" MiB"
]
}
},
{
case: {
$gte: [
"$totalMaxBytesReceived",
1024 // 1024^1
]
},
then: {
$concat: [
{
$toString: {
$divide: [
"$totalMaxBytesReceived",
1024 // 1024^1
]
}
},
" KiB"
]
}
}
],
default: {
$concat: [
{
$toString:
"$totalMaxBytesReceived"
},
" bytes"
]
}
}
},
formattedBytesSent: {
$switch: {
branches: [
{
case: {
$gte: [
"$totalMaxBytesSent",
1073741824 // 1024^3
]
},
then: {
$concat: [
{
$toString: {
$divide: [
"$totalMaxBytesSent",
1073741824 // 1024^3
]
}
},
" GiB"
]
}
},
{
case: {
$gte: [
"$totalMaxBytesSent",
1048576 // 1024^2
]
},
then: {
$concat: [
{
$toString: {
$divide: [
"$totalMaxBytesSent",
1048576 // 1024^2
]
}
},
" MiB"
]
}
},
{
case: {
$gte: ["$totalMaxBytesSent", 1024] // 1024^1
},
then: {
$concat: [
{
$toString: {
$divide: [
"$totalMaxBytesSent",
1024 // 1024^1
]
}
},
" KiB"
]
}
}
],
default: {
$concat: [
{ $toString: "$totalMaxBytesSent" },
" bytes"
]
}
}
}
}
}
]).toArray();
}
// Get results from both collections using their respective time windows
const highLatencyResults = getAggregationResult("peertube_hetzner_high_latency", highLatencyTimeWindow);
const defaultLatencyResults = getAggregationResult("peertube_hetzner_default_latency", defaultLatencyTimeWindow);
// Combine and return the results
const combinedResults = [...highLatencyResults, ...defaultLatencyResults];
combinedResults;

File diff suppressed because one or more lines are too long

View File

@@ -5,4 +5,5 @@ seaborn
numpy
scipy
plotly
nbformat
nbformat
ipykernel

1
peertube/statnerd Submodule

Submodule peertube/statnerd added at 8b35d3068b

View File

@@ -1,21 +0,0 @@
__pycache__
*.pyc
*.pyo
*.pyd
.Python
env
pip-log.txt
pip-delete-this-directory.txt
.tox
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.log
.git
.mypy_cache
.pytest_cache
.hypothesis
.pytest_caches

View File

@@ -1,13 +0,0 @@
FROM python:3.13.1-slim-bookworm
# Install dependencies
COPY requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir -r /app/requirements.txt
# Copy the application
COPY main.py /app
COPY utils/ /app/utils
WORKDIR /app
# Run the application
CMD ["python", "main.py"]

View File

@@ -1,41 +0,0 @@
services:
selenium:
container_name: selenium-standalone-chromium
image: selenium/standalone-chromium:129.0
ports:
- "7900:7900"
volumes:
- ./webrtc-internals-exporter:/tmp/webrtc-internals-exporter:ro
shm_size: "2g"
attach: false
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4444/wd/hub/status"]
interval: 5s
timeout: 10s
retries: 5
networks:
- backend
telegraf:
container_name: telegraf
image: telegraf:1.33.1
volumes:
- ./telegraf.conf:/etc/telegraf/telegraf.conf:ro
networks:
- backend
collector:
container_name: collector
build:
context: .
dockerfile: Dockerfile
depends_on:
selenium:
condition: service_healthy
telegraf:
condition: service_started
networks:
- backend
networks:
backend:

View File

@@ -1,191 +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_argument("--load-extension=/tmp/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://selenium-standalone-chromium: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(), ('telegraf', 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(('collector', 9092), partial(Handler, downloadStats, driver, logger))
logger.info('Server collector started.')
httpd.serve_forever()

View File

@@ -1,2 +0,0 @@
selenium
beautifulsoup4

View File

@@ -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

View File

@@ -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
)

View File

@@ -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')

View File

@@ -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}
```

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
.version[data-v-4cce5501]{text-decoration:none}

File diff suppressed because one or more lines are too long

View File

@@ -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);

View File

@@ -1 +0,0 @@
.version[data-v-16634bc6]{font-size:smaller;text-decoration:none}

View File

@@ -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://collector: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;
});

View File

@@ -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

View File

@@ -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://*/*"
]
}
]
}

View File

@@ -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>

View File

@@ -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;
},
});

View File

@@ -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>