# JavaScript APIs

Source: https://www.veeting.com/de/developer-documentation/javascript-apis

## Übersicht

Mit der JavaScript-API steuern Sie aus Ihrer eigenen Seite heraus einen laufenden Meetingraum. Sie muten Teilnehmer, lesen die Teilnehmerliste, senden Chat-Nachrichten, wechseln Geräte und hören auf Events. Das gelingt Ihnen mit einem eingebetteten iFrame genauso wie mit den Web-Komponenten von Veeting Blocks.

## Wo Ihr Code läuft, entscheidet über den Zugang zur API

Das ist wichtiger als alles andere auf dieser Seite, klären Sie es deshalb zuerst.

| Ihr Code läuft                                                               | So erreichen Sie die API                                                                    |
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| **Im Meetingraum selbst**, also in Veeting Blocks oder in einem eigenen Tool | Direkt über den Handler. Die vollständige API, samt Rückgabewerten.                         |
| **In der Seite rund um einen eingebetteten iFrame**                          | Über `postMessage`. Sie rufen jede Methode ohne Rückgabewert auf und empfangen jedes Event. |

Betten Sie den Raum als iFrame ein, sind Ihre Seite und der Meetingraum zwei verschiedene Dokumente, meist auf zwei verschiedenen Origins. Ihre Seite kommt nicht an das `window` des Raums heran und der Raum nicht an Ihres. Alles überquert diese Grenze als Nachricht.

**Unterschiedliche Origins sind vorgesehen und unterstützt.** Der Raum läuft normalerweise unter Ihrer eigenen White-Label-Domain, `meeting.example.com`, während Ihre Anwendung unter `app.example.com` oder ganz woanders liegt. Beides braucht keine Sonderbehandlung: Eine andere Subdomain und eine völlig andere Domain verhalten sich hier gleich.

## Den Raum aus der umgebenden Seite steuern

### Events empfangen

Der Meetingraum sendet jedes Event an sein übergeordnetes Fenster. Hören Sie darauf:

```javascript
window.addEventListener("message", (message) => {
  // Always check the origin. The room posts to any parent, so this check is
  // yours to make, and without it any page could forge these events.
  if (message.origin !== "https://<DOMAIN-NAME>") {
    return;
  }

  const { event, payload } = message.data;

  if (event === "joined") {
    console.log("The meeting room is ready");
  }

  if (event === "participantsUpdated") {
    console.log(`${payload.length} participants in the room`);
  }
});
```

`event` ist einer der 12 Namen weiter unten, `payload` genau das, was dieses Event mitführt. Sie müssen nichts abonnieren: Jedes Event geht an die übergeordnete Seite, ob Sie darauf hören oder nicht.

### Methoden aufrufen

Senden Sie dem iFrame eine Nachricht mit dem Namen der Methode und ihren Argumenten:

```javascript
const room = document.getElementById("meeting-frame");

room.contentWindow.postMessage({
  action: "muteAudio",
  payload: [true]
}, "https://<DOMAIN-NAME>");
```

`action` ist der Methodenname, `payload` sind ihre Argumente in der richtigen Reihenfolge als Array. Eine Methode ohne Argumente braucht trotzdem `payload: []` oder gar nichts.

Weil der Weg nur in eine Richtung führt, gelten zwei Grenzen:

- **Sie erhalten keinen Rückgabewert.** Alles, was mit `get` beginnt, nützt über diese Grenze hinweg also nichts. Lesen Sie die Teilnehmer aus dem Event `participantsUpdated` statt über `getParticipantsList()`, und die Raumkonfiguration aus `meetingRoomConfigUpdated`.
- **Sie erhalten auch keinen Fehler.** Vertippen Sie sich im Methodennamen, geschieht nichts und niemand meldet es. Prüfen Sie den Namen gegen die Tabellen weiter unten.

### Ein vollständiges Beispiel

Ihr eigener Mute-Button und Ihr eigener Teilnehmerzähler, in Ihrer eigenen Seite, ausserhalb des iFrames:

```html
<button id="mute">Mute</button>
<span id="count">0</span> participants

<iframe id="meeting-frame"
  src="https://<DOMAIN-NAME>/meeting/<MEETING-ID>"
  allow="microphone;camera;encrypted-media;fullscreen;autoplay;display-capture;layout-animations;">
</iframe>

<script>
  const ROOM_ORIGIN = "https://<DOMAIN-NAME>";
  const room = document.getElementById("meeting-frame");
  let muted = false;

  window.addEventListener("message", (message) => {
    if (message.origin !== ROOM_ORIGIN) {
      return;
    }
    if (message.data.event === "participantsUpdated") {
      document.getElementById("count").textContent = message.data.payload.length;
    }
  });

  document.getElementById("mute").addEventListener("click", () => {
    muted = !muted;
    room.contentWindow.postMessage({ action: "muteAudio", payload: [muted] }, ROOM_ORIGIN);
  });
</script>
```

## Den Handler erhalten

Der übrige Teil dieses Abschnitts gilt für Code, der **im** Meetingraum läuft: Veeting Blocks oder ein eigenes Tool. Betten Sie einen iFrame ein, verwenden Sie stattdessen die Nachrichten oben.

Die API existiert erst, wenn der Meetingraum vollständig geladen ist. Aus einem Skript, das beim Laden der Seite läuft, erreichen Sie sie deshalb noch nicht. Definieren Sie stattdessen auf `window` eine Funktion `wlvmrApiReady`. Sobald der Raum bereit ist, ruft er sie auf und übergibt Ihnen den Namen des Objekts, unter dem er sich registriert hat.

```javascript
window["wlvmrApiReady"] = (handler) => {
  if (!window[handler]) {
    console.error(`Handler window.${handler} not found!`);
    return;
  }

  // Every API method lives on this object
  window[handler].muteAudio(true);
};
```

Daraus folgen zwei Regeln. Beide gehen häufig vergessen:

1. **Definieren Sie `wlvmrApiReady`, bevor der Meetingraum lädt.** Weisen Sie die Funktion später zu, ist der Aufruf längst erfolgt und Sie erhalten den Handler nie.
2. **Schreiben Sie den Handler-Namen nicht fest in den Code.** Sie bekommen ihn als Argument. Greifen Sie immer über `window[handler]` zu, nie über einen festen Namen.

## Ein durchgängiges Beispiel

Warten Sie auf den Raum, abonnieren Sie Events und rufen Sie erst dann Methoden auf:

```javascript
window["wlvmrApiReady"] = (handler) => {
  if (!window[handler]) {
    console.error(`Handler window.${handler} not found!`);
    return;
  }

  const api = window[handler];

  api.on("joined", () => {
    console.log("The meeting room is ready to use");
  });

  api.on("participantsUpdated", (participants) => {
    console.log(`${participants.length} participants in the room`);
  });

  api.on("chatMessage", (message) => {
    console.log(`${message.fromParticipantName}: ${message.message}`);
  });

  api.setAudioInputDeviceId("default", false);
  api.setVideoInputDeviceId("default", true);
};
```

## Events

Sie abonnieren mit `on(event, callback)`. Es gibt 12 Events.

| Event                      | Löst aus, wenn                                            | Payload                   |
| -------------------------- | --------------------------------------------------------- | ------------------------- |
| `beforeConnecting`         | Der Raum gleich seine Medien verbindet                    | keine                     |
| `connected`                | Der WebSocket zur Veeting-API steht und verwendet wird    | keine                     |
| `joined`                   | Der Meetingraum einsatzbereit ist                         | keine                     |
| `disconnected`             | Der WebSocket zur Veeting-API getrennt oder ungenutzt ist | keine                     |
| `leave`                    | Der Teilnehmer das Meeting verlässt                       | keine                     |
| `participantsUpdated`      | Jemand beitritt, geht oder seinen Zustand ändert          | `IApiParticipant[]`       |
| `chatMessage`              | Eine Gruppen-Chatnachricht eintrifft                      | `IApiChatMessage`         |
| `privateChatMessage`       | Eine private Chatnachricht eintrifft                      | `IApiChatMessage`         |
| `meetingDurationUpdated`   | Sich die verbleibende Meeting-Zeit ändert                 | `IApiRemainingTimeUpdate` |
| `meetingRoomConfigUpdated` | Sich die Raumkonfiguration ändert                         | `IMeetingRoomConfig`      |
| `customMessage`            | Ein anderer Teilnehmer eine eigene Nachricht sendet       | `ICustomMessage`          |
| `screenshareStateChange`   | Ein Screensharing startet oder endet                      | `IScreenshareState`       |

> **Hinweis:** `connected` und `disconnected` beschreiben den WebSocket zu unserer API, nicht die WebRTC-Medien. Ein Teilnehmer kann von den Medien getrennt sein und trotzdem das Whiteboard nutzen.

## Methoden

### Meeting und Teilnehmer

| Methode                                    | Beschreibung                                                   |
| ------------------------------------------ | -------------------------------------------------------------- |
| `getVersion(): string`                     | Gibt den Versionsstring des laufenden Meetingraums zurück.     |
| `getParticipantsList(): IApiParticipant[]` | Gibt die aktuelle Teilnehmerliste zurück.                      |
| `leaveMeeting(): void`                     | Trennt die Verbindung zum Server und verlässt den Meetingraum. |
| `enableFollowMe(enabled: boolean): void`   | Aktiviert Follow Me. Sie brauchen dafür Moderationsrechte.     |

### Audio und Video

| Methode                                                           | Beschreibung                                                                                                                        |
| ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `muteAudio(muted: boolean): void`                                 | Schaltet Audio stumm oder wieder ein.                                                                                               |
| `toggleMuteAudio(): void`                                         | Schaltet die Audio-Stummschaltung um.                                                                                               |
| `muteVideo(muted: boolean): void`                                 | Schaltet Video stumm oder wieder ein.                                                                                               |
| `toggleMuteVideo(): void`                                         | Schaltet die Video-Stummschaltung um.                                                                                               |
| `setVolume(volume: number, meetingParticipantId?: string): void`  | Setzt die Wiedergabelautstärke, entweder für einen Teilnehmer oder für alle.                                                        |
| `connect(mediaConfig?: { audio: boolean, video: boolean }): void` | Verbindet die Medien. Sind sie bereits verbunden, passiert nichts. Beim Beitritt verbindet der Raum die Medien ohnehin selbst.      |
| `disconnect(): void`                                              | Trennt die Medien. Sind sie bereits getrennt, passiert nichts.                                                                      |
| `restartMediaConnections(): void`                                 | Startet die Medienverbindungen neu. Das hilft nach einem Gerätewechsel.                                                             |
| `enterVideoFullscreen(): void`                                    | Wechselt in den Fullscreen. Ausser in Chrome scheitert das in den meisten Browsern, denn Fullscreen verlangt eine User-Interaktion. |

### Geräte

| Methode                                                                                  | Beschreibung                                                                                                               |
| ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `getMediaDeviceSettings(): IMediaDeviceSettings`                                         | Gibt die aktuell gewählten Geräte und die Auflösung zurück.                                                                |
| `getMediaDeviceAudioInputList(audioOnly: boolean): MediaDeviceInfo[]`                    | Gibt die verfügbaren Mikrofone zurück. `audioOnly` beschränkt die Berechtigungsabfrage auf Audio.                          |
| `getMediaDeviceAudioOutputList(audioOnly: boolean): MediaDeviceInfo[]`                   | Gibt die verfügbaren Lautsprecher zurück. `audioOnly` beschränkt die Berechtigungsabfrage auf Audio.                       |
| `getMediaDeviceVideoInputList(): MediaDeviceInfo[]`                                      | Gibt die verfügbaren Kameras zurück.                                                                                       |
| `setAudioInputDeviceId(deviceId: string, reconnect: boolean): void`                      | Setzt das Mikrofon. Die API prüft nicht, ob die Geräte-ID gültig ist.                                                      |
| `setAudioOutputDeviceId(deviceId: string, reconnect: boolean): void`                     | Setzt den Lautsprecher. Heute unterstützen das nur Chrome und Edge.                                                        |
| `setVideoInputDeviceId(deviceId: string, reconnect: boolean): void`                      | Setzt die Kamera. Die API prüft nicht, ob die Geräte-ID gültig ist.                                                        |
| `setVideoResolution(resolution: MeetingRoomVideoResolution, reconnect: boolean): void`   | Setzt die Auflösung des ausgehenden Videos.                                                                                |
| `setMediaStreamConstraints(constraints: MediaStreamConstraints, merge?: boolean): void`  | Setzt Constraints für `getUserMedia`. `merge` steht standardmässig auf `true` und kombiniert Ihre Constraints mit unseren. |
| `setDisplayMediaConstraints(constraints: MediaStreamConstraints, merge?: boolean): void` | Setzt Constraints für `getDisplayMedia`. `merge` steht standardmässig auf `true`.                                          |

`reconnect` steht standardmässig auf `false`. Übergeben Sie `true`, baut der Raum die Medien neu auf und die Änderung greift sofort.

> **Die drei Listen-Methoden können eine Abfrage auslösen.** Sie fragen den Browser nach der Medienberechtigung, um die Gerätenamen lesen zu können. Ein Aufruf kann also einen Berechtigungsdialog öffnen. Rufen Sie sie deshalb dann auf, wenn der Teilnehmer damit rechnet, und nicht beim Laden der Seite.

### Screensharing und Aufzeichnung

| Methode                                                   | Beschreibung                                                                                                            |
| --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `setScreensharingInterceptor(callback: () => void): void` | Erlaubt einer Electron-Anwendung, Screensharing-Anfragen abzufangen und eine Quelle vorzuwählen.                        |
| `startScreensharing(sourceId?: string): void`             | Startet das Screensharing, auf Wunsch von einer bestimmten Quelle. Darf der Teilnehmer nicht teilen, geschieht nichts.  |
| `stopScreensharing(): void`                               | Beendet das Screensharing.                                                                                              |
| `forceStopScreensharing(): void`                          | Beendet das Screensharing eines anderen Teilnehmers. Das dürfen nur Moderatoren, sonst blockiert der Server den Aufruf. |
| `startRecording(): void`                                  | Startet die Aufzeichnung. Das gelingt nur, wenn das Meeting für teilweise Aufzeichnung konfiguriert ist.                |
| `stopRecording(): void`                                   | Beendet die Aufzeichnung. Es gilt dieselbe Bedingung.                                                                   |

### Nachrichten

| Methode                                                                | Beschreibung                                                                                   |
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `sendChatMessage(message: string): void`                               | Sendet eine Gruppen-Chatnachricht.                                                             |
| `sendPrivateChatMessage(message: string, participantId: string): void` | Sendet eine private Chatnachricht an einen Teilnehmer.                                         |
| `sendCustomMessage(message: ICustomMessage): void`                     | Sendet eine eigene Nachricht an alle Teilnehmer. Diese erhalten sie als Event `customMessage`. |

### Darstellung und Events

| Methode                                                                | Beschreibung                                                                                                              |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `setVideoDisplayCalculator(calculator: IVideoDisplayCalculator): void` | Überlässt Ihnen die Anordnung der Videos. Siehe [Videodarstellung](/de/developer-documentation/video-display-calculator). |
| `on(event: MeetingRoomApiEvent, callback: ApiEventCallback): void`     | Abonniert ein Event.                                                                                                      |

## Zusätzliche Methoden in Veeting Blocks

Setzen Sie Veeting Blocks statt eines eingebetteten Raums ein, erhalten Sie vier zusätzliche Methoden. Mit ihnen steuern Sie den Beitritt selbst:

| Methode                                                        | Beschreibung                                                |
| -------------------------------------------------------------- | ----------------------------------------------------------- |
| `isBrowserSupported(): boolean`                                | Meldet, ob der aktuelle Browser ein Meeting ausführen kann. |
| `loadMeeting(meetingId: string): Promise<IMeetingRoomConfig>`  | Lädt die Konfiguration eines Meetings.                      |
| `joinMeeting(config: IMeetingConnectionConfig): Promise<void>` | Tritt dem Meeting bei.                                      |
| `can(meetingPermission: string): Promise<boolean>`             | Meldet, ob der aktuelle Teilnehmer eine Berechtigung hat.   |

## Typdefinitionen

```typescript
interface IApiParticipant {
  id: string;
  name: string;
  muted?: boolean;
  handRaised?: boolean;
  fromPSTN?: boolean;
  hasVideo?: boolean;
  hadVideo?: boolean;
  joinedAt: number;
}

interface IApiChatMessage {
  fromParticipantId: string;
  fromParticipantName: string;
  message: string;
}

interface IApiRemainingTimeUpdate {
  remainingSeconds: number;
}

interface IScreenshareState {
  active: boolean;
}

interface IMediaDeviceSettings {
  videoResolution: string;
  audioInputDeviceId: string;
  audioOutputDeviceId: string;
  videoInputDeviceId: string;
}

interface IMeetingConnectionConfig {
  meetingId: string;
  participantName: string;
  participantEmail?: string;
  audio: boolean;
  video: boolean;
  moderatorToken?: string;
  interpreterToken?: string;
  invisibleToken?: string;
  speakerToken?: string;
}

type MediaDirection = "receiveonly" | "sendonly" | "sendreceive";
type JoinMode = "audio-only" | "audio-video" | "video-only" | "no-media";

type ApiEventCallback = (payload: void | IApiParticipant[] | IApiChatMessage
  | IApiRemainingTimeUpdate | IMeetingRoomConfig | ICustomMessage | IScreenshareState) => void;
```

## Häufige Fehler

- Sie weisen `wlvmrApiReady` zu, nachdem der Meetingraum geladen ist. Die Funktion wird dann nie aufgerufen.
- Sie schreiben den Handler-Namen fest in den Code, statt den übergebenen zu verwenden.
- Sie rufen eine Methode vor dem Event `joined` auf. Der Raum ist dann zwar geladen, das Meeting aber noch nicht betreten.
- Sie halten `connected` für einen Beleg, dass Medien fliessen. Es bezieht sich auf den WebSocket zu unserer API.
- Sie erwarten von `startScreensharing` eine Rückmeldung. Darf der Teilnehmer nicht teilen, kehrt der Aufruf stillschweigend zurück.
- Sie rufen `startRecording` in einem Meeting auf, das nicht für teilweise Aufzeichnung konfiguriert ist.
- Sie rufen `forceStopScreensharing` ohne Moderationsrechte auf. Der Server blockiert es.
- Sie übergeben eine Geräte-ID, ohne zu prüfen, ob sie existiert. Die API validiert sie nicht.

---

## Die übrige Dokumentation

- [Custom Tools – eigene Widgets im Meetingraum](https://www.veeting.com/de/developer-documentation/custom-tools)
- [Externer Autorisierungsdienst](https://www.veeting.com/de/developer-documentation/external-meeting-authorization-service)
- [iFrame und Web Komponenten](https://www.veeting.com/de/developer-documentation/iframe-and-web-components)
- [JavaScript und Typescript APIs](https://www.veeting.com/de/veeting-blocks/apis)
- [Komponenten](https://www.veeting.com/de/veeting-blocks/components)
- [Kontrolle über die Videodarstellung](https://www.veeting.com/de/developer-documentation/video-display-calculator)
- [Parameter in der URL](https://www.veeting.com/de/developer-documentation/query-parameters)
- [Veeting Blocks - Übersicht](https://www.veeting.com/de/veeting-blocks/introduction)
- [Veeting Rooms REST APIs](https://www.veeting.com/de/developer-documentation/api-usage)
- [Webhooks](https://www.veeting.com/de/developer-documentation/web-hooks)

Alles in einer Datei: https://www.veeting.com/llms-full.txt
