Videoconferenza Jitsi

Sembra che quell’errore indichi che Jitsi nel tuo browser non ha accesso all’archiviazione locale. Stai utilizzando un browser con impostazioni di sicurezza restrittive che potrebbero impedire agli iframe di usare l’archiviazione locale?

2 Mi Piace

qualcosa di “strano” nella mia console, anche alcuni errori, come ad esempio:

Content Security Policy: le impostazioni della pagina hanno bloccato il caricamento di una risorsa in eval (“script-src”). Origine: (function injected(eventName, injectedIntoContentWindow)
{

anche se funziona :thinking:

puoi provare una di queste istanze:
https://framatalk.org/accueil/fr/info/

1 Mi Piace

Sì, ho configurato Chrome per non consentire i cookie di terze parti. Disabilitando quella impostazione si risolve il problema. Sarebbe utile avere un modo per essere avvisati di questa causa, ma immagino che si tratti di un problema abbastanza raro.

Esiste un modo in Jitsi per aprire una chiamata video in una nuova scheda o in una finestra popup separata? Mi chiedo se le persone potrebbero cliccare altrove durante la chiamata per continuare a navigare nel forum mentre parlano o per aggiungere una risposta all’argomento.

2 Mi Piace

Forse no, poiché complicherebbe le csp_extensions in about.json

1 Mi Piace

No, la versione attuale del componente del tema aggiunge già il percorso dell’API Jitsi alle fonti CSP. È fatto da questa riga di about.json come ha notato @Benjamin_D:

1 Mi Piace

@pmusaraj È possibile impostare questo nuovo passaggio direttamente all’interno di Discourse?

:smiley: sì, sto imparando mentre procedo…
Mi chiedo ancora perché questa funzione: injected(eventName, injectedIntoContentWindow) ecc… non rispetti la CSP

console

Policy di Sicurezza dei Contenuti: le impostazioni della pagina hanno bloccato il caricamento di una risorsa in eval (“script-src”). Origine: (function injected(eventName, injectedIntoContentWindow)
{
let checkRequest;

/*

  • Wrapper per il contesto del frame
  • In alcuni casi limite, Chrome non esegue gli script di contenuto all’interno dei frame.
  • Alcuni siti web hanno iniziato a sfruttare questo fatto per accedere a API non incapsulate
  • tramite il contentWindow di un frame (#4586, 5207). Pertanto, finché Chrome non eseguirà
  • in modo coerente gli script di contenuto per tutti i frame, dobbiamo assicurarci di
  • (ri)iniettare i nostri wrapper quando viene accesso al contentWindow.
    */
    let injectedToString = Function.prototype.toString.bind(injected);
    let injectedFrames = new WeakSet();
    let injectedFramesAdd = WeakSet.prototype.add.bind(injectedFrames);
    let injectedFramesHas = WeakSet.prototype.has.bind(injectedFrames);

function injectIntoContentWindow(contentWindow)
{
if (contentWindow && !injectedFramesHas(contentWindow))
{
injectedFramesAdd(contentWindow);
try
{
contentWindow[eventName] = checkRequest;
contentWindow.eval(
“(” + injectedToString() + “)('” + eventName + “', true);”
);
delete contentWindow[eventName];
}
catch (e) {}
}
}

for (let element of [HTMLFrameElement, HTMLIFrameElement, HTMLObjectElement])
{
let contentDocumentDesc = Object.getOwnPropertyDescriptor(
element.prototype, “contentDocument”
);
let contentWindowDesc = Object.getOwnPropertyDescriptor(
element.prototype, “contentWindow”
);

// Sembra che in HTMLObjectElement.prototype.contentWindow non esista
// nelle versioni più vecchie di Chrome, come la 51.
if (!contentWindowDesc)
  continue;

let getContentDocument = Function.prototype.call.bind(
  contentDocumentDesc.get
);
let getContentWindow = Function.prototype.call.bind(
  contentWindowDesc.get
);

contentWindowDesc.get = function()
{
  let contentWindow = getContentWindow(this);
  injectIntoContentWindow(contentWindow);
  return contentWindow;
};
contentDocumentDesc.get = function()
{
  injectIntoContentWindow(getContentWindow(this));
  return getContentDocument(this);
};
Object.defineProperty(element.prototype, "contentWindow",
                      contentWindowDesc);
Object.defineProperty(element.prototype, "contentDocument",
                      contentDocumentDesc);

}

/*

// Se siamo stati iniettati in un frame tramite contentWindow, possiamo semplicemente
// prendere la copia di checkRequest lasciataci dal documento padre. Altrimenti
// dobbiamo impostarla ora, insieme alle funzioni di gestione degli eventi.
if (injectedIntoContentWindow)
checkRequest = window[eventName];
else
{
let addEventListener = document.addEventListener.bind(document);
let dispatchEvent = document.dispatchEvent.bind(document);
let removeEventListener = document.removeEventListener.bind(document);
checkRequest = (url, callback) =>
{
let incomingEventName = eventName + “-” + url;

  function listener(event)
  {
    callback(event.detail);
    removeEventListener(incomingEventName, listener);
  }
  addEventListener(incomingEventName, listener);

  dispatchEvent(new RealCustomEvent(eventName, {detail: {url}}));
};

}

// Da chiamare solo prima del codice della pagina, non è protetto.
function copyProperties(src, dest, properties)
{
for (let name of properties)
{
if (Object.prototype.hasOwnProperty.call(src, name))
{
Object.defineProperty(dest, name,
Object.getOwnPropertyDescriptor(src, name));
}
}
}

let RealRTCPeerConnection = window.RTCPeerConnection ||
window.webkitRTCPeerConnection;

// Firefox ha l’opzione (media.peerconnection.enabled) per disabilitare WebRTC,
// nel qual caso RealRTCPeerConnection è undefined.
if (typeof RealRTCPeerConnection != “undefined”)
{
let closeRTCPeerConnection = Function.prototype.call.bind(
RealRTCPeerConnection.prototype.close
);
let RealArray = Array;
let RealString = String;
let {create: createObject, defineProperty} = Object;

let normalizeUrl = url =>
{
  if (typeof url != "undefined")
    return RealString(url);
};

let safeCopyArray = (originalArray, transform) =>
{
  if (originalArray == null || typeof originalArray != "object")
    return originalArray;

  let safeArray = RealArray(originalArray.length);
  for (let i = 0; i < safeArray.length; i++)
  {
    defineProperty(safeArray, i, {
      configurable: false, enumerable: false, writable: false,
      value: transform(originalArray[i])
    });
  }
  defineProperty(safeArray, "length", {
    configurable: false, enumerable: false, writable: false,
    value: safeArray.length
  });
  return safeArray;
};

// Sarebbe molto più semplice usare il metodo .getConfiguration per ottenere
// la configurazione normalizzata e sicura dall'istanza RTCPeerConnection.
// Purtroppo non è implementato in Chrome unstable 59.
// Vedi https://www.chromestatus.com/feature/5271355306016768
let protectConfiguration = configuration =>
{
  if (configuration == null || typeof configuration != "object")
    return configuration;

  let iceServers = safeCopyArray(
    configuration.iceServers,
    iceServer =>
    {
      let {url, urls} = iceServer;

      // RTCPeerConnection non itera attraverso pseudo-array di urls.
      if (typeof urls != "undefined" && !(urls instanceof RealArray))
        urls = [urls];

      return createObject(iceServer, {
        url: {
          configurable: false, enumerable: false, writable: false,
          value: normalizeUrl(url)
        },
        urls: {
          configurable: false, enumerable: false, writable: false,
          value: safeCopyArray(urls, normalizeUrl)
        }
      });
    }
  );

  return createObject(configuration, {
    iceServers: {
      configurable: false, enumerable: false, writable: false,
      value: iceServers
    }
  });
};

let checkUrl = (peerconnection, url) =>
{
  checkRequest(url, blocked =>
  {
    if (blocked)
    {
      // Chiamare .close() lancia un'eccezione se già chiuso.
      try
      {
        closeRTCPeerConnection(peerconnection);
      }
      catch (e) {}
    }
  });
};

let checkConfiguration = (peerconnection, configuration) =>
{
  if (configuration && configuration.iceServers)
  {
    for (let i = 0; i < configuration.iceServers.length; i++)
    {
      let iceServer = configuration.iceServers[i];
      if (iceServer)
      {
        if (iceServer.url)
          checkUrl(peerconnection, iceServer.url);

        if (iceServer.urls)
        {
          for (let j = 0; j < iceServer.urls.length; j++)
            checkUrl(peerconnection, iceServer.urls[j]);
        }
      }
    }
  }
};

// Chrome unstable (testato con la 59) ha già implementato
// setConfiguration, quindi dobbiamo avvolgerlo anche se esiste.
// https://www.chromestatus.com/feature/5596193748942848
if (RealRTCPeerConnection.prototype.setConfiguration)
{
  let realSetConfiguration = Function.prototype.call.bind(
    RealRTCPeerConnection.prototype.setConfiguration
  );

  RealRTCPeerConnection.prototype.setConfiguration = function(configuration)
  {
    configuration = protectConfiguration(configuration);

    // Chiamare prima il metodo reale, così da far validare la configurazione
    // per noi. Inoltre, dato che checkRequest è asincrono, tanto vale.
    realSetConfiguration(this, configuration);
    checkConfiguration(this, configuration);
  };
}

let WrappedRTCPeerConnection = function(...args)
{
  if (!(this instanceof WrappedRTCPeerConnection))
    return RealRTCPeerConnection();

  let configuration = protectConfiguration(args[0]);

  // Poiché il vecchio costruttore webkitRTCPeerConnection accetta un secondo
  // argomento opzionale, dobbiamo assicurarci di passarlo. Necessario
  // per versioni più vecchie di Chrome come la 51.
  let constraints = undefined;
  if (args.length > 1)
    constraints = args[1];

  let peerconnection = new RealRTCPeerConnection(configuration,
                                                 constraints);
  checkConfiguration(peerconnection, configuration);
  return peerconnection;
};

WrappedRTCPeerConnection.prototype = RealRTCPeerConnection.prototype;

let boundWrappedRTCPeerConnection = WrappedRTCPeerConnection.bind();
copyProperties(RealRTCPeerConnection, boundWrappedRTCPeerConnection,
               ["generateCertificate", "name", "prototype"]);
RealRTCPeerConnection.prototype.constructor = boundWrappedRTCPeerConnection;

if ("RTCPeerConnection" in window)
  window.RTCPeerConnection = boundWrappedRTCPeerConnection;
if ("webkitRTCPeerConnection" in window)
  window.webkitRTCPeerConnection = boundWrappedRTCPeerConnection;

}
})(‘abp-request-o6i81ij12x’, true);. bd833f87-4c58-41b0-a0cd-15b978834599:27:22

Hai attivato la tua Content Security Policy?
Inoltre, per favore mandami un messaggio privato spiegando come hai aggiunto quella comoda console a tendina. Gracias.

Ho capito!
È un adblock, non c’entra nulla con Jitsi… :sweat_smile:

1 Mi Piace

@sunjam non c’è un nuovo passaggio: il componente mette automaticamente in whitelist lo script che deve essere messo in whitelist.

1 Mi Piace

Al momento no, ma sarebbe un ottimo miglioramento. Ci lavorerò appena avrò un momento.

4 Mi Piace

Molto figo! Un paio di commenti rapidi—

Per i dispositivi mobili, dopo aver cliccato il pulsante per avviare l’evento, il link “Prosegui verso l’app” a volte non è cliccabile! Funziona quando visualizzato direttamente in Safari, ma non in Chrome e nemmeno nell’app iOS di Discourse Hub…

Concordo anche su questo: un metodo rapido per generare automaticamente un ID stanza potrebbe essere molto utile. Penso soprattutto per chi non è già molto familiare con Jitsi, non è del tutto chiaro se in quel campo si debba inserire una stanza esistente o se si stia generando una nuova al volo semplicemente usando una stringa casuale arbitraria.

2 Mi Piace

Ottima individuazione! Questo accade perché il link “Continua all’app” effettua una richiesta URL personalizzata che inizia con org.jitsi.meet://. Ho inserito questo schema di query personalizzato nell’elenco consentito di DiscourseHub, il che dovrebbe risolvere il problema nella prossima versione dell’app. (Purtroppo, non credo esista una soluzione per questo in Chrome.)

Ho aggiunto un’opzione di generazione casuale: gli utenti devono solo lasciare vuoto il campo ID e verrà generato un ID. Ho anche aggiunto del testo esplicativo:

Tieni presente che l’ID non conterrà parole, ma sarà una combinazione casuale di lettere e numeri.

4 Mi Piace

C’è la possibilità di dare priorità a questo? Stiamo sperimentando l’uso di Jitsi per le nostre chat interne.

Il problema è questo:

  1. avviamo un thread come Evento utilizzando il plugin Calendario e includiamo una chiamata Jitsi programmata per quell’orario. A volte lo facciamo dopo un sondaggio simile a Doodle nello stesso thread. Il link Jitsi viene inserito nel primo post.
  2. La chiamata parte, tutto ok.
  3. Qualcuno usa la stessa scheda per cercare qualcosa o rispondere nel thread (ad esempio per scrivere il verbale in tempo reale) e viene espulso dalla chiamata.

Non dovrebbe essere troppo difficile far sì che il link apra una nuova scheda vuota invece di essere incorporato in un iframe, vero? Non che io abbia le competenze per farlo!

2 Mi Piace

Beh, @nathank, questo componente è un po’ eccessivo se ti serve solo un link a una stanza Jitsi. Puoi aggiungere https://meet.jit.si/ROOMID a un link e questo dovrebbe bastare, senza bisogno di un componente tema sofisticato.

Tuttavia, ho aggiunto questa opzione al componente: ora puoi scegliere se vuoi l’iframe per mobile, desktop o entrambi:

Per impostazione predefinita, la videoconferenza viene caricata in un iframe. Se deselezionato, verrà aperta la chiamata video in una finestra intera. Con BigBlueButton video conference questa era un’opzione desiderabile, specialmente su mobile, quindi ho fatto lo stesso qui.

Nota anche che il link alla stanza Jitsi in finestra intera non si apre in una nuova scheda. Se vuoi questo comportamento, usa un anchor con target="_blank".

8 Mi Piace

Per me va bene, ma meno per i miei utenti meno esperti di tecnologia! Grazie per il potenziamento dell’iframe, è super utile. Non sono sicuro che abbia senso impostare l’iframe come predefinito per i dispositivi mobili; potresti valutare di cambiarlo?

Ho scoperto altrove che è possibile bypassare la pagina “Scarica l’app Jitsi” che appare sui dispositivi mobili aggiungendo #config.disableDeepLinking=true all’ID della stanza:
https://meet.jit.si/YourMeetingNameHere#config.disableDeepLinking=true

4 Mi Piace

Ho appena provato ad aggiungere questo componente del tema alla nostra istanza di Discourse 2.7.0.beta3, ma non importa cosa faccio, non vedo un’icona aggiuntiva nella barra del compositore per collegare all’ID della conferenza.

“Mostra nel menu a tendina delle opzioni” nella configurazione del componente del tema è selezionata, quindi dovrebbe essere visibile. Qualche idea su dove cercare l’errore?

Ecco cosa vedo su un nuovo sito Discourse con quell’opzione selezionata:

1 Mi Piace

Sì, il problema è stato risolto ora, forse era un problema di caching.
Comunque grazie per la tua risposta.

1 Mi Piace

Adoro come funziona questo componente tematico. Sto ancora lottando con i problemi audio di Jitsi, altrimenti tutto avrebbe funzionato senza intoppi. Mi stavo chiedendo se qualcuno stesse attualmente utilizzando Jitsi nel proprio forum e non avesse lamentele al riguardo. I miei problemi audio sono forse dovuti a WebRTC ---- qualcuno ha anche esperienze simili?

1 Mi Piace