Jitsi Videokonferenz

Dieser Fehler deutet darauf hin, dass Jitsi in Ihrem Browser keinen Zugriff auf den lokalen Speicher hat. Verwenden Sie einen Browser mit restriktiven Sicherheitseinstellungen, die verhindern könnten, dass Iframes den lokalen Speicher nutzen?

2 „Gefällt mir“

Etwas „Komisches

1 „Gefällt mir“

Ja – ich habe Chrome so eingestellt, dass Drittanbieter-Cookies nicht erlaubt sind. Wenn man diese Einstellung deaktiviert, wird das Problem behoben. Es wäre schön, wenn es eine Möglichkeit gäbe, auf diese Ursache hingewiesen zu werden, aber ich nehme an, dies ist ein seltenes Problem.

Gibt es in Jitsi eine Möglichkeit, einen Videoanruf in einem neuen Tab oder einem separaten Popup-Fenster zu öffnen? Ich frage mich, ob Nutzer während des Anrufs das Forum verlassen, um weiter zu surfen oder eine Antwort zum Thema hinzuzufügen.

2 „Gefällt mir“

Vielleicht nicht, da dies die csp_extensions in about.json komplizierter machen würde.

1 „Gefällt mir“

Nein, die aktuelle Version der Theme-Komponente fügt den Jitsi-API-Pfad bereits zu den CSP-Quellen hinzu. Dies wird durch diese Zeile in about.json bewerkstelligt, wie @Benjamin_D bereits angemerkt hat:

1 „Gefällt mir“

@pmusaraj Ist dieser neuere Schritt in Discourse selbst einstellbar?

:smiley: Ja, ich lerne es gerade im laufenden Betrieb…
Ich frage mich immer noch, warum diese Funktion: injected(eventName, injectedIntoContentWindow) usw. … die CSP nicht durchlässt.

Konsole

Content Security Policy: Die Einstellungen der Seite haben das Laden einer Ressource an eval („script-src“) blockiert. Quelle: (function injected(eventName, injectedIntoContentWindow)
{
let checkRequest;

/*

  • Wrapper für den Frame-Kontext
  • In einigen Randfällen führt Chrome Content-Skripte nicht innerhalb von Frames aus.
  • Webseiten haben begonnen, dies auszunutzen, um über das contentWindow eines Frames
  • auf ungewrappte APIs zuzugreifen (#4586, 5207). Daher müssen wir, bis Chrome
  • Content-Skripte konsistent für alle Frames ausführt, darauf achten, unsere
  • Wrapper beim Zugriff auf das contentWindow (neu) zu injizieren.
    */
    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”
);

// Offensichtlich existiert HTMLObjectElement.prototype.contentWindow in
// älteren Chrome-Versionen wie 51 nicht.
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);

}

/*

// Wenn wir über contentWindow in einen Frame injiziert wurden, können wir einfach
// die Kopie von checkRequest greifen, die uns vom Hauptdokument hinterlassen wurde.
// Andernfalls müssen wir sie jetzt zusammen mit den Event-Handler-Funktionen einrichten.
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}}));
};

}

// Nur vor dem Code der Seite aufzurufen, nicht gehärtet.
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 hat die Option (media.peerconnection.enabled), WebRTC zu deaktivieren.
// In diesem Fall ist RealRTCPeerConnection undefiniert.
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;
};

// Es wäre viel einfacher, die Methode .getConfiguration zu verwenden, um
// die normalisierte und sichere Konfiguration von der RTCPeerConnection-Instanz
// zu erhalten. Leider ist sie in Chrome unstable 59 noch nicht implementiert.
// Siehe 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 iteriert nicht durch pseudo-Arrays von 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)
    {
      // Der Aufruf von .close() wirft eine Ausnahme, wenn bereits geschlossen.
      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 (getestet mit 59) hat setConfiguration bereits implementiert,
// daher müssen wir diese Methode ebenfalls umhüllen, falls sie existiert.
// 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);

    // Rufen wir zuerst die echte Methode auf, damit sie die Konfiguration für uns validiert.
    // Außerdem ist es sinnvoll, da checkRequest ohnehin asynchron ist.
    realSetConfiguration(this, configuration);
    checkConfiguration(this, configuration);
  };
}

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

  let configuration = protectConfiguration(args[0]);

  // Da der alte webkitRTCPeerConnection-Konstruktor ein optionales
  // zweites Argument erwartet, müssen wir darauf achten, dieses weiterzugeben.
  // Notwendig für ältere Chrome-Versionen wie 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

Hast du deine Content Security Policy aktiviert?
Außerdem, bitte schick mir eine DM, wie du dieses coole console-Dropdown hinzugefügt hast. Gracias.

Verstanden! Das ist AdBlock, hat nichts mit Jitsi zu tun… :sweat_smile:

1 „Gefällt mir“

@sunjam Es gibt keinen neuen Schritt. Die Komponente whitelistet das erforderliche Skript automatisch.

1 „Gefällt mir“

Aktuell nicht, aber das wäre eine gute Verbesserung. Ich werde mich darum kümmern, sobald ich Zeit habe.

4 „Gefällt mir“

Super cool! Ein paar kurze Anmerkungen –

Auf Mobilgeräten ist der Link „Weiter zur App

2 „Gefällt mir“

[quote=“bts, Beitrag: 33, Thema: 146046”]
Beim Handy ist der Link „Weiter zur App

4 „Gefällt mir“

Hätten Sie vielleicht die Möglichkeit, das zu priorisieren? Wir testen gerade, Jitsi für unsere internen Chats zu nutzen.

Das Problem ist folgendes:

  1. Wir starten einen Thread als Veranstaltung über das Kalender-Plugin und fügen einen Jitsi-Call hinzu, der zu diesem Zeitpunkt stattfinden soll. Manchmal machen wir das nach einer Doodle-ähnlichen Umfrage im selben Thread. Der Jitsi-Link befindet sich im ersten Beitrag (OP).
  2. Der Call startet, alles läuft gut.
  3. Jemand nutzt denselben Tab, um etwas zu suchen oder im Thread zu antworten (zum Beispiel, um Minuten live zu verfassen) – und dabei wird der Call unterbrochen.

Es sollte nicht allzu schwierig sein, den Link so zu gestalten, dass er eine leere Seite öffnet, anstatt in einem Iframe eingebettet zu sein, oder? Nicht, dass ich die entsprechenden Fähigkeiten hätte!

2 „Gefällt mir“

Nun, @nathank, diese Komponente ist etwas übertrieben, wenn du nur einen Link zu einem Jitsi-Raum möchtest. Du kannst einfach https://meet.jit.si/ROOMID als Link einfügen, und das erledigt die Aufgabe ohne eine aufwendige Theme-Komponente.

Ich habe jedoch diese Option zur Komponente hinzugefügt: Du kannst nun wählen, ob du das Iframe für Mobilgeräte, Desktop oder beides verwenden möchtest:

Standardmäßig wird die Videokonferenz in einem Iframe geladen. Wenn diese Option deaktiviert ist, wird der Videoanruf im gesamten Fenster geöffnet. Bei der BigBlueButton-Videokonferenz war dies insbesondere auf Mobilgeräten wünschenswert, daher habe ich hier dasselbe umgesetzt.

Beachte auch, dass der Link zum Jitsi-Raum im Vollbildmodus nicht in einem neuen Tab geöffnet wird. Wenn du das möchtest, verwende ein Ankerelement mit target="_blank".

8 „Gefällt mir“

Für mich in Ordnung, für meine weniger technikaffinen Nutzer weniger! Danke für die Iframe-Erweiterung, super hilfreich. Ich bin mir nicht sicher, ob es sinnvoll ist, Iframe als Standard für Mobilgeräte zu verwenden; würdest du das ändern?

Ich habe anderweitig entdeckt, dass man die Seite „Jitsi-App herunterladen“, die auf Mobilgeräten erscheint, umgehen kann, indem man #config.disableDeepLinking=true zur Raum-ID hinzufügt:
https://meet.jit.si/DeinMeetingsNameHier#config.disableDeepLinking=true

4 „Gefällt mir“

Ich habe gerade versucht, diese Theme-Komponente zu unserer Discourse 2.7.0.beta3-Instanz hinzuzufügen, aber egal was ich tue, ich sehe kein zusätzliches Symbol in der Composer-Leiste, um auf die Konferenz-ID zu verlinken.

„Im Options-Dropdown anzeigen

Das ist es, was ich auf einer frischen Discourse-Website mit dieser aktivierten Option sehe:

1 „Gefällt mir“

Ja, das Problem ist jetzt gelöst, vielleicht lag es an einem Caching-Problem. Trotzdem danke für deine Antwort.

1 „Gefällt mir“

Ich liebe es wirklich, wie diese Theme-Komponente funktioniert. Ich kämpfe immer noch mit Jitsi-Audio-Problemen, ansonsten hätte alles reibungslos funktioniert. Ich habe mich gefragt, ob jemand derzeit Jitsi in seinem Forum verwendet und sich nicht darüber beschwert. Meine Audioprobleme sind vielleicht auf WebRTC zurückzuführen ---- hat jemand ähnliche Erfahrungen gemacht?

1 „Gefällt mir“