<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>GL-Como</title><link href="https://www.gl-como.it/" rel="alternate"/><link href="https://www.gl-como.it/feeds/all.atom.xml" rel="self"/><id>https://www.gl-como.it/</id><updated>2025-10-17T20:30:00+02:00</updated><subtitle>Figli orgogliosi del pinguino ;-)</subtitle><entry><title>Linux Day 2025</title><link href="https://www.gl-como.it/linux-day-2025/" rel="alternate"/><published>2025-10-17T20:30:00+02:00</published><updated>2025-10-17T20:30:00+02:00</updated><author><name>gl-como</name></author><id>tag:www.gl-como.it,2025-10-17:/linux-day-2025/</id><summary type="html">&lt;p&gt;Anche quest'anno il GL-Como partecipa al Linux Day!&lt;/p&gt;
&lt;p&gt;L'appuntamento annuale organizzato da &lt;a href="https://www.ils.org/"&gt;ILS&lt;/a&gt; è nato nel 2001 per promuovere le idee del software libero e dell'open source, con un occhio di riguardo verso Linux. L'evento  è costituito da una rete di eventi decentralizzati in tutta Italia organizzati autonomamente da gruppi …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Anche quest'anno il GL-Como partecipa al Linux Day!&lt;/p&gt;
&lt;p&gt;L'appuntamento annuale organizzato da &lt;a href="https://www.ils.org/"&gt;ILS&lt;/a&gt; è nato nel 2001 per promuovere le idee del software libero e dell'open source, con un occhio di riguardo verso Linux. L'evento  è costituito da una rete di eventi decentralizzati in tutta Italia organizzati autonomamente da gruppi volontari e appassionati.&lt;/p&gt;
&lt;p&gt;In questa edizione il GL-Como si unisce a &lt;a href="https://casoratesempione.ils.org/2025/07/18/linuxday-2025/"&gt;ILS Casorate Sempione&lt;/a&gt;, &lt;strong&gt;sabato 25 ottobre&lt;/strong&gt; presso la &lt;strong&gt;biblioteca comunale Alda Merini&lt;/strong&gt; .&lt;/p&gt;
&lt;p&gt;Il programma definitivo è disponibile su &lt;a href="https://casoratesempione.ils.org/2025/07/18/linuxday-2025/"&gt;https://casoratesempione.ils.org/2025/07/18/linuxday-2025/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Volantino Linux Day 2025" src="https://casoratesempione.ils.org/wp-content/uploads/sites/12/2025/07/volantino-scala-di-grigi-render-LO-1-724x1024.jpg.webp"&gt;&lt;/p&gt;</content><category term="Annunci, Eventi"/></entry><entry><title>Linux App Summit 2022</title><link href="https://www.gl-como.it/LAS2022/" rel="alternate"/><published>2022-05-25T12:28:00+02:00</published><updated>2022-05-25T12:28:00+02:00</updated><author><name>Figio</name></author><id>tag:www.gl-como.it,2022-05-25:/LAS2022/</id><summary type="html">&lt;p&gt;Nei giorni 29 e 30 aprile 2022, in due membri del gruppo si è partecipato all’evento &lt;a href="https://linuxappsummit.org/"&gt;Linux App Summit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Il pass e gli adesivi del LAS 2022" src="https://www.gl-como.it/images/las2022/IMG_20220501_211141.jpg"&gt;&lt;/p&gt;
&lt;p&gt;E' la prima volta che partecipiamo al &lt;em&gt;LAS&lt;/em&gt;, evento a livello mondiale, giunto alla sua quarta edizione, che ha lo scopo di far incontrare sviluppatori di programmi per il desktop …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Nei giorni 29 e 30 aprile 2022, in due membri del gruppo si è partecipato all’evento &lt;a href="https://linuxappsummit.org/"&gt;Linux App Summit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Il pass e gli adesivi del LAS 2022" src="https://www.gl-como.it/images/las2022/IMG_20220501_211141.jpg"&gt;&lt;/p&gt;
&lt;p&gt;E' la prima volta che partecipiamo al &lt;em&gt;LAS&lt;/em&gt;, evento a livello mondiale, giunto alla sua quarta edizione, che ha lo scopo di far incontrare sviluppatori di programmi per il desktop Linux con riferimento agli ambienti KDE e GNOME.&lt;/p&gt;
&lt;p&gt;Quest'anno il &lt;em&gt;Linux App Summit&lt;/em&gt; si è tenuto presso il &lt;a href="http://www.smartlabrovereto.it/"&gt;Centro Giovani Smart Lab&lt;/a&gt; di Rovereto, un ambiente polifunzionale dove si tengono varie attività e con uno spazio per concerti e conferenze.&lt;/p&gt;
&lt;p&gt;Abbiamo preso confidenza con la location già da giovedì sera, durante il "party di pre-registrazione": dopo aver ritirato i nostri badge abbiamo gironzolato per il posto bevendo qualcosa e mischiandoci ai presenti. &lt;/p&gt;
&lt;p&gt;&lt;img alt="Fabrix e Figio mostrano i pass durante un momento di socializzazione davanti a una birra" src="https://www.gl-como.it/images/las2022/IMG_20220428_182349.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Le giornate “lavorative” del venerdì e sabato sono state intense con interventi dal vivo e on-line (purtroppo non sempre di qualità); durante i coffe break veniva offerto uno spuntino con succo di frutta e cornetto.&lt;/p&gt;
&lt;p&gt;La giornata di sabato è terminata con un aperitivo release party in occasione del rilascio della versione di Ubuntu 22.04: pizza, focaccia e birra a volontà; si è fatta anche la foto di gruppo.&lt;/p&gt;
&lt;p&gt;Il risultato dei lavori di questi due giorni indicano che c’è sempre fermento nel mondo open source, tanti nuovi progetti e aggiornamenti di quelli esistenti, che c'è il coinvolgimento di giovani sviluppatori e che serve sempre il supporto di tutti sia a livello economico che intellettuale.&lt;/p&gt;
&lt;p&gt;&lt;img alt="L'aula principale, con le sedie in distanziamento sociale" src="https://www.gl-como.it/images/las2022/IMG_20220429_084758.jpg"&gt;&lt;/p&gt;
&lt;p&gt;In attesa della prossima edizione vi consiglio di &lt;a href="https://linuxappsummit.org/"&gt;visitare il sito&lt;/a&gt; e di approfondire gli &lt;a href="https://conf.linuxappsummit.org/event/4/timetable/#all"&gt;argomenti trattati&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Buone cose a tutti.&lt;/p&gt;</content><category term="Articoli"/></entry><entry><title>Cinema web in javascript - Parte 1</title><link href="https://www.gl-como.it/cinema-web-in-javascript-parte-1/" rel="alternate"/><published>2022-02-10T16:00:00+01:00</published><updated>2022-02-10T16:00:00+01:00</updated><author><name>fabrix</name></author><id>tag:www.gl-como.it,2022-02-10:/cinema-web-in-javascript-parte-1/</id><summary type="html">&lt;p&gt;Proviamo a scrivere un'applicazione web che permette di vedere un video con gli 
amici e di commentarlo via chat.&lt;/p&gt;
&lt;p&gt;L'idea è di avere un lettore video in una pagina web che carica un file video 
da un url e che sia controllato da uno dei partecipanti.&lt;/p&gt;
&lt;p&gt;Avremo quindi un utente …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Proviamo a scrivere un'applicazione web che permette di vedere un video con gli 
amici e di commentarlo via chat.&lt;/p&gt;
&lt;p&gt;L'idea è di avere un lettore video in una pagina web che carica un file video 
da un url e che sia controllato da uno dei partecipanti.&lt;/p&gt;
&lt;p&gt;Avremo quindi un utente (che chiameremo 'controllore') crea una "stanza", 
decide l'url del video che sarà riprodotto e che controlla il lettore video.
Gli altri utenti quando si connettono alla stanza vedranno caricarsi il video 
scelto e la riproduzione sarà sincrona con quella del 'controllore', che invierà
una serie di messaggi per far sapere se è in pausa o in play, e a che posizione 
del video si trova.&lt;/p&gt;
&lt;p&gt;Abbiamo quindi bisogno di un sistema per inviare e ricevere messaggi tra diversi
computer attraverso javascript.&lt;/p&gt;
&lt;p&gt;Potremmo scrivere un server che distribuisce i messaggi ai vari client collegati
via websocket, ma scrivere e mettere online un server simile è troppo complicato
per ora.&lt;/p&gt;
&lt;p&gt;Potremmo utilizzare webrtc e mettere in comunicazione i browser tra di loro, ma 
serve comunque un sistema per inizializzare le connessioni, attraverso un 
software lato server (e siamo come sopra) o attraverso un canale di 
comunicazione alternativo (per esempio scambiandosi delle stringhe di 
connessione via email o via chat esterne), ma è scomodo.
Per di più il codice javascript per gestire tutto questo diventa alquanto
complicato.&lt;/p&gt;
&lt;p&gt;L'idea per questo esperimento è di utilizzare &lt;a href="https://github.com/binwiederhier/ntfy"&gt;ntfy&lt;/a&gt;
che è un server di pub-sub basato su http: messaggi inviati a un "topic" con una
richiesta HTTP POST vengono girati a tutti i client in ascolto su quel topic. 
E siccome per questo esperimento non vogliamo gestire niente lato server, 
utilizzeremo il servizio gestito dallo stesso progetto: &lt;a href="https://ntfy.sh/"&gt;https://ntfy.sh/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Scrviamo del codice&lt;/h2&gt;
&lt;p&gt;Cominciamo con la nostra pagina:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;hidden&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;start&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;video_url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;URL video:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;video_url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;start_button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Start&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;video&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;player&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hidden&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;video&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;room_link&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;#&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;app.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Una pagina molto semplice: abbiamo un campo di testo un cui inserire l'indirizzo
del video da riprodurre e il lettore video, che teniamo nascosto finchè 
l'utente non pigia "Start".&lt;/p&gt;
&lt;p&gt;Il codice della nostra applicazione è tutto nel file &lt;code&gt;app.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_start_form&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;start&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_video_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;video_url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_start_button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;start_button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;player&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_room_link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;room_link&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;iniziamo salvandoci un riferimento agli elementi nella pagina che dovremo 
modificare.&lt;/p&gt;
&lt;p&gt;Definiamo poi un po' di variabili:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="c1"&gt;// topic su ntfy che useremo per inviare e ricevere i messaggi&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;video_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;// l&amp;#39;url del video che stiamo vedendo&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;last_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;// questo è l&amp;#39;ultima posizione nel video che abbiamo notificato&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;player_state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// stato del lettore, &amp;quot;stop&amp;quot; &amp;quot;play&amp;quot; &amp;quot;pause&amp;quot;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;im_control&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;// siamo noi i controllori nella stanza?&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MIN_T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// tempo minimo tra le notifiche&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eventSource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Teniamo traccia del topic (che è una stringa di testo e che genereremo 
casualmente), e dell'url del video.
Il lettore ci notifica a ogni cambio della posizione nel video.
Se inviamo un messaggio per ogni aggiornamento finisce che generiamo troppo 
traffico inutile, quindi teniamo traccia dell'ultima posizione inviata e quando 
la nostra posizione cambia più di un tot di secondi, allora inviamo una nuova 
notifica. 
Questa quantità è definita nella costante &lt;code&gt;MIN_T&lt;/code&gt;, qui definita a 5 secondi.
Ovviamente le notifiche le inviamo solo se siamo i controllori, e siamo i 
controllori se abbiamo creato noi la stanza, nel qual caso la variabile 
&lt;code&gt;im_control&lt;/code&gt; sarà &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;L'utima variabile, &lt;code&gt;eventSource&lt;/code&gt; la vediamo dopo, è l'oggetto che riceve i 
messaggi da ntfy.&lt;/p&gt;
&lt;p&gt;Ora definiamo qualche funzione.&lt;/p&gt;
&lt;p&gt;Per prima la funzione che invia i messaggi:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;send_message_sync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;im_control&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;last_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;type&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sync&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;url&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;video_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;state&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;player_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;currenttime&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://ntfy.sh/&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ntfy prende solo testo, quindi nella funzion costruiamo un messaggio e lo 
convertiamo in JSON per inviarlo al topic con una richiesta POST.&lt;/p&gt;
&lt;p&gt;Il messaggio conterrà:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;il tipo di messaggio&lt;/li&gt;
&lt;li&gt;l'url del video che stiamo guardando&lt;/li&gt;
&lt;li&gt;lo stato del lettore&lt;/li&gt;
&lt;li&gt;la posizione del lettore in secondi&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il tipo di messaggio è &lt;code&gt;sync&lt;/code&gt;, ed è l'unico che vedremo per ora. 
In una versione più avanzata la pagina conterrà anche una chat, quindi ci sarà
anche un messaggio di tipo &lt;code&gt;chat&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Poi definiamo la funzione che crea una nuova stanza:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;create_room&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;video_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;get_required_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e_video_url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;video_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;room_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;im_control&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;room_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Qui recuperiamo il valore inserito nel campo di testo, che non deve essere 
vuoto, nel caso usciamo subito dalla funzione.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;get_required_value()&lt;/code&gt; è una funzione di supporto che controlla che il campo
non sia vuoto e nel caso imposta il campo stesso come non valido.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;get_required_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setCustomValidity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Field required.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setCustomValidity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Tornando a &lt;code&gt;create_room()&lt;/code&gt;, generiamo un nuovo id random e impostiamo 
&lt;code&gt;im_control&lt;/code&gt; a &lt;code&gt;true&lt;/code&gt;: l'utente che crea la stanza è il controllore.&lt;/p&gt;
&lt;p&gt;Chiamiamo quindi &lt;code&gt;start()&lt;/code&gt; che avvia la stanza:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;room_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;room_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;e_start_form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hidden&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hidden&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;e_room_link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;e_room_link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;eventSource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;EventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://ntfy.sh/&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/sse&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;eventSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on_message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;video_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;video_url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Il &lt;code&gt;room_id&lt;/code&gt; diventa il topic che useremo con ntfy.&lt;/p&gt;
&lt;p&gt;Nascondiamo la form e mostriamo il player video, e aggiorniamo il link chiamato
&lt;code&gt;room_link&lt;/code&gt; in modo che l'utente possa copiare il link diretto alla stanza, 
compreso del topic. Il link sarà &lt;code&gt;http//server/percorso/#topic&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Creiamo poi l'oggetto &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource"&gt;&lt;code&gt;EventSource&lt;/code&gt;&lt;/a&gt;.
&lt;code&gt;EventSource&lt;/code&gt; è un oggetto del browser che implementa un client &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events"&gt;Server-side events&lt;/a&gt;.
Una volta connesso, viene mantenuta la connessione aperta e ogni volta che il 
server genera un nuovo messaggio l'oggetto emette l'evento &lt;code&gt;message&lt;/code&gt; che noi
gestiamo nella funzione &lt;code&gt;on_message()&lt;/code&gt; che vediamo sotto.&lt;/p&gt;
&lt;p&gt;Questo ci permette di ricevere da ntfy i messaggi che il controllore invia al
topic.&lt;/p&gt;
&lt;p&gt;Per concludere la funzione, se è stato definito un url video, lo facciamo 
caricare al lettore.&lt;/p&gt;
&lt;p&gt;Per chiudere il funzionamento della pagina per il controllore, gestiamo 
un po' di eventi.&lt;/p&gt;
&lt;p&gt;Quando l'utente clicca su "Start", creiamo la stanza:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;e_start_button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;click&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;create_room&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Quando la posizione del lettore nel video cambia, emette l'evento 'timeupdate',
se il tempo corrente è diverso di più di &lt;code&gt;MIN_T&lt;/code&gt;, inviamo il messaggio di sync:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;timeupdate&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;last_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MIN_T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;send_message_sync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Aggiorniamo lo stato del lettore quando passa a &lt;code&gt;play&lt;/code&gt; o &lt;code&gt;pausa&lt;/code&gt;, inviano un
messaggio di sync. Inviamo "stop" quando il video finisce.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;play&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;player_state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;play&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;send_message_sync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pause&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;player_state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pause&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;send_message_sync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ended&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;player_state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;send_message_sync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;E con questo abbiamo terminato la parte di codice che riguarda il controllore.
Ora vediamo cosa ci serve per far funzionare gli altri partecipanti.&lt;/p&gt;
&lt;h2&gt;E gli spettatori?&lt;/h2&gt;
&lt;p&gt;Inanzitutto dobbiamo ricevere i messaggi inviati dal controllore e reagire di 
conseguenza. Quindi definiamo la funzione &lt;code&gt;on_message()&lt;/code&gt; che gestisce l'evento 
&lt;code&gt;message&lt;/code&gt; dell'oggetto &lt;code&gt;EventSource&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;il parametro &lt;code&gt;e&lt;/code&gt; è un oggetto che contiene i dettagli dell'evento.
In questo caso ci sarà una proprietà &lt;code&gt;data&lt;/code&gt; che sarà il json ricevuto dal server.
Questo json contiene a sua volta una proprietà &lt;code&gt;message&lt;/code&gt; che contiene il messaggio 
che il controllore ha effettivamente inviato al topic.&lt;/p&gt;
&lt;p&gt;Una volta estratto quello possiamo reagire di conseguenza:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sync&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// il messaggio è di tipo sync&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// se sono il controllore, ho inviato io il messaggio,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// non ho bisogno di reagire.&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;im_control&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// se il lettore video ha un indirizzo diverso da quello&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// nel messaggio, dobbiamo aggiornarlo&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// Questo permette agli spettatori di caricare il video&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// la prima volta che ricevono un messaggio di sync&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentSrc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// se il mio lettore video è più di MIN_T lontano dalla posizione&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// del messaggio allora mi riposiziono.&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// questo lascia al player un po&amp;#39; di gioco per gestire ritardi di&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// buffering senza essere sempre spostato mentre cerca di recuperare&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// nuovi dati video.&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currenttime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MIN_T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currenttime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// se lo stato del player del controllore è diverso da quello locale&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// aggiorniamoci!&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// se arriva lo stato stop, &amp;quot;scarico&amp;quot; il video dal lettore&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// impostando un url vuoto&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;player_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pause&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;play&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;play&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;L'utente spettatore per entrare nella chat deve utilizzare il link che riporta 
l'id della stanza come &lt;a href="https://en.wikipedia.org/wiki/URI_fragment"&gt;fragment&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Quindi al caricamento della pagina controlliamo se c'è un id nell'url, e nel 
caso avviamo la stanza:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;L'api javascript lo chiama &lt;code&gt;hash&lt;/code&gt;. Il valore riporta anche il &lt;code&gt;#&lt;/code&gt; iniziale, che 
togliamo. Quello che resta, se c'è, è l'id della nostra stanza, che quindi 
passiamo a &lt;code&gt;start()&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Rendiamo felice il browser&lt;/h2&gt;
&lt;p&gt;A questo punto abbiamo la possibiltà di creare una stanza con un player 
che sarà sincronizzato tra i vari spettatori, più o meno qualche secondo
(ma nelle mie prove riesce ad essere abbastanza preciso).&lt;/p&gt;
&lt;p&gt;Quando uno spettatore entra nella stanza il player non ha sorgente che verrà
caricata al primo messaggio di sync.&lt;/p&gt;
&lt;p&gt;Se il video si trova su un dominio differente, il fatto di caricare l'url via 
javascript potrebbe far scattare qualche estensione blocca-pubblicità. Non 
possiamo fare molto, salvo servire il file video dallo stesso dominio su cui 
si trova la pagina.&lt;/p&gt;
&lt;p&gt;Al primo messaggio di sync che arriva con stato &lt;code&gt;play&lt;/code&gt;, invochiamo 
&lt;code&gt;e_player.play()&lt;/code&gt;. Questo fa sciuramente scattare la protezione del browser che 
blocca l'autoplay dei video. L'utente deve eseguire un'azione sul browser che 
porta a far partire il video, ma nel nostro caso non è così, e quindi viene 
bloccato.&lt;/p&gt;
&lt;p&gt;Per bypassare il problema, aggiungiamo un div con un messaggio di avviso 
nell'html, giusto sotto il player video, e lo teniamo nascosto:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;...
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;video&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;player&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hidden&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="cm"&gt;&amp;lt;!--👇 questo è nuovo --&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;alert&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hidden&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Per avviare il video premi &amp;quot;play&amp;quot;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;aggiungiamo una nuova variabile&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_start_form&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;start&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_video_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;video_url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_start_button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;start_button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;player&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_room_link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;room_link&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// 👇 questo è nuovo&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_alert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;alert&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;e modifichiamo la funzione &lt;code&gt;on_message()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ora se lo stato corrente locale è &lt;code&gt;stop&lt;/code&gt; (stato in cui si trova lo script 
all'avvio), e il messaggio di sync riporta &lt;code&gt;play&lt;/code&gt; mostriamo il messaggio di avviso.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentSrc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// 👇 questo è nuovo&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// il browser ci impedisce di passare da &amp;quot;stop&amp;quot; a &amp;quot;play&amp;quot; la prima volta&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// usando javascript, quindi mostriamo il messaggio di avviso&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player_state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;play&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;e_alert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hidden&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;E per terminare, nasconiamo il messaggio di avviso quando il lettore passa in
&lt;code&gt;play&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;e_player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;play&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;player_state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;play&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;send_message_sync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// 👇 questo è nuovo&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;e_alert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hidden&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Conlusioni e note&lt;/h2&gt;
&lt;p&gt;Ovviamente il codice qui descritto è pittosto semplice e non gestisce eventuali 
errori, così come non tiene conto di alcuni casi 'strani'.&lt;/p&gt;
&lt;p&gt;Per esempio, nel caso il file sorgente sia un mp4 "frammentato", il browser non 
puo' sapere quanto è lungo tutto il video, ma solo quanto è lungo fino al 
frammento caricato. Diciamo che ogni frammento è un minuto di video, appena 
caricato il video il browser pensa che &lt;em&gt;tutto&lt;/em&gt; il video duri un minuto. Poi a 
una successiva richiesta, riceve il frammento successivo ed ora pensa che 
duri due minuti.&lt;/p&gt;
&lt;p&gt;Diciamo che il nostro "controllore" è a 1m39s, quindi già nel secondo frammento, 
quando uno spettatore si collega.&lt;/p&gt;
&lt;p&gt;Appena collegato lo spettatore riceve un messaggio di sync che gli da l'url da 
caricare e lo fa saltare a 1m39s. Pero' il browser dello spettatore ha fin'ora 
caricato solo il primo frammento e quindi pensa che il video duri solo un 
minuto. Quindi il lettore salterà alla fine del frammento e andrà in stop, e il 
browser smetterà di chiedere nuovi frammenti, lasciando lo spettatore con il 
video fermo che non prosegue.&lt;/p&gt;
&lt;p&gt;Vi lascio il piacere di risolvere questi problemi.&lt;/p&gt;
&lt;p&gt;Un'altra cosa da notare è che utilizzare così ntfy.sh non è bello, ed è facile 
incappare nei limiti di richieste. Ma è comunque possibile installarsi il proprio 
server ntfy e farci ciò che si vuole.&lt;/p&gt;
&lt;p&gt;Per ultimo, è chiaro che abbiamo accuratamente evitato di parlare di CSS, salvo 
per ciò che è utile al funzionamento (come la classe &lt;code&gt;.hidden&lt;/code&gt;).
Di CSS non ne parleremo proprio, lascio a voi il piacere di creare una veste 
grafica interessante.&lt;/p&gt;
&lt;p&gt;Nel prossimo articolo proviamo ad aggiungere una chat.&lt;/p&gt;
&lt;p&gt;Il codice scritto fin'ora lo trovate &lt;a href="https://paste.sr.ht/~fabrixxm/31ec6c649eba7ee3b803a84d3382517d37dcad6d"&gt;qui&lt;/a&gt;&lt;/p&gt;</content><category term="Articoli"/></entry><entry><title>Serata Lesana - un gestore di collezioni</title><link href="https://www.gl-como.it/serata-lesana/" rel="alternate"/><published>2021-09-21T13:00:00+02:00</published><updated>2021-09-21T13:00:00+02:00</updated><author><name>fabrix</name></author><id>tag:www.gl-como.it,2021-09-21:/serata-lesana/</id><summary type="html">&lt;p&gt;È online la presentazione del progetto Lesana e derivati tenuta durante la serata di giovedì 16 scorso.&lt;/p&gt;
&lt;p&gt;I video sono pubblicati sul canale GL-Como su peertube.uno &lt;a href="https://peertube.uno/video-channels/glcomo/videos"&gt;@glcomo@peertube.uno&lt;/a&gt;, 
che è possibile seguire con un account sul fediverso (Friendica, Mastodon, etc)&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://peertube.uno/videos/watch/b986ad97-51d9-4c04-b093-3abf0bd99242"&gt;Lesana - un gestore di collezioni - parte 1&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://peertube.uno/videos/watch/b986ad97-51d9-4c04-b093-3abf0bd99242"&gt;&lt;img alt="L'interfaccia CLI di Lesana durante la presentazione" src="https://peertube.uno/lazy-static/previews/4063cdd2-a1db-44ff-90b8-f485400be736.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://peertube.uno/videos/watch/b2d12fbe-bc2a-4746-b859-a59813872f2a"&gt;Lesana …&lt;/a&gt;&lt;/h2&gt;</summary><content type="html">&lt;p&gt;È online la presentazione del progetto Lesana e derivati tenuta durante la serata di giovedì 16 scorso.&lt;/p&gt;
&lt;p&gt;I video sono pubblicati sul canale GL-Como su peertube.uno &lt;a href="https://peertube.uno/video-channels/glcomo/videos"&gt;@glcomo@peertube.uno&lt;/a&gt;, 
che è possibile seguire con un account sul fediverso (Friendica, Mastodon, etc)&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://peertube.uno/videos/watch/b986ad97-51d9-4c04-b093-3abf0bd99242"&gt;Lesana - un gestore di collezioni - parte 1&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://peertube.uno/videos/watch/b986ad97-51d9-4c04-b093-3abf0bd99242"&gt;&lt;img alt="L'interfaccia CLI di Lesana durante la presentazione" src="https://peertube.uno/lazy-static/previews/4063cdd2-a1db-44ff-90b8-f485400be736.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://peertube.uno/videos/watch/b2d12fbe-bc2a-4746-b859-a59813872f2a"&gt;Lesana - un gestore di collezioni - parte 2&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://peertube.uno/videos/watch/b2d12fbe-bc2a-4746-b859-a59813872f2a"&gt;&lt;img alt="L'interfaccia GUI di Collector durante la presentazione" src="https://peertube.uno/lazy-static/previews/4d103fd3-0626-4ec7-8170-a2b5f789758a.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I link:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lesana - &lt;a href="https://lesana.trueelena.org/"&gt;https://lesana.trueelena.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Lesana Web : interfaccia web a una collezione Lesana - &lt;a href="https://git.sr.ht/~fabrixxm/lesanaweb"&gt;https://git.sr.ht/~fabrixxm/lesanaweb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Collector : applicazione Python/Gtk3 per getire una collezione Lesana - &lt;a href="https://git.sr.ht/~fabrixxm/Collector"&gt;https://git.sr.ht/~fabrixxm/Collector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="Eventi"/></entry><entry><title>Serata su lesana, software di gestione di inventari</title><link href="https://www.gl-como.it/serata-lesana-gestione-inventari/" rel="alternate"/><published>2021-09-14T10:00:00+02:00</published><updated>2021-09-14T10:00:00+02:00</updated><author><name>fabrix</name></author><id>tag:www.gl-como.it,2021-09-14:/serata-lesana-gestione-inventari/</id><summary type="html">&lt;p&gt;&lt;strong&gt;Giovedì 16 settembre&lt;/strong&gt; il GL-Como torna a fare serate a tema: stavolta
l'argomento è &lt;code&gt;lesana&lt;/code&gt;, un programma per gestire inventari di collezioni,
come libri, musica, componenti di elettronica, abbastanza versatile da
poter essere usato ad esempio per un issue tracker personale.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;lesana&lt;/code&gt; è un programma da riga di comando ed …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;strong&gt;Giovedì 16 settembre&lt;/strong&gt; il GL-Como torna a fare serate a tema: stavolta
l'argomento è &lt;code&gt;lesana&lt;/code&gt;, un programma per gestire inventari di collezioni,
come libri, musica, componenti di elettronica, abbastanza versatile da
poter essere usato ad esempio per un issue tracker personale.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;lesana&lt;/code&gt; è un programma da riga di comando ed una libreria python, e ne
esistono già un'interfaccia grafica scritta in GTK e un visualizzatore
web.&lt;/p&gt;
&lt;p&gt;Saranno presenti gli autori di lesana e delle interfacce grafiche.&lt;/p&gt;
&lt;p&gt;L'appuntamento è per &lt;strong&gt;giovedì 16 settembre 2021 alle 21:00 &lt;/strong&gt;all'indirizzo&lt;/p&gt;
&lt;p&gt;&lt;a href="https://live.gl-como.it/view.html#GL-Como"&gt;https://live.gl-como.it/view.html#GL-Como&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;dove saranno disponibili lo streaming e una chat per interagire con i relatori.&lt;/p&gt;
&lt;p&gt;Maggiori informazioni su lesana si trovano sul sito del progetto:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://live.gl-como.it/view.html#GL-Como"&gt;https://lesana.trueelena.org/&lt;/a&gt;&lt;/p&gt;</content><category term="Annunci, Corsi"/></entry><entry><title>Opencast Alpha n.51</title><link href="https://www.gl-como.it/opencast-alpha-n-51/" rel="alternate"/><published>2021-04-07T10:00:00+02:00</published><updated>2021-04-07T10:00:00+02:00</updated><author><name>fabrix</name></author><id>tag:www.gl-como.it,2021-04-07:/opencast-alpha-n-51/</id><summary type="html">&lt;p&gt;&lt;img alt="Icona OpenCast code:alpha" src="https://www.gl-como.it/opencast/logo_icon_alpha-150x150.png" title="Icona OpenCast code:alpha"&gt;&lt;/p&gt;
&lt;p&gt;La puntata numero 51, con una parvenza di regolarità, dedicata all'imminente 
release di &lt;a href="https://www.debian.org/"&gt;Debian&lt;/a&gt; &lt;em&gt;Bullseye&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Scarica:
&lt;a href="https://archive.org/download/opencast51/opencast51.ogg" title="Scarica OpenCast Alpha nel formato libero e di qualità Ogg/Vorbis! Non ne resterai deluso!"&gt;Ogg/Vorbis&lt;/a&gt;
/
&lt;a href="https://archive.org/download/opencast51/opencast51.mp3" title="Se proprio devi, puoi scaricare OpenCast Alpha nel non ottimale formato chiuso e proprietario Mp3"&gt;Mp3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I links&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.debian.org/releases/bullseye/"&gt;Debian “bullseye” Release Information&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://release.debian.org/bullseye/freeze_policy.html"&gt;Bullseye Freeze Timeline and Policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.debian.org/releases/bullseye/releasenotes"&gt;Debian 11 -- Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.debian.org/CUPSAirPrint?action=show&amp;amp;redirect=AirPrint"&gt;CUPSAirPrint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bits.debian.org/2021/02/arduino-back-in-debian.html"&gt;Arduino is back on Debian&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I link di Opencast li trovate anche sulla &lt;a href="https://linkopedia.gl-como.it/search.html?q=updated%3A%222021-04-02%22"&gt;Linkopedia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Abbiamo …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;img alt="Icona OpenCast code:alpha" src="https://www.gl-como.it/opencast/logo_icon_alpha-150x150.png" title="Icona OpenCast code:alpha"&gt;&lt;/p&gt;
&lt;p&gt;La puntata numero 51, con una parvenza di regolarità, dedicata all'imminente 
release di &lt;a href="https://www.debian.org/"&gt;Debian&lt;/a&gt; &lt;em&gt;Bullseye&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Scarica:
&lt;a href="https://archive.org/download/opencast51/opencast51.ogg" title="Scarica OpenCast Alpha nel formato libero e di qualità Ogg/Vorbis! Non ne resterai deluso!"&gt;Ogg/Vorbis&lt;/a&gt;
/
&lt;a href="https://archive.org/download/opencast51/opencast51.mp3" title="Se proprio devi, puoi scaricare OpenCast Alpha nel non ottimale formato chiuso e proprietario Mp3"&gt;Mp3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I links&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.debian.org/releases/bullseye/"&gt;Debian “bullseye” Release Information&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://release.debian.org/bullseye/freeze_policy.html"&gt;Bullseye Freeze Timeline and Policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.debian.org/releases/bullseye/releasenotes"&gt;Debian 11 -- Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.debian.org/CUPSAirPrint?action=show&amp;amp;redirect=AirPrint"&gt;CUPSAirPrint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bits.debian.org/2021/02/arduino-back-in-debian.html"&gt;Arduino is back on Debian&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I link di Opencast li trovate anche sulla &lt;a href="https://linkopedia.gl-como.it/search.html?q=updated%3A%222021-04-02%22"&gt;Linkopedia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Abbiamo registrato con &lt;a href="https://wiki.mumble.info/wiki/Main_Page"&gt;Mumble&lt;/a&gt;, ognuno a casa
propria.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://creativecommons.org/licenses/by-nc-nd/3.0/it/"&gt;&lt;img alt="Creative Commons Attribuzione-Non commerciale-Non opere derivate 3.0 - Italia" src="https://www.gl-como.it/opencast/cc-by-nc-nd.png" title="Creative Commons Attribuzione-Non commerciale-Non opere derivate 3.0 - Italia"&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="OpenCast"/></entry><entry><title>Installare un server minetest da usare con gli amici</title><link href="https://www.gl-como.it/minetest/" rel="alternate"/><published>2021-04-05T23:42:00+02:00</published><updated>2021-04-05T23:42:00+02:00</updated><author><name>valhalla</name></author><id>tag:www.gl-como.it,2021-04-05:/minetest/</id><summary type="html">&lt;p&gt;Dopo mesi di lockdown in cui si sono incontrati gli amici solo tramite
videoconferenza (ovviamente sul &lt;a class="reference external" href="https://www.gl-como.it/installare_jitsi/"&gt;proprio server jitsi&lt;/a&gt;), viene voglia di
interagire di più, e cosa c'è di meglio che farlo in una comoda sala da
té costruita su dei pioppi, o sul fondo di una miniera scavando ferro …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Dopo mesi di lockdown in cui si sono incontrati gli amici solo tramite
videoconferenza (ovviamente sul &lt;a class="reference external" href="https://www.gl-como.it/installare_jitsi/"&gt;proprio server jitsi&lt;/a&gt;), viene voglia di
interagire di più, e cosa c'è di meglio che farlo in una comoda sala da
té costruita su dei pioppi, o sul fondo di una miniera scavando ferro,
oro, diamanti e MESE?&lt;/p&gt;
&lt;img alt="La sala da tè di sera" src="https://www.gl-como.it/images/minetest_tea_hall.png" /&gt;
&lt;p&gt;MESE? sì, sto parlando del minerale originale del mondo di &lt;a class="reference external" href="https://www.minetest.net/"&gt;Minetest&lt;/a&gt;, un
gioco libero della stessa famiglia del proprietario Minecraft, in cui si
interagisce in vari modi con un mondo composto da cubi di materiale
vario.&lt;/p&gt;
&lt;p&gt;Uno dei punti di forza di minetest è la varietà di mod disponibili (e la
facilità con cui se ne possono scrivere di personalizzati, usando il
linguaggio lua), per cui l'esperienza di gioco può essere resa molto
diversa a seconda dei gusti dei partecipanti: un server può offrire uno
spazio per costruire strutture, un'altro un mondo pieno di pericoli da
esplorare ed un'altro ancora può dare spazio a giocatori che vogliono
combattere tra di loro o contro mostri.&lt;/p&gt;
&lt;p&gt;In questo articolo ci concentreremo soprattutto sulla prima opzione, ma
dando indicazioni su cosa modificare se si preferiscono le altre due (o
varie combinazioni).&lt;/p&gt;
&lt;div class="section" id="installazione"&gt;
&lt;h2&gt;Installazione&lt;/h2&gt;
&lt;p&gt;Partiamo come al solito da un'installazione di debian su una macchina
che possa essere raggiunta da internet (la solita vps, o un SBC in casa
con un router opportunamente configurato); ad aprile 2021 possiamo
scegliere se usare buster (l'attuale stable) più buster-backports oppure
bullseye, la prossima stable di imminente rilascio.&lt;/p&gt;
&lt;p&gt;Installiamo quindi il server minetest ed altri strumenti utili:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# apt install -t buster-backports minetest-server minetestmapper
&lt;/pre&gt;
&lt;p&gt;e poi la selezione di mod che vogliamo usare, ad esempio:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# apt install -t buster-backports \
      minetest-mod-basic-materials minetest-mod-craftguide \
      minetest-mod-homedecor minetest-mod-unifieddyes \
      minetest-mod-moreblocks minetest-mod-moreores
&lt;/pre&gt;
&lt;p&gt;Quindi passiamo a configurare il nostro server modificando il file
&lt;tt class="docutils literal"&gt;/etc/minetest/minetest.conf&lt;/tt&gt;: di sicuro vogliamo modificare nome
(&lt;tt class="docutils literal"&gt;server_name&lt;/tt&gt;), descrizione (&lt;tt class="docutils literal"&gt;server_description&lt;/tt&gt;) e volendo il
messaggio mostrato al collegamento (&lt;tt class="docutils literal"&gt;motd&lt;/tt&gt;), e possiamo scegliere se
vogliamo annunciare il server sull'elenco comune dove chiunque possa
giocarci (per iniziare, o comunque se si vuole giocare solo con i propri
amici, conviene lasciare &lt;tt class="docutils literal"&gt;server_announce = false&lt;/tt&gt;) e se impostare una
password comune per poter entrare nel server (&lt;tt class="docutils literal"&gt;default_password&lt;/tt&gt;).&lt;/p&gt;
&lt;p&gt;Decidiamo poi, nello stesso file, che tipo di esperienza di gioco
vogliamo avere, impostando &lt;tt class="docutils literal"&gt;creative_mode&lt;/tt&gt; (&lt;tt class="docutils literal"&gt;true&lt;/tt&gt; per far sì che
i giocatori abbiano a disposizione tutti i materiali senza doverli
andare a cercare, &lt;tt class="docutils literal"&gt;false&lt;/tt&gt; per la modalità classica in cui li si deve
trovare e scavare), &lt;tt class="docutils literal"&gt;enable_damage&lt;/tt&gt; (utile se si vuole rendere
pericolosa l'esplorazione o si vuole fare combattimento) ed
&lt;tt class="docutils literal"&gt;enable_pvp&lt;/tt&gt; (per il combattimento tra giocatori).&lt;/p&gt;
&lt;p&gt;Inoltre in &lt;tt class="docutils literal"&gt;default_privs&lt;/tt&gt; impostiamo che privilegi devono avere di
default i giocatori: il minimo consueto è &lt;tt class="docutils literal"&gt;interact, shout&lt;/tt&gt;, ma in un
mondo incentrato sulla costruzione più che sull'esplorazione può essere
utile aggiungere anche &lt;tt class="docutils literal"&gt;fly, fast, home&lt;/tt&gt;.
La &lt;a class="reference external" href="https://wiki.minetest.net/Privileges"&gt;wiki di minetest documenta i privilegi a disposizione&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Infine, impostiamo il nome di un giocatore che avrà i privilegi di
admin, nella variabile &lt;tt class="docutils literal"&gt;name&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;A questo punto passiamo nella directory del mondo,
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/var/games/minetest-server/.minetest/worlds/world/&lt;/span&gt;&lt;/tt&gt; e configuriamo
quali mod vogliamo abilitare in &lt;tt class="docutils literal"&gt;world.mt&lt;/tt&gt;; può essere necessario fare
un po' di prove, dato che le dipendenze non vengono abilitate
automaticamente, ma ad esempio questa è una combinazione funzionante con
i mod installati prima:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
load_mod_unifieddyes = true
load_mod_moreores = true
load_mod_moreblocks = true
load_mod_plasmascreen = false
load_mod_lavalamp = false
load_mod_building_blocks = false
load_mod_homedecor_3d_extras = false
load_mod_lrfurn = false
load_mod_homedecor = true
load_mod_inbox = false
load_mod_basic_materials = true
load_mod_itemframes = false
load_mod_computer = false
load_mod_craftguide = true
load_mod_currency = true
load_mod_fake_fire = false
load_mod_homedecor_i18n = true
&lt;/pre&gt;
&lt;p&gt;Infine, riavviare minetest-server per applicare le modifiche:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# service minetest-server restart
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="primi-passi-nel-mondo"&gt;
&lt;h2&gt;Primi passi nel mondo&lt;/h2&gt;
&lt;p&gt;A questo punto, sul proprio pc, installare il gioco minetest (in debian
il pacchetto &lt;tt class="docutils literal"&gt;minetest&lt;/tt&gt; e collegarsi al server usando il nome utente
che si è selezionato come admin, ricordandosi di cambiare la password
(con il comando &lt;tt class="docutils literal"&gt;/setpassword&lt;/tt&gt;) appena entrati.&lt;/p&gt;
&lt;p&gt;Dopo aver esplorato un po' si può scegliere un posto da cui partire con
la costruzione: lo si può impostare come propria “casa” usando il
comando &lt;tt class="docutils literal"&gt;/sethome&lt;/tt&gt;, ma può anche valere la pena di impostarlo come
punto di partenza per tutti i giocatori: premere F5 per abilitare la
modalità di debug e scoprire le coordinate in cui ci si trova, quindi
tornare sul server, modificare &lt;tt class="docutils literal"&gt;static_spawnpoint&lt;/tt&gt; in
&lt;tt class="docutils literal"&gt;/etc/minetest/minetest.conf&lt;/tt&gt; e riavviare nuovamente il server.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://wiki.minetest.net/Server_commands"&gt;Ulteriori comandi utili sono documentati sulla wiki di minetest&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="ulteriori-migliorie"&gt;
&lt;h2&gt;Ulteriori migliorie&lt;/h2&gt;
&lt;div class="section" id="mappa-del-mondo"&gt;
&lt;h3&gt;Mappa del mondo&lt;/h3&gt;
&lt;p&gt;Il comando &lt;tt class="docutils literal"&gt;/usr/games/minetestmapper&lt;/tt&gt; può essere usato per generare
una mappa generale di tutto quanto è mai stato esplorato; può essere una
buona idea aggiungere a crontab una riga come:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
05 8,16,0 * * * /usr/games/minetestmapper -i /var/games/minetest-server/.minetest/worlds/world/ -o /var/www/minetest/world_map.png --drawscale
&lt;/pre&gt;
&lt;p&gt;per rigenerare la mappa ogni 8 ore, lasciandola in una directory dove
può essere servita da un eventuale server web presente sulla macchina.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="backup"&gt;
&lt;h3&gt;“backup”&lt;/h3&gt;
&lt;p&gt;Per avere una forma minimale di backup, possiamo sfruttare git: creiamo
un repository nella directory del mondo:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# cd /var/games/minetest-server/.minetest/worlds/world/
# git init
&lt;/pre&gt;
&lt;p&gt;e poi impostiamo a crontab un commit automatico quotidiano:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
14 4 * * * cd /var/games/minetest-server/.minetest/worlds/world/ ; git add . ; git commit -m 'daily commit'
&lt;/pre&gt;
&lt;p&gt;Da un altro computer facciamo poi un clone di questo repository, e
mantenendo aggiornato quel clone potremo recuperare il mondo in caso di
catastrofi.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="come-proseguire"&gt;
&lt;h2&gt;Come proseguire&lt;/h2&gt;
&lt;p&gt;A questo punto si è pronti per passare la password del server ai propri
amici ed iniziare a giocare, il che probabilmente vuol dire che non si
avrà più tempo per ulteriori configurazioni :)&lt;/p&gt;
&lt;p&gt;Se invece si riesce a mantenere l'autocontrollo, queste sono alcune idee
di migliorie su cui si può lavorare.&lt;/p&gt;
&lt;div class="section" id="ulteriori-mod"&gt;
&lt;h3&gt;Ulteriori mod&lt;/h3&gt;
&lt;p&gt;I mod disponibili per minetest-sono estremamente numerosi e vari: la
maggior parte viene &lt;a class="reference external" href="https://forum.minetest.net/viewforum.php?f=11&amp;amp;sid=ba2aaffa60901aecf4f81e28829c7972"&gt;pubblicata sul forum di minetest&lt;/a&gt;,
ma una buona selezione di mod scelti per la loro qualità e affidabili
può essere ottenuta cercando i mod che sono stati pacchettizzati per
debian con il comando:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ apt search minetest-mod
&lt;/pre&gt;
&lt;p&gt;l'elenco è troppo lungo per parlarne in questo articolo, ma una buona
idea può essere quella di installarli localmente sul proprio pc,
provarli su un mondo single player e installare sul server ciò che si è
trovato particolarmente interessante.&lt;/p&gt;
&lt;p&gt;In alternativa, se tra i propri amici ci sono dei giocatori di minetest,
di sicuro avranno un elenco di mod che trovano interessanti da
suggerire.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="piu-mondi-sullo-stesso-server"&gt;
&lt;h3&gt;Più mondi sullo stesso server&lt;/h3&gt;
&lt;p&gt;Il pacchetto debian di minetest-server è predisposto per poter gestire
più mondi: per farlo basta creare una nuova configurazione in
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/minetest/&amp;lt;nome&amp;gt;.conf&lt;/span&gt;&lt;/tt&gt; e far partire il server con &lt;tt class="docutils literal"&gt;systemctl
start &lt;span class="pre"&gt;minetest-server&amp;#64;pvp.service&lt;/span&gt;&lt;/tt&gt;; sarà necessario usare una porta
diversa, e ulteriori dettagli si possono trovare nel file
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/share/doc/minetest-server/README.Debian&lt;/span&gt;&lt;/tt&gt; installato dal
pacchetto del server.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="backend-piu-adatti-a-server-affollati"&gt;
&lt;h3&gt;Backend più adatti a server affollati&lt;/h3&gt;
&lt;p&gt;Il backend di default di minetest è sqlite3, che non è in grado di
scalare e supportare server con molti utenti: se si pensa di averne
bisogno sono disponibili &lt;a class="reference external" href="https://wiki.minetest.net/Database_backends"&gt;ulteriori backend come redis o postgresql, la
cui configurazione è spiegata sempre sulla wiki di minetest&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ovviamente, in questo caso non sarà più possibile usare git per
effettuare un backup del mondo, ma sarà necessario avere una strategia
di backup più tradizionale.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusione"&gt;
&lt;h2&gt;Conclusione&lt;/h2&gt;
&lt;p&gt;Qui avrebbe dovuto esserci una conclusione, ma chi doveva scriverla era
impegnato a giocare…&lt;/p&gt;
&lt;img alt="Il tunnel sotto la montagna" src="https://www.gl-como.it/images/minetest_panama.png" /&gt;
&lt;/div&gt;
</content><category term="articoli"/></entry><entry><title>Opencast Alpha n.50</title><link href="https://www.gl-como.it/opencast-alpha-n-50/" rel="alternate"/><published>2021-03-02T10:00:00+01:00</published><updated>2021-03-02T10:00:00+01:00</updated><author><name>fabrix</name></author><id>tag:www.gl-como.it,2021-03-02:/opencast-alpha-n-50/</id><summary type="html">&lt;p&gt;&lt;img alt="Icona OpenCast code:alpha" src="https://www.gl-como.it/opencast/logo_icon_alpha-150x150.png" title="Icona OpenCast code:alpha"&gt;&lt;/p&gt;
&lt;p&gt;La puntata numero 50, la puntata d'oro!&lt;/p&gt;
&lt;p&gt;Non pensavate che ci saremmo arivati? Nemmeno noi, e invece eccoci qui!&lt;/p&gt;
&lt;p&gt;Una puntata dedicata al giovane mondo di linux sugli smartphone: parleremo di 
pinephone, cosa funziona, cosa ancora non funziona, le nostre esperienze.&lt;/p&gt;
&lt;p&gt;Scarica:
&lt;a href="https://archive.org/download/OpencastAlpha50/opencast50.ogg" title="Scarica OpenCast Alpha nel formato libero e di qualità Ogg/Vorbis! Non ne resterai deluso!"&gt;Ogg/Vorbis&lt;/a&gt;
/
&lt;a href="https://archive.org/download/OpencastAlpha50/opencast50.mp3" title="Se proprio devi, puoi scaricare OpenCast Alpha nel non ottimale formato chiuso e proprietario Mp3"&gt;Mp3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I links&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pine64.org/pinephone/"&gt;PinePhone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://puri.sm/products/librem-5/"&gt;Librem 5 …&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;&lt;img alt="Icona OpenCast code:alpha" src="https://www.gl-como.it/opencast/logo_icon_alpha-150x150.png" title="Icona OpenCast code:alpha"&gt;&lt;/p&gt;
&lt;p&gt;La puntata numero 50, la puntata d'oro!&lt;/p&gt;
&lt;p&gt;Non pensavate che ci saremmo arivati? Nemmeno noi, e invece eccoci qui!&lt;/p&gt;
&lt;p&gt;Una puntata dedicata al giovane mondo di linux sugli smartphone: parleremo di 
pinephone, cosa funziona, cosa ancora non funziona, le nostre esperienze.&lt;/p&gt;
&lt;p&gt;Scarica:
&lt;a href="https://archive.org/download/OpencastAlpha50/opencast50.ogg" title="Scarica OpenCast Alpha nel formato libero e di qualità Ogg/Vorbis! Non ne resterai deluso!"&gt;Ogg/Vorbis&lt;/a&gt;
/
&lt;a href="https://archive.org/download/OpencastAlpha50/opencast50.mp3" title="Se proprio devi, puoi scaricare OpenCast Alpha nel non ottimale formato chiuso e proprietario Mp3"&gt;Mp3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I links&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pine64.org/pinephone/"&gt;PinePhone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://puri.sm/products/librem-5/"&gt;Librem 5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mobian-project.org/"&gt;Mobian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://manjaro.org/"&gt;Manjaro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.pine64.org/index.php/PinePhone_Software_Releases"&gt;Lista dei SO disponibili per il PinePhone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://source.puri.sm/Librem5/phosh"&gt;Phosh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.plasma-mobile.org/"&gt;Plasma Mobile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/retext-project/retext"&gt;ReText&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://xournal.sourceforge.net/"&gt;Xournal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://xournalpp.github.io/"&gt;Xournal++&lt;/a&gt;, una versione riscritta ed aggiornata di Xournal (non è stato testato, nel caso &lt;a href="https://social.gl-como.it/profile/gl-como"&gt;fateci sapere&lt;/a&gt;!)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confy.kirgroup.net/"&gt;Confy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.toastfreeware.priv.at/confclerk"&gt;ConfClerk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.videolan.org/"&gt;VLC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dino.im/"&gt;Dino&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gajim.org/"&gt;Gajim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/IBBoard/cawbird/"&gt;Cawbird&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bleakgrey/tootle/"&gt;Tootle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anbox.io/"&gt;Anbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.mobian-project.org/doku.php?id=anbox"&gt;Anbox sul wiki di mobian&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- I link di Opencast li trovate anche sulla [Linkopedia](https://linkopedia.gl-como.it/search.html?q=updated%3A%222021-02-12+20%22) --&gt;

&lt;p&gt;Abbiamo registrato con &lt;a href="https://wiki.mumble.info/wiki/Main_Page"&gt;Mumble&lt;/a&gt;, ognuno a casa
propria.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://creativecommons.org/licenses/by-nc-nd/3.0/it/"&gt;&lt;img alt="Creative Commons Attribuzione-Non commerciale-Non opere derivate 3.0 - Italia" src="https://www.gl-como.it/opencast/cc-by-nc-nd.png" title="Creative Commons Attribuzione-Non commerciale-Non opere derivate 3.0 - Italia"&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="OpenCast"/></entry><entry><title>Opencast Alpha n.49</title><link href="https://www.gl-como.it/opencast-alpha-n-49/" rel="alternate"/><published>2021-02-12T23:00:00+01:00</published><updated>2021-02-12T23:00:00+01:00</updated><author><name>fabrix</name></author><id>tag:www.gl-como.it,2021-02-12:/opencast-alpha-n-49/</id><summary type="html">&lt;p&gt;&lt;img alt="Icona OpenCast code:alpha" src="https://www.gl-como.it/opencast/logo_icon_alpha-150x150.png" title="Icona OpenCast code:alpha"&gt;&lt;/p&gt;
&lt;p&gt;Dopo anni di silenzio audio (ma non video), una nuova puntata di OpenCast!&lt;/p&gt;
&lt;p&gt;Dopo la prima edizione interamente online di FOSDEM, raccontiamoci com'è andata, 
cosa abbiamo visto e cosa ci siamo segnati da rivedere!&lt;/p&gt;
&lt;p&gt;Scarica:
&lt;a href="https://archive.org/download/OpencastAlpha49/opencast49.ogg" title="Scarica OpenCast Alpha nel formato libero e di qualità Ogg/Vorbis! Non ne resterai deluso!"&gt;Ogg/Vorbis&lt;/a&gt;
/
&lt;a href="https://archive.org/download/OpencastAlpha49/opencast49.mp3" title="Se proprio devi, puoi scaricare OpenCast Alpha nel non ottimale formato chiuso e proprietario Mp3"&gt;Mp3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I links&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://osmocom.org/"&gt;Open Source Mobile Communications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fosdem.org/2021/schedule/event/retro_freedos/"&gt;Working on DOS in 2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fosdem.org/2021/schedule/event/git_learning_game/"&gt;Building …&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;&lt;img alt="Icona OpenCast code:alpha" src="https://www.gl-como.it/opencast/logo_icon_alpha-150x150.png" title="Icona OpenCast code:alpha"&gt;&lt;/p&gt;
&lt;p&gt;Dopo anni di silenzio audio (ma non video), una nuova puntata di OpenCast!&lt;/p&gt;
&lt;p&gt;Dopo la prima edizione interamente online di FOSDEM, raccontiamoci com'è andata, 
cosa abbiamo visto e cosa ci siamo segnati da rivedere!&lt;/p&gt;
&lt;p&gt;Scarica:
&lt;a href="https://archive.org/download/OpencastAlpha49/opencast49.ogg" title="Scarica OpenCast Alpha nel formato libero e di qualità Ogg/Vorbis! Non ne resterai deluso!"&gt;Ogg/Vorbis&lt;/a&gt;
/
&lt;a href="https://archive.org/download/OpencastAlpha49/opencast49.mp3" title="Se proprio devi, puoi scaricare OpenCast Alpha nel non ottimale formato chiuso e proprietario Mp3"&gt;Mp3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I links&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://osmocom.org/"&gt;Open Source Mobile Communications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fosdem.org/2021/schedule/event/retro_freedos/"&gt;Working on DOS in 2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fosdem.org/2021/schedule/event/git_learning_game/"&gt;Building a Git learning game&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fosdem.org/2021/schedule/event/retro_ada/"&gt;Ada Lovelace and The Very First Computer Program&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fosdem.org/2021/schedule/event/fsr_openwifi_opensource_wifi_chip/"&gt;openwifi - opensource WiFi chip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fosdem.org/2021/schedule/event/tims_webrtc_history/"&gt;Why WebRTC is the way it is.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fosdem.org/2021/schedule/event/retro_emulation/"&gt;An emulator for my old system today and tomorrow ?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fosdem.org/2021/schedule/event/webrtc_development/"&gt;WebRTC shouldn't be this hard!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.mobian-project.org/doku.php?id=networking"&gt;networking [Mobian Wiki]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://aprs.gids.nl/nmea/"&gt;GPS - NMEA sentence information&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I link di Opencast li trovate anche sulla &lt;a href="https://linkopedia.gl-como.it/search.html?q=updated%3A%222021-02-12+20%22"&gt;Linkopedia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Abbiamo registrato con &lt;a href="https://wiki.mumble.info/wiki/Main_Page"&gt;Mumble&lt;/a&gt;, ognuno a casa
propria.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://creativecommons.org/licenses/by-nc-nd/3.0/it/"&gt;&lt;img alt="Creative Commons Attribuzione-Non commerciale-Non opere derivate 3.0 - Italia" src="https://www.gl-como.it/opencast/cc-by-nc-nd.png" title="Creative Commons Attribuzione-Non commerciale-Non opere derivate 3.0 - Italia"&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="OpenCast"/></entry><entry><title>Controllo vocale delle luci di casa</title><link href="https://www.gl-como.it/controllo-vocale-delle-luci-di-casa/" rel="alternate"/><published>2020-08-09T16:00:00+02:00</published><updated>2020-08-09T16:00:00+02:00</updated><author><name>fabrix</name></author><id>tag:www.gl-como.it,2020-08-09:/controllo-vocale-delle-luci-di-casa/</id><summary type="html">&lt;p&gt;Avendo in casa una lampada "smart" della serie Trådfri di IKEA a fare poco di 
"smart", un computer con linux, ed essendo capitato sul sito di &lt;a href="http://voice2json.org/"&gt;voice2json.org&lt;/a&gt; mi son detto: perchè non fare due più due più due?&lt;/p&gt;
&lt;p&gt;Ed ecco il risultato! (cinque... oh no.)&lt;/p&gt;
&lt;h2&gt;Step 1: controllare le …&lt;/h2&gt;</summary><content type="html">&lt;p&gt;Avendo in casa una lampada "smart" della serie Trådfri di IKEA a fare poco di 
"smart", un computer con linux, ed essendo capitato sul sito di &lt;a href="http://voice2json.org/"&gt;voice2json.org&lt;/a&gt; mi son detto: perchè non fare due più due più due?&lt;/p&gt;
&lt;p&gt;Ed ecco il risultato! (cinque... oh no.)&lt;/p&gt;
&lt;h2&gt;Step 1: controllare le luci&lt;/h2&gt;
&lt;p&gt;Cominciamo col poter controllare le luci da riga di comando.&lt;/p&gt;
&lt;p&gt;Cosa serve:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Una lampadina "smart" della serie Trådfri di IKEA&lt;/li&gt;
&lt;li&gt;Il suo gateway, già accoppiato e testato&lt;/li&gt;
&lt;li&gt;Un computer con linux&lt;/li&gt;
&lt;li&gt;bash, grep, sed&lt;/li&gt;
&lt;li&gt;coap-client (debian: &lt;a href="https://packages.debian.org/buster/libcoap2-bin"&gt;libcoap2-bin&lt;/a&gt;, AUR: &lt;a href="https://aur.archlinux.org/packages/coap-client/"&gt;coap-client&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;lo script "tradfri" che trovate qui &lt;a href="https://git.sr.ht/~fabrixxm/tradfri-cli/blob/master/tradfri"&gt;https://git.sr.ht/~fabrixxm/tradfri-cli/blob/master/tradfri&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Salviamo lo script in una cartella appropriata, p.e. &lt;code&gt;~/tradfri/&lt;/code&gt; e lo rendiamo
eseguibile.&lt;/p&gt;
&lt;p&gt;Da console lo eseguiamo una prima volta per fargli creare il file di 
configurazione vuoto: &lt;code&gt;~/tradfri/tradfri.rc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Editiamo il file di configurazione e inseriamo l'ip e il codice del gateway.
Possiamo anche inserire un nome utente a scelta, o lasciare che lo script ne 
generi uno a caso.&lt;/p&gt;
&lt;p&gt;Eseguiamo ancora lo script per fare login. Teoricamente dovrebbe salvare in
automatico la chiave di sessione nel file di configurazione, ma non funziona.
La chiave viene stampata a terminale e dovremmo copiarla nel file di configurazione
a mano.&lt;/p&gt;
&lt;p&gt;Ora possiamo verificare che funzioni:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;~/tradfri/tradfri&lt;span class="w"&gt; &lt;/span&gt;info
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;per avere informazioni sul gateway&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;~/tradfri/tradfri&lt;span class="w"&gt; &lt;/span&gt;devicelist
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;per avere un elenco degli id dei devices collegati al gateway&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;~/tradfri/tradfri&lt;span class="w"&gt; &lt;/span&gt;deviceinfo&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;per avere i dettagli di ogni device. Cerchiamo quello che corrisponde alla lampadina.
Nel mio caso è il device con id &lt;code&gt;65538&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Proviamo ad accendere e spegnere la lampadina:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;~/tradfri/tradfri&lt;span class="w"&gt; &lt;/span&gt;light&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;id&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;on
$&lt;span class="w"&gt; &lt;/span&gt;~/tradfri/tradfri&lt;span class="w"&gt; &lt;/span&gt;light&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;id&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;off
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Se tutto funziona ora dovremmo poter controllare la lampadina.&lt;/p&gt;
&lt;p&gt;Impostiamo un alias, un nome, per la lampadina. Diciamo che è la lampadina del 
salotto. La chiameremo quindi "salotto".&lt;/p&gt;
&lt;p&gt;Per impostare l'alias creiamo il file &lt;code&gt;~/tradfri/aliases.rc&lt;/code&gt; e ci scriviamo dentro:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;salotto=&amp;lt;id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;sostituendo l'id con quello della nostra lampadina. p.e:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;salotto=65538
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ora possiamo controllare la lampadina riferendoci al suo nome:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;~/tradfri/tradfri&lt;span class="w"&gt; &lt;/span&gt;light&lt;span class="w"&gt; &lt;/span&gt;salotto&lt;span class="w"&gt; &lt;/span&gt;on
$&lt;span class="w"&gt; &lt;/span&gt;~/tradfri/tradfri&lt;span class="w"&gt; &lt;/span&gt;light&lt;span class="w"&gt; &lt;/span&gt;salotto&lt;span class="w"&gt; &lt;/span&gt;off
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Step 2: controllo vocale&lt;/h2&gt;
&lt;p&gt;serve:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;lo stesso computer dello step 1&lt;/li&gt;
&lt;li&gt;microfono e (opzionalmente) casse&lt;/li&gt;
&lt;li&gt;aplay&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;pazienza&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Installiamo &lt;a href="http://voice2json.org/"&gt;voice2json&lt;/a&gt; seguendo la guida qui: 
&lt;a href="http://voice2json.org/install.html"&gt;http://voice2json.org/install.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;è disponibile un pacchetto debian che non ho provato, ma la compilazione da 
sorgente non è complicata.&lt;/p&gt;
&lt;p&gt;Il profilo in italiano da scaricare è qui &lt;a href="https://github.com/synesthesiam/it_pocketsphinx-cmu/releases"&gt;https://github.com/synesthesiam/it_pocketsphinx-cmu/releases&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Possiamo seguire la guida di voice2json per vedere che funzioni.&lt;/p&gt;
&lt;p&gt;Ho trovato che nel profilo in italiano il frasario (il contenuto del file 
&lt;a href="https://github.com/synesthesiam/it_pocketsphinx-cmu/blob/261415833d2f5290638debfaeb292f687b4531e6/sentences.ini"&gt;senteces.ini&lt;/a&gt;) non funziona benissimo,
in particolare la parola "lampada" non viene riconosciuta.
Si puo' sostituirla con la parola "luce" e &lt;a href="http://voice2json.org/commands.html#train-profile"&gt;rilanciare il training&lt;/a&gt; 
per avere risultati migliori.&lt;/p&gt;
&lt;p&gt;Una volta che &lt;code&gt;voice2json&lt;/code&gt; funziona, possiamo mettere assieme i pezzi.&lt;/p&gt;
&lt;p&gt;Innanzitutto modifichiamo il file &lt;code&gt;senteces.ini&lt;/code&gt; per adeguarsi alle nostre esigenze.
Cambiamo il contenuto in:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;[ChangeLightState]&lt;/span&gt;
&lt;span class="na"&gt;light_state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;(accendi | spegni){state}&lt;/span&gt;
&lt;span class="na"&gt;light_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;(soggiorno | garage){name}&lt;/span&gt;

&lt;span class="na"&gt;&amp;lt;light_state&amp;gt; [la] luce (in|del) &amp;lt;light_name&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Il nuovo file imposta quindi un solo "intent" (&lt;code&gt;ChangeLightState&lt;/code&gt;) e definisce
il nome delle lampade con una sola parola.&lt;/p&gt;
&lt;p&gt;Perchè l'accrocchio funzioni, i nomi delle lampade in questo file devono 
corrispondere ad almeno un alias del file &lt;code&gt;~/tradfri/aliases.rc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Seguendo questa guida abbiamo definito l'alias &lt;code&gt;soggiorno&lt;/code&gt; e quindi potremmo dire
"accendi la luce del soggiorno" o "spegni la luce in soggiorno".&lt;/p&gt;
&lt;p&gt;Scarichiamo lo script &lt;a href="https://git.sr.ht/~fabrixxm/tradfri-voice/blob/master/listen.sh"&gt;listen.sh&lt;/a&gt;, lo salviamo in &lt;code&gt;~/tradfri/&lt;/code&gt; e lo rendiamo eseguibile.&lt;/p&gt;
&lt;p&gt;Lo apriamo con un editor di testo per modificare i primi due percorsi:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;il percorso all'eseguibile di &lt;code&gt;voice2json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;il percorso allo script &lt;code&gt;tradfri&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ci servono anche tre file di suoni, che &lt;a href="https://git.sr.ht/~fabrixxm/tradfri-voice/tree/master/sounds"&gt;troviamo qui&lt;/a&gt; che vanno salvati in &lt;code&gt;~/tradfri/sounds/&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;nota:&lt;/em&gt; vi sarete accorti che arriva tutto da un repo git. Per fare più in fretta, 
possiamo clonare il repo &lt;a href="https://&amp;gt; git.sr.ht/~fabrixxm/tradfri-voice"&gt;https://git.sr.ht/~fabrixxm/tradfri-voice&lt;/a&gt; e scaricare così lo script, i suoni e il file 
&lt;code&gt;senteces.ini&lt;/code&gt; da copiare nel profilo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;E lo eseguiamo.&lt;/p&gt;
&lt;p&gt;Lo script resterà in attesa della frase di attivazione ("Hey, Mycroft"), 
stampando a terminale "Waiting for wake word...".&lt;br&gt;
Quando riconosce la frase di attivazione, esegue un suono di notifica e resta in
ascolto del comando (p.e. "accendi la luce del soggiorno").&lt;br&gt;
Terminato l'ascolto del comando, emette un nuovo suono di notifica e tenta di 
riconosce ed eseguire il comando.&lt;br&gt;
Se l'operazione va a buon fine vedremo accendersi la luce del soggiorno!&lt;br&gt;
Altrimenti un nuovo suono di notifica che segnala un errore viene emesso.&lt;br&gt;
Il ciclo poi si ripete e lo script si rimette in attesa della frase di 
attivazione.&lt;/p&gt;
&lt;h2&gt;Link e riferimenti&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://voice2json.org/"&gt;voice2json.org&lt;/a&gt; : Command-line tools for speech and intent recognition on Linux&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.sr.ht/~fabrixxm/tradfri-cli"&gt;tradfri-cli&lt;/a&gt; : a WiP commandline client for tradfri gateway&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.sr.ht/~fabrixxm/tradfri-voice"&gt;tradfri-voice&lt;/a&gt; : Experimental tradfri light voice control&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/glenndehaan/ikea-tradfri-coap-docs"&gt;ikea-tradfri-coap-docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;nota&lt;/em&gt;: questo post &lt;strong&gt;non&lt;/strong&gt; è sponsorizzato in nessuna maniera da IKEA, la quale
è proprietaria del marchio "Trådfri"&lt;/p&gt;
&lt;/blockquote&gt;</content><category term="Articoli"/></entry></feed>