# Mitglieder & Rechte (https://docs.pixl.lennox.zip/access) > Leute einladen, Twitch-Mods beitreten lassen und Rechte vergeben Ein Kanal in pixl hat genau einen **Owner**. Der darf alles, auch Verbindungen ändern, den Kanal übertragen oder löschen. Alle anderen sind Mitglieder und haben nur die Rechte, die du ihnen gibst. ## Einladungen [#einladungen] Eingeladen wird über den **Twitch-Namen**, nicht per E-Mail. Das klappt für Mods genauso wie für alle anderen. Auf der Seite **Mitglieder** auf **Einladen** klicken, die Person suchen und ihre Rechte wählen. Die Person meldet sich mit Twitch bei pixl an. Sie sieht die Einladung über die Glocke oben und unter **Kanäle beitreten** und nimmt sie dort an. Du kannst auch Leute einladen, die noch nie bei pixl waren. ## Rechte [#rechte] | Recht | Was es erlaubt | | ------------------------- | ------------------------------------------------------------------------------------------------------- | | Events ansehen | Sehen, wer folgt, abonniert, Bits schickt oder spendet | | Events wiederholen | Ein Event noch einmal auslösen, z.B. für einen Alert | | Events bearbeiten | Events ausblenden und entscheiden, ob ein vermuteter Refund einer war | | Spotify steuern | Den aktuellen Song sehen, Songs anhängen, pausieren und überspringen | | TTS verwalten | TTS an- und ausschalten, einstellen, Texte überspringen, eingesprochene Stimmen umbenennen und löschen | | Eigene Stimme einsprechen | Aus der eigenen Stimme eine KI-Stimme für den Kanal machen. Wer den Kanal verlässt, verliert sie | | API-Schlüssel verwalten | [Schlüssel](/sdk/api-keys) für Widgets anlegen, ändern und löschen | Ohne Rechte sieht ein Mitglied nur die Übersicht des Kanals. ## Admins [#admins] Admins haben alle Rechte und dürfen dazu Mitglieder einladen, entfernen und ihre Rechte ändern. Nur der Owner darf Konten verbinden, Admins ernennen und den Kanal übertragen oder löschen. Admin kann nur werden, wen du eingeladen hast. Die Rolle wählst du beim Einladen oder später über die Rechte der Person. Überträgst du den Kanal, wirst du selbst Admin. ## Twitch-Mods [#twitch-mods] Unter **Mitglieder › Twitch-Mods** schaltest du ein, dass die Mods deines Twitch-Kanals selbst beitreten können. Welche Rechte sie dann haben, legst du dort einmal für alle Mods fest. - Deine Mods finden den Kanal nach ihrer Anmeldung bei pixl unter **Kanäle beitreten** und treten mit einem Klick bei. - Nimmst du jemandem auf Twitch den Mod-Status, endet auch der Zugang in pixl. Wer selbst austritt, kann jederzeit wieder beitreten. - Mods, die du zusätzlich eingeladen hast, haben die Rechte aus der Einladung. Unter **Eingeladene Mods** kannst du einstellen, dass die Rechte für Mods dazukommen. - **Eigene Stimme einsprechen** gibt es für Mods nicht automatisch, nur per Einladung. Dafür muss ein [Twitch-Kanal verbunden](/twitch) sein. # LLMs (https://docs.pixl.lennox.zip/ai) > Jede Seite als Markdown für ChatGPT, Claude und andere Assistenten Soll dir ein KI-Assistent ein Widget schreiben oder eine Frage zu pixl beantworten, gib ihm die Doku mit. Dafür gibt es jede Seite auch als reines Markdown, ohne Menüs und Layout. ## Eine Seite als Markdown [#eine-seite-als-markdown] Häng `.md` an die Adresse einer Seite: | Seite | Als Markdown | | --- | --- | | `docs.pixl.lennox.zip/sdk/events` | `docs.pixl.lennox.zip/sdk/events.md` | | `docs.pixl.lennox.zip/sdk` | `docs.pixl.lennox.zip/sdk.md` | | `docs.pixl.lennox.zip` (Startseite) | `docs.pixl.lennox.zip/index.md` | Oben auf jeder Seite geht das auch per Knopf: **Markdown kopieren** legt den Text der Seite in die Zwischenablage. Unter **Öffnen** kopierst du den Link, siehst dir das Markdown an oder schickst die Seite direkt an ChatGPT, Claude oder Cursor. ## Die ganze Doku [#die-ganze-doku] | Datei | Inhalt | | --- | --- | | [`llms.txt`](https://docs.pixl.lennox.zip/llms.txt) | Inhaltsverzeichnis mit einem Link pro Seite | | [`llms-full.txt`](https://docs.pixl.lennox.zip/llms-full.txt) | Alle Seiten in einer Datei | `llms.txt` folgt dem Vorschlag von [llmstxt.org](https://llmstxt.org). Soll der Assistent alles kennen, gib ihm `llms-full.txt`. Für ein Widget reichen meist die Seiten zum SDK. ## Für eigene Werkzeuge [#für-eigene-werkzeuge] Fragt ein Programm mit dem Header `Accept: text/markdown` an, bekommt es unter der normalen Adresse direkt Markdown: ```bash curl -H "Accept: text/markdown" https://docs.pixl.lennox.zip/sdk/events ``` Außerdem verweist jede Seite im `` per `` auf ihre Markdown-Fassung. Beschreib dem Assistenten genau, was dein Widget tun soll, zum Beispiel: „Ein StreamElements Custom Widget, das die Subs seit Stream-Start zählt und bei jedem neuen Sub kurz aufleuchtet.“ Deinen API-Schlüssel gibst du ihm nicht, den trägst du am Ende selbst ein. # Häufige Fragen (https://docs.pixl.lennox.zip/faq) > Antworten auf das, was oft gefragt wird Ja. Einladungen laufen über Twitch: Du suchst die Person über ihren Twitch-Namen. Sobald sie sich zum ersten Mal mit Twitch bei pixl anmeldet, sieht sie die Einladung und kann sie annehmen. Wenn du es einschaltest, können sie selbst beitreten. Unter **Mitglieder › Twitch-Mods** legst du fest, welche Rechte Mods bekommen. Mehr unter [Mitglieder & Rechte](/access#twitch-mods). Überall, wo JavaScript läuft: als Custom Widget in StreamElements, als Browserquelle in OBS oder auf einer eigenen Seite. Das [SDK](/sdk) holt sich die Events mit deinem API-Schlüssel. Nein. `pixl.feed()` gleicht nach jedem Neuverbinden mit der Historie ab und liefert jedes Event genau einmal. Auf der Seite **Events** schickst du echte Events mit **Wiederholen** noch einmal los. Sie kommen mit `replay: true` an, damit dein Widget sie beim Zählen auslassen kann. Ja. Jede Seite dieser Doku gibt es auch als Markdown, das ChatGPT, Claude und andere Assistenten gut lesen können. Wie das geht, steht unter [LLMs](/ai). # Willkommen (https://docs.pixl.lennox.zip/) > Was pixl kann und wo du anfängst pixl sammelt alles, was in deinem Stream passiert, an einer Stelle: Follows, Subs, Bits, Raids, Kanalpunkte und Spenden. Du siehst die Events im Dashboard, spielst sie bei Bedarf noch einmal ab und holst sie dir per SDK in deine eigenen Widgets, egal ob die in StreamElements, OBS oder auf einer eigenen Seite laufen. ## Erste Schritte [#erste-schritte] ## Was kann pixl? [#was-kann-pixl] - **Events**: pixl speichert jedes Event. Im Dashboard filterst du nach Art und spielst Events mit einem Klick noch einmal ab. - **SDK und API**: Mit einem [API-Schlüssel](/sdk/api-keys) liest dein Widget die bisherigen Events und bekommt neue live, ohne doppelte und auch nach Verbindungsabbrüchen. - **Team**: Lade Leute über ihren Twitch-Namen ein und gib ihnen nur die [Rechte](/access), die sie brauchen. Deine Twitch-Mods können auf Wunsch selbst beitreten. - **Verbindungen**: [Twitch](/twitch), [StreamElements](/streamelements) und [Spotify](/spotify) sind direkt eingebaut. ## Mehr entdecken [#mehr-entdecken] # Schnellstart (https://docs.pixl.lennox.zip/quickstart) > Vom Login zum ersten Widget mit Live-Events In vier Schritten vom leeren Dashboard zu einem Widget, das live auf deinen Stream reagiert. ### Mit Twitch anmelden [#mit-twitch-anmelden] Bei pixl meldest du dich mit deinem Twitch-Konto an, einen anderen Weg gibt es nicht. Danach brauchst du einen Kanal: Entweder du legst mit einem Einladungscode einen eigenen an, oder du nimmst eine [Einladung](/access#einladungen) an. Bei pixl anmelden ### Twitch verbinden [#twitch-verbinden] Unter **Einstellungen › Verbindungen** verbindest du deinen [Twitch-Kanal](/twitch). Ab dann landen Follows, Subs, Bits und mehr auf der Seite **Events**. Spenden über [StreamElements](/streamelements) verbindest du an derselben Stelle. ### API-Schlüssel erstellen [#api-schlüssel-erstellen] Unter **API & SDK** legst du einen [Schlüssel](/sdk/api-keys) an. Kopier ihn dir direkt, er wird nur einmal angezeigt. ### SDK einbinden [#sdk-einbinden] Ein Script-Tag und ein paar Zeilen reichen, zum Beispiel in einem StreamElements Custom Widget: ```html ``` Alles Weitere steht in der [SDK-Doku](/sdk). # Spotify (https://docs.pixl.lennox.zip/spotify) > Musik im Stream anzeigen und steuern, auch für Mitglieder und per Kanalpunkte Verbindest du Spotify mit deinem Kanal, können du und Mitglieder mit dem passenden Recht auf der Seite **Musik** sehen, was gerade läuft, Songs suchen und in die Warteschlange legen und die Wiedergabe steuern (Pause, Weiter, Zurück, Lautstärke). ## Verbinden [#verbinden] 1. Öffne in deinem Kanal **Einstellungen › Verbindungen**. 2. Klick bei **Spotify** auf **Verbinden** und melde dich mit dem Spotify-Konto an, auf dem im Stream die Musik läuft. Verbinden und trennen kann nur der Owner. Mitglieder brauchen kein eigenes Spotify-Konto. Steuern geht nur mit **Spotify Premium**. Außerdem lässt Spotify die App von pixl nur wenige freigeschaltete Konten verbinden. Lehnt Spotify dein Konto ab, verbinde über eine eigene Spotify-App: Den Weg dorthin findest du über den Pfeil neben **Verbinden**. ## Wer darf steuern? [#wer-darf-steuern] Alle mit dem Recht **Spotify steuern**. Du vergibst es unter [Mitglieder](/access#rechte), auch an deine Twitch-Mods. Von Haus aus nur, solange du auf Twitch live bist. Ist dein Kanal offline, sehen Mitglieder nicht, was du hörst, und können nichts steuern. Du selbst kannst als Owner immer. Willst du das anders, stell auf der Seite **Musik** bei **Wann dürfen Mitglieder steuern?** auf **Immer**. ## Songwünsche per Kanalpunkte [#songwünsche-per-kanalpunkte] Auf der Seite **Musik** richtest du unter **Einstellungen** Belohnungen ein, mit denen Zuschauer einen Song wünschen, den laufenden überspringen oder den letzten noch einmal spielen lassen. pixl legt sie bei Twitch erst an, wenn du auf **Erstellen** klickst. Dafür muss dein Kanal Affiliate oder Partner sein. Ein Wunsch landet direkt in der Warteschlange von Spotify. Findet pixl den Song nicht, ist er zu lang oder gesperrt, oder läuft gerade kein Spotify, bekommt die Person ihre Kanalpunkte automatisch zurück. Solange du offline bist, sind die Belohnungen pausiert. ## Warteschlange [#warteschlange] pixl zeigt die Warteschlange so, wie Spotify sie meldet. Songs, die jemand über pixl hinzugefügt hat, tragen den Namen der Person. Entfernen oder umsortieren lässt Spotify von außen nicht zu. Einen Song, der nicht laufen soll, überspringst du, sobald er dran ist. ## Kein Gerät gefunden? [#kein-gerät-gefunden] Spotify muss auf irgendeinem Gerät laufen (PC, Handy, Web Player). Starte Spotify und spiel kurz etwas ab, danach klappt das Steuern. # StreamElements (https://docs.pixl.lennox.zip/streamelements) > Spenden über StreamElements in pixl empfangen Nimmst du Spenden über StreamElements an, kann pixl sie empfangen: Sie erscheinen auf der Seite **Events** unter **Spenden** und kommen bei deinen Widgets über das [SDK](/sdk/events) als `streamelements.tip` an. Follows, Subs und Bits kommen weiterhin direkt über die [Twitch-Verbindung](/twitch). ## Verbinden [#verbinden] Die Verbindung richtest du unter **Einstellungen › Verbindungen** ein. Nur der Owner des Kanals kann sie verwalten. ### Overlay Token kopieren [#overlay-token-kopieren] Öffne in StreamElements [Account › Channels](https://streamelements.com/dashboard/account/channels) und schalte **Show secrets** ein. Kopiere den **Overlay Token**. ### In pixl einfügen [#in-pixl-einfügen] Klicke in den Einstellungen bei StreamElements auf **Verbinden** und füge den Token ein. pixl prüft ihn direkt bei StreamElements und speichert ihn nur, wenn er funktioniert. Nimm nicht den **JWT Token**, der auf derselben Seite steht. Er gibt vollen Zugriff auf dein StreamElements-Konto, pixl lehnt ihn deshalb ab. Der Overlay Token reicht für alles, was pixl braucht, und kann nur lesen. Der Token wird verschlüsselt gespeichert und verlässt pixl nicht. Überträgst du den Kanal an jemand anderen, wird er automatisch entfernt, weil er zu deinem StreamElements-Konto gehört. ## Was pixl speichert [#was-pixl-speichert] Im Dashboard, in Widgets und in der API landen von jeder Spende nur der Name, den die spendende Person angegeben hat, der Betrag, die Währung und die Nachricht. Zusätzlich speichert pixl die Originaldaten von StreamElements zur Fehlersuche. Darin steht auch das Twitch-Konto, mit dem bezahlt wurde, selbst wenn unter einem anderen Namen gespendet wurde. Sehen können das nur Mitglieder deines Kanals über **Rohdaten ansehen** auf der Seite **Events**, nie Widgets oder die API. E-Mail-Adresse oder Land der spendenden Person bekommt pixl nicht. ## Testen [#testen] Im Dashboard von StreamElements kannst du Spenden simulieren, um Alerts auszuprobieren. pixl erkennt solche Testspenden: Sie gehen live an deine Widgets und stehen auf der Seite **Events** mit dem Hinweis **Simuliert**. Im Menü **Ansicht anpassen** blendest du sie über **Simulierte anzeigen** aus. In Auswertungen zählen sie nicht mit. Widgets bekommen sie mit `replay: true` und `data.test: true`, damit sie z.B. beim Zusammenzählen nicht mitzählen. ## StreamElements-Daten in eigenen Widgets [#streamelements-daten-in-eigenen-widgets] Mit einem [API-Schlüssel](/sdk/api-keys) mit dem Recht **StreamElements lesen** können Widgets weitere Daten aus deinem StreamElements-Kanal abfragen, etwa Ranglisten der Spender, Treuepunkte oder die Warteschlange der Songwünsche. Details in der [SDK-Referenz](/sdk/reference#pixlstreamelementspath-params). ## Trennen [#trennen] Über **Trennen** in den Einstellungen löschst du den Token. pixl empfängt danach keine Spenden mehr, bereits empfangene bleiben erhalten. Willst du sichergehen, dass der Token nirgends mehr funktioniert, kannst du ihn zusätzlich in StreamElements neu erzeugen lassen. Lehnt StreamElements den Token später ab, zum Beispiel weil du ihn dort erneuert hast, zeigen die Einstellungen **Token abgelehnt**. Füge dann einfach den aktuellen Token neu ein. # Twitch (https://docs.pixl.lennox.zip/twitch) > Twitch-Kanal verbinden und seine Events in eigenen Widgets nutzen Verbindest du einen Twitch-Kanal mit deinem pixl-Kanal, kommen seine Events sofort in pixl an: Follows, Subs, Bits, Raids, eingelöste Kanalpunkte und ob du live bist. Deine Widgets bekommen sie über das [SDK](/sdk/events), ohne ständig nachfragen zu müssen. ## Kanal verbinden [#kanal-verbinden] Die Verbindung richtest du im Dashboard unter **Einstellungen › Verbindungen** ein. Nur der Owner des Kanals kann sie verwalten. Überträgst du den Kanal an jemand anderen, entfernt pixl die Twitch-Verbindung aus Sicherheitsgründen. Der neue Owner verbindet danach seinen eigenen Kanal. ### Verbinden starten [#verbinden-starten] Klick bei Twitch auf **Verbinden**. Du landest auf der Anmeldeseite von Twitch. ### Mit dem Konto des Kanals anmelden [#mit-dem-konto-des-kanals-anmelden] Wichtig: Melde dich mit dem Konto an, **dessen Kanal verbunden werden soll**. Das muss nicht das Konto sein, mit dem du bei pixl angemeldet bist. Twitch zeigt dafür immer die Kontoauswahl. ### Berechtigungen bestätigen [#berechtigungen-bestätigen] Bestätige alle Berechtigungen, die pixl anfragt. Sie decken die Events deines Kanals und Funktionen wie den Chat ab. Lehnst du einzelne ab, klappt die Verbindung nicht und du kannst es noch einmal versuchen. Danach zeigen die Einstellungen den verbundenen Kanal. Die Verbindung gehört dem pixl-Kanal, nicht deinem pixl-Konto. ## Was die Verbindung kann [#was-die-verbindung-kann] | Event | Beispiel im Widget | | --- | --- | | Follow | Alert mit dem Namen des neuen Followers | | Sub und Resub | Alert mit Sub-Stufe | | Bits | Alert mit Betrag | | Raid | Einblendung mit Raider und Zuschauerzahl | | Kanalpunkte | Reaktion auf eingelöste Belohnungen | | Live gehen | Widget blendet sich beim Streamstart ein | Dazu kommen Umfragen, Vorhersagen, Hype Trains, Chat und einiges mehr. Alle Events stehen in der [Event-Referenz](/sdk/events). ## Neue Berechtigungen [#neue-berechtigungen] Braucht pixl für neue Funktionen zusätzliche Berechtigungen bei Twitch, steht in den Einstellungen bei Twitch **Braucht neue Berechtigungen**. Klick auf **Autorisieren** und bestätige einmal, die Verbindung selbst bleibt dabei bestehen. ## Trennen [#trennen] Über **Trennen** in den Einstellungen entfernst du die Verbindung. pixl gibt die Berechtigungen bei Twitch zurück, und es kommen keine Events mehr an. Twitch-Mods, die über den Mod-Zugang beigetreten sind, verlieren dabei auch den Zugang. Du kannst jederzeit neu verbinden. Du kannst pixl den Zugriff auch direkt in den [Einstellungen von Twitch](https://www.twitch.tv/settings/connections) entziehen. Dann kommen sofort keine Events mehr an. Trenn die Verbindung danach trotzdem auch in pixl. # Was ist neu? (https://docs.pixl.lennox.zip/changelog) > Was sich an pixl, am SDK und an der API ändert Hier steht künftig, was sich an pixl ändert: neue Funktionen im Dashboard, im SDK und in der API. ### pixl ist in Entwicklung [#pixl-ist-in-entwicklung] Wir arbeiten gerade an der ersten öffentlichen Version. Sobald es losgeht, steht hier, was jede neue Version bringt. Bis dahin lohnt sich ein Blick in den [Schnellstart](/quickstart) und die [SDK-Doku](/sdk). # Impressum (https://docs.pixl.lennox.zip/legal/imprint) > Angaben gemäß § 5 DDG Dies ist ein Entwurf. Bitte alle eckigen Platzhalter ausfüllen und vor Live-Schaltung von einem Anwalt oder Dienst wie e-recht24 gegenprüfen lassen. Sobald ein Kleingewerbe angemeldet ist, müssen Rechtsform, Steuernummer/USt-ID und ggf. Registereintrag ergänzt werden. ## Angaben gemäß § 5 DDG [#angaben-gemäß--5-ddg] [Vor- und Nachname]
[Straße und Hausnummer]
[PLZ und Ort]
[Land] ## Kontakt [#kontakt] E-Mail: [deine-email@beispiel.de] ## Verantwortlich für den Inhalt nach § 18 Abs. 2 MStV [#verantwortlich-für-den-inhalt-nach--18-abs-2-mstv] [Vor- und Nachname] [Straße und Hausnummer] [PLZ und Ort] ## Streitschlichtung [#streitschlichtung] Die Europäische Kommission stellt eine Plattform zur Online-Streitbeilegung (OS) bereit: [https://ec.europa.eu/consumers/odr/](https://ec.europa.eu/consumers/odr/) Wir sind nicht verpflichtet und nicht bereit, an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teilzunehmen. ## Haftung für Inhalte [#haftung-für-inhalte] Als Diensteanbieter sind wir gemäß § 7 Abs. 1 DDG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 DDG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen. ## Haftung für Links [#haftung-für-links] Unser Angebot enthält Links zu externen Webseiten Dritter (z. B. Twitch, Spotify, Discord), auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. # Datenschutzerklärung (https://docs.pixl.lennox.zip/legal/privacy) > Informationen zur Verarbeitung personenbezogener Daten Dies ist ein Entwurf auf Basis des aktuellen technischen Stands von pixl. Bitte alle eckigen Platzhalter ausfüllen und vor Live-Schaltung von einem Anwalt oder Dienst wie e-recht24 gegenprüfen lassen. Diese Seite muss aktuell gehalten werden, sobald neue Datenverarbeitungen dazukommen (z. B. Twitch EventSub, Spotify-Widget, Resend, Stripe). ## 1. Verantwortlicher [#1-verantwortlicher] [Vor- und Nachname]
[Straße und Hausnummer]
[PLZ und Ort]
E-Mail: [deine-email@beispiel.de] ## 2. Übersicht der Verarbeitungen [#2-übersicht-der-verarbeitungen] pixl ist eine Plattform für Streamer zum Erstellen und Verwalten von Overlays. Bei der Nutzung verarbeiten wir folgende personenbezogene Daten: - Anmeldedaten über Twitch (verpflichtend) und optional Spotify - Von dir angelegte Overlay- und Widget-Inhalte - Zustandsdaten deiner Overlays (z. B. Zähler-Werte) inkl. Änderungsverlauf - Hochgeladene Mediendateien (Bilder, Video, PDFs) - Discord-Server- und Kanal-IDs, sofern du die Discord-Integration einrichtest - Spenden-Ereignisse aus StreamElements (Name, Betrag, Währung, Nachricht), sofern die StreamElements-Integration eingerichtet ist ## 3. Hosting [#3-hosting] pixl läuft auf einem dedizierten Server bei der Hetzner Online GmbH, Deutschland. Die Datenverarbeitung findet ausschließlich in Deutschland statt. Mit Hetzner besteht ein Auftragsverarbeitungsvertrag nach Art. 28 DSGVO. ## 4. Anmeldung über Twitch und Spotify [#4-anmeldung-über-twitch-und-spotify] Die Anmeldung bei pixl erfolgt ausschließlich über **Twitch OAuth**. Dabei erhalten wir von Twitch deine Twitch-Nutzer-ID, deinen Anzeigenamen und ggf. deine E-Mail-Adresse. Ein klassisches Passwort-Login gibt es nicht. Als Owner eines Kanals kannst du optional dein **Spotify**-Konto mit dem Kanal verbinden. Spotify ist dabei kein Login, sondern eine Verbindung des Kanals: du und Mitglieder mit dem Recht „Spotify steuern“ können damit sehen, was gerade läuft, Songs in die Warteschlange legen und die Wiedergabe steuern. Angefragt werden nur die Scopes `user-read-playback-state`, `user-modify-playback-state` und `user-read-currently-playing`. Wir speichern deine Spotify-Nutzer-ID, deinen Anzeigenamen sowie, welches Mitglied welchen Song über pixl hinzugefügt hat (wird nach 7 Tagen gelöscht). Die Zugangs- und Refresh-Token beider Anbieter werden verschlüsselt in unserer Datenbank gespeichert. Rechtsgrundlage: Art. 6 Abs. 1 lit. b DSGVO (Vertragserfüllung, Bereitstellung des Nutzerkontos). ## 5. Cookies [#5-cookies] Wir setzen ein einzelnes technisch notwendiges Session-Cookie zur Anmeldung. Es enthält keine Tracking- oder Marketing-Informationen und wird nicht an Dritte weitergegeben. Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse an einer funktionierenden Anmeldung), keine Einwilligung nach § 25 Abs. 2 TTDSG erforderlich, da technisch notwendig. ## 6. Server-Logs [#6-server-logs] Unsere Hosting-Infrastruktur protokolliert aus Sicherheitsgründen für kurze Zeit technische Zugriffsdaten (IP-Adresse, Zeitpunkt, aufgerufene URL). pixl selbst speichert oder verarbeitet diese Daten in der Anwendung nicht weiter. ## 7. Overlay-Daten und Zustandsverlauf [#7-overlay-daten-und-zustandsverlauf] Overlays, deren Widgets sowie Zustandswerte (z. B. Zähler) sind an eine **Organisation** gebunden, nicht an dein persönliches Nutzerkonto. Sie sind für alle Mitglieder dieser Organisation entsprechend ihrer Rolle sichtbar und bleiben bestehen, solange die Organisation besteht, auch wenn du dein eigenes Konto löschst oder die Organisation verlässt. Änderungen an Zustandswerten werden in einem Verlauf protokolliert, damit Mitglieder der Organisation sie nachvollziehen können. ## 8. Datei-Uploads [#8-datei-uploads] Von dir hochgeladene Bilder, Videos und PDFs werden auf unserem selbst betriebenen Speicher (Garage, S3-kompatibel) auf demselben Server abgelegt. Es findet keine Weitergabe an externe Cloud-Anbieter statt. ## 9. Einladungen [#9-einladungen] Einladungen zu deiner Organisation erfolgen ausschließlich über die Twitch-Nutzer-ID des Eingeladenen, niemals über pixl-interne Konten-Informationen oder E-Mail-Adressen. So verhindern wir, dass sichtbar wird, wer bei pixl registriert ist. ## 10. Discord-Integration [#10-discord-integration] Wenn du die Discord-Integration einrichtest, speichern wir die von dir ausgewählte Server- (Guild-) und Kanal-ID, um automatisierte Nachrichten (z. B. Live-Benachrichtigungen) zu senden. Es findet kein dauerhafter Zugriff auf Discord-Nachrichteninhalte statt. ## 11. StreamElements-Integration [#11-streamelements-integration] Verbindet der Owner einer Organisation StreamElements, speichern wir dessen StreamElements Overlay Token verschlüsselt und rufen darüber Spenden-Ereignisse des Kanals ab. Von jeder Spende speichern wir im Eventlog der Organisation den von der spendenden Person angegebenen Namen, Betrag, Währung und Nachricht sowie das von StreamElements übermittelte Spenden-Ereignis im Original (u. a. Twitch-Nutzername, -ID und Profilbild des zahlenden Kontos). Das Original ist nur für Mitglieder der Organisation zur Fehlersuche einsehbar und wird nicht an Overlays oder über die API weitergegeben. E-Mail-Adresse oder Land der spendenden Person erhalten wir nicht. Rechtsgrundlage ist unser berechtigtes Interesse bzw. das des Streamers an der Anzeige von Spenden im Stream (Art. 6 Abs. 1 lit. f DSGVO). ## 12. Empfänger und Drittländer [#12-empfänger-und-drittländer] Im Rahmen der Anmeldung und Integrationen übermitteln wir Daten an folgende Anbieter: | Anbieter | Zweck | Sitz | |---|---|---| | Twitch (Amazon) | Login, Zuschauer-Einladungen | USA | | Spotify | Musik eines Kanals anzeigen und steuern (optional) | EU/USA | | Discord | Live-Benachrichtigungen (optional) | USA | | StreamElements | Spenden-Ereignisse abrufen (optional) | Israel/USA | Bei Übermittlungen in die USA stützen wir uns auf das EU-US Data Privacy Framework, sofern der jeweilige Anbieter dort zertifiziert ist. ## 13. Speicherdauer [#13-speicherdauer] Dein persönliches Nutzerkonto (Twitch-Verknüpfung, Login-Daten) speichern wir, solange dein Konto besteht. Organisationsbezogene Daten (Overlays, Widgets, Zustandsdaten, Medien) bleiben davon unabhängig bestehen, solange die jeweilige Organisation besteht. Eigentümer einer Organisation können diese und alle zugehörigen Daten löschen. Nach Löschung deines Kontos werden deine persönlichen Daten gelöscht, soweit keine gesetzlichen Aufbewahrungspflichten entgegenstehen. ## 14. Deine Rechte [#14-deine-rechte] Du hast das Recht auf: - Auskunft über die von uns verarbeiteten Daten (Art. 15 DSGVO) - Berichtigung unrichtiger Daten (Art. 16 DSGVO) - Löschung deiner Daten (Art. 17 DSGVO) - Einschränkung der Verarbeitung (Art. 18 DSGVO) - Datenübertragbarkeit (Art. 20 DSGVO) - Widerspruch gegen die Verarbeitung (Art. 21 DSGVO) - Beschwerde bei einer Datenschutz-Aufsichtsbehörde Wende dich dazu an: [deine-email@beispiel.de] ## 15. Zuständige Aufsichtsbehörde [#15-zuständige-aufsichtsbehörde] [Name und Anschrift der zuständigen Landesdatenschutzbehörde einfügen] # Nutzungsbedingungen (https://docs.pixl.lennox.zip/legal/terms) > Allgemeine Geschäftsbedingungen für die Nutzung von pixl Dies ist ein Entwurf. Bitte alle eckigen Platzhalter ausfüllen und vor Live-Schaltung von einem Anwalt oder Dienst wie e-recht24 gegenprüfen lassen. Diese Seite muss aktuell gehalten werden, sobald neue Features (z. B. Stripe-Bezahlung, Overlay-SDK) dazukommen. ## 1. Geltungsbereich [#1-geltungsbereich] Diese Allgemeinen Geschäftsbedingungen (AGB) gelten für die Nutzung von pixl, einer Plattform für Streamer zum Erstellen und Verwalten von Overlays, erreichbar unter pixl.lennox.zip, betrieben von [Vor- und Nachname / Firma], [Anschrift]. ## 2. Vertragsschluss und Registrierung [#2-vertragsschluss-und-registrierung] Die Anmeldung erfolgt ausschließlich über Twitch OAuth. Ein Zugang zu pixl setzt einen gültigen Twitch-Account sowie entweder einen Einladungscode für eine neue Organisation oder eine Einladung in eine bestehende Organisation voraus. Mit erfolgreicher Anmeldung kommt ein Nutzungsvertrag zu diesen AGB zustande. ## 3. Leistungsumfang [#3-leistungsumfang] pixl stellt Werkzeuge zur Erstellung, Verwaltung und Anzeige von Overlays für Streaming bereit, unter anderem Widgets, Datei-Uploads sowie Integrationen mit Twitch, Spotify und Discord. Der Funktionsumfang richtet sich nach dem gebuchten Plan (siehe [Preisseite]). Ein Anspruch auf ununterbrochene Verfügbarkeit besteht nicht. ## 4. Pflichten der Nutzer [#4-pflichten-der-nutzer] Nutzer verpflichten sich, keine Inhalte hochzuladen oder zu erstellen, die gegen geltendes Recht, Rechte Dritter (insbesondere Urheber-, Marken- oder Persönlichkeitsrechte) oder die guten Sitten verstoßen. Zugangsdaten sind vor dem Zugriff Dritter zu schützen. ## 5. Nutzergenerierte Inhalte [#5-nutzergenerierte-inhalte] Von Nutzern hochgeladene oder erstellte Inhalte (Overlay-Layouts, Texte, Bilder, Videos, PDFs) bleiben Eigentum des jeweiligen Nutzers bzw. der Organisation. Für diese Inhalte sind wir gemäß §§ 8–10 DDG nicht verantwortlich, solange uns keine Kenntnis einer konkreten Rechtsverletzung vorliegt (siehe auch [Impressum, Haftung für Inhalte](/legal/imprint#haftung-für-inhalte)). Bei Bekanntwerden entsprechender Rechtsverletzungen entfernen wir die betroffenen Inhalte umgehend. Nutzer räumen uns das für den Betrieb von pixl notwendige Recht ein, die von ihnen hochgeladenen Inhalte zu speichern, zu verarbeiten und im Rahmen der jeweiligen Organisation anzuzeigen. ## 6. Organisationen und Rollen [#6-organisationen-und-rollen] Overlays, Widgets und Zustandsdaten sind an eine Organisation gebunden, nicht an das persönliche Nutzerkonto. Eigentümer einer Organisation können Mitglieder einladen, Rollen vergeben und die Organisation samt aller zugehörigen Daten löschen. ## 7. Verfügbarkeit und Änderungen [#7-verfügbarkeit-und-änderungen] Wir sind bemüht, pixl möglichst unterbrechungsfrei bereitzustellen, garantieren jedoch keine bestimmte Verfügbarkeit. Wir behalten uns vor, den Funktionsumfang von pixl weiterzuentwickeln, zu ändern oder einzelne Funktionen einzustellen. ## 8. Haftung [#8-haftung] Wir haften unbeschränkt für Vorsatz und grobe Fahrlässigkeit sowie nach Maßgabe des Produkthaftungsgesetzes. Für leichte Fahrlässigkeit haften wir nur bei Verletzung einer wesentlichen Vertragspflicht (Kardinalpflicht) und begrenzt auf den vertragstypisch vorhersehbaren Schaden. Im Übrigen ist die Haftung ausgeschlossen. ## 9. Laufzeit und Kündigung [#9-laufzeit-und-kündigung] Nutzer können ihr Konto jederzeit löschen. Eigentümer einer Organisation können diese jederzeit löschen; damit werden auch alle zugehörigen Overlays, Widgets und Zustandsdaten gelöscht. [Regelung zu kostenpflichtigen Plänen ergänzen, sobald Stripe angebunden ist.] ## 10. Änderung dieser AGB [#10-änderung-dieser-agb] Wir behalten uns vor, diese AGB bei Bedarf anzupassen, etwa wenn neue Funktionen hinzukommen. Über wesentliche Änderungen informieren wir registrierte Nutzer in angemessener Form. ## 11. Schlussbestimmungen [#11-schlussbestimmungen] Es gilt das Recht der Bundesrepublik Deutschland. Sollte eine Bestimmung dieser AGB unwirksam sein, bleibt die Wirksamkeit der übrigen Bestimmungen unberührt. # API-Schlüssel (https://docs.pixl.lennox.zip/sdk/api-keys) > Schlüssel für Widgets und Scripts anlegen und sicher nutzen Mit einem API-Schlüssel liest ein Widget oder Script die Events und andere Daten deines Kanals. Was genau, legst du pro Schlüssel über seine Rechte fest. Bis auf **TTS auslösen** können Schlüssel nur lesen: Sie lösen keine Events aus und fassen keine Einstellungen an. ## Schlüssel erstellen [#schlüssel-erstellen] Öffne im Dashboard die Seite **API & SDK**. Klick auf **Neuer Schlüssel** und gib ihm einen Namen, an dem du später erkennst, wo du ihn benutzt (z.B. „StreamElements Sub-Zähler“). Wähl aus, was der Schlüssel darf (siehe [Rechte](#rechte)). Kopier den Schlüssel sofort. Er wird nur dieses eine Mal angezeigt, pixl speichert nur einen Hash davon. Schlüssel sehen so aus: `pixl_` gefolgt von 43 Zeichen. In der Liste erkennst du sie an den ersten Zeichen und siehst, wann sie zuletzt benutzt wurden. ## Rechte [#rechte] Jeder Schlüssel hat eigene Rechte (Scopes). Gib ihm nur, was der Einsatzort wirklich braucht: Ein Sub-Zähler braucht kein TTS, ein TTS-Overlay keine Events. | Recht | Scope | Erlaubt | |---|---|---| | Events lesen | `events:read` | Eventlog abfragen (`pixl.events()`, `pixl.feed()`, `GET /events`) und Twitch-, Stream-, StreamElements- und 7TV-Events live empfangen (`pixl.on()`) | | StreamElements lesen | `streamelements:read` | Daten aus dem verbundenen StreamElements-Kanal abfragen (`pixl.streamElements()`, `GET /streamelements/…`), z.B. Sitzungswerte, Ranglisten, Songwünsche. Beim Erstellen nicht vorausgewählt | | TTS abspielen | `tts:read` | Vorgelesene Texte live empfangen und das Audio abrufen (`pixl.tts.player()`, `GET /tts/{id}/audio`) | | TTS auslösen | `tts:write` | Eigene Texte vorlesen lassen (`pixl.tts.speak()`, `POST /tts`). Verbraucht Credits des Kanals, beim Erstellen nicht vorausgewählt | | Subs lesen | `subs:read` | Aktuelle Sub-Zahl laut Twitch abfragen (`pixl.subs()`, `GET /subs`) und Änderungen live empfangen (`subs.updated`). Beim Erstellen nicht vorausgewählt | | Musik lesen | `spotify:read` | Laufenden Spotify-Song abfragen (`pixl.nowPlaying()`, `GET /spotify/now-playing`) und Wechsel live empfangen (`spotify.now_playing`). Braucht das Modul Musik, beim Erstellen nicht vorausgewählt | `pixl.org()` bzw. `GET /org` geht mit jedem Schlüssel und verrät auch dessen Scopes. Fehlt ein Recht, antworten HTTP-Aufrufe mit `403` und einer Meldung, welches Recht fehlt. Im Live-Stream kommen die entsprechenden Nachrichten einfach nicht an: Ohne `tts:read` kommen z.B. keine vorgelesenen Texte an. ### Rechte ändern [#rechte-ändern] Über den Stift neben einem Schlüssel änderst du seine Rechte. Das gilt sofort: HTTP-Aufrufe prüfen bei jeder Anfrage neu, offene Live-Verbindungen werden kurz getrennt und verbinden sich mit den neuen Rechten wieder. Das SDK macht das automatisch, dein Widget merkt höchstens kurz den Status `disconnected`. ## Wer darf Schlüssel verwalten? [#wer-darf-schlüssel-verwalten] Owner und Admins immer. Andere Mitglieder brauchen das Recht **API-Schlüssel verwalten**, das du unter [Mitglieder](/access#rechte) vergibst. Schlüssel gehören dem Kanal, nicht der Person, die sie angelegt hat. Verlässt jemand den Kanal, funktionieren seine Schlüssel weiter. ## Schlüssel löschen [#schlüssel-löschen] Ein gelöschter Schlüssel funktioniert sofort nicht mehr. Offene Verbindungen werden getrennt, das SDK meldet dann den Status `unauthorized` und versucht es nicht erneut. ## Sicherheit [#sicherheit] Wer deinen Schlüssel hat, kann alle Events deines Kanals mitlesen, also z.B. wer wann wie viele Subs oder Bits geschickt hat. Behandle ihn wie ein Passwort. - Zeig den Code deines Widgets nicht im Stream. Bei StreamElements ist der Schlüssel in einem Feld des Widgets gut aufgehoben, das sieht nur, wer Zugriff auf dein StreamElements-Konto hat. - Nimm pro Einsatzort einen eigenen Schlüssel. Taucht einer irgendwo auf, wo er nicht hingehört, löschst du nur diesen und der Rest läuft weiter. - Pro Schlüssel sind 120 Anfragen pro Minute erlaubt. Ein Widget stellt beim Laden ein paar Anfragen und hängt danach nur noch an der Live-Verbindung, das reicht locker. # HTTP-API (https://docs.pixl.lennox.zip/sdk/api) > REST- und Stream-Endpunkte ohne SDK Das SDK ist nur eine dünne Schicht über diese Endpunkte. Wenn du kein JavaScript im Browser nutzt (z.B. ein Bot oder ein Script auf deinem Rechner), sprichst du sie direkt an. ## Grundlagen [#grundlagen] - Basis-URL: `https://pixl.lennox.zip/api/v1` - Authentifizierung: Header `Authorization: Bearer pixl_DEIN_SCHLÜSSEL`. Einen Query-Parameter für den Schlüssel gibt es bewusst nicht, URLs landen zu leicht in Logs. - Alle Endpunkte sind lesend (`GET`), außer [`POST /tts`](#post-tts). Alle erlauben Aufrufe von fremden Seiten (CORS `*`, ohne Cookies). - Die API liefert nur Daten, sie rechnet nichts aus. Summen o.ä. bildest du selbst aus den Events. - Antworten sind JSON. Fehler haben die Form `{ "error": "..." }`, bei TTS zusätzlich mit `problem`. - Limit: 120 Anfragen pro Minute und Schlüssel. | Status | Bedeutung | |---|---| | `400` | Ungültiger Parameter, z.B. kaputtes Datum | | `401` | Schlüssel fehlt, ist falsch oder gelöscht | | `403` | Dem Schlüssel fehlt das nötige [Recht](/sdk/api-keys#rechte), oder das Modul ist im Kanal aus (`"code": "module_disabled"`, siehe unten) | | `404` | `since=stream`, aber es gab noch keinen Stream, oder ein TTS-Audio ist abgelaufen | | `422` | TTS: Text gesperrt, leer oder unbekannte Stimme | | `429` | Zu viele Anfragen, bei TTS auch: Credits aufgebraucht | **Module:** Owner und Admins können ganze Funktionen eines Kanals ausschalten (aktuell TTS). Die Endpunkte des Moduls antworten dann mit `403` und `{ "error": "...", "code": "module_disabled", "problem": "module_disabled", "module": "tts" }`. Nach dem Einschalten geht alles wie vorher, der Schlüssel bleibt gültig. Zeitangaben (`since`, `until`, `cursor`) gehen als ISO-Datum (`2026-10-01T18:00:00Z`) oder Unix-Millisekunden. `since` kennt zusätzlich `stream` für den Start des letzten Streams. ## `GET /org` [#get-org] Kanal des Schlüssels und seine Scopes. Braucht kein besonderes Recht. ```json { "id": "…", "name": "laGameTV", "logo": "https://…", "twitchChannelLogin": "lagametv", "scopes": ["events:read"] } ``` ## `GET /events` [#get-events] Eventlog, neueste zuerst. Braucht `events:read`. | Parameter | Bedeutung | |---|---| | `since` | Ab wann (optional) | | `until` | Bis wann, exklusiv (optional) | | `types` | Kommagetrennte Event-Typen, z.B. `twitch.sub,twitch.cheer` | | `limit` | 1 bis 200, Standard 50 | | `cursor` | `nextCursor` der vorherigen Antwort | ```json { "events": [ { "id": "b24f…", "event": "twitch.cheer", "data": { "username": "Kekskruemel698", "bits": 100 }, "at": "2026-10-08T21:48:56.984Z" } ], "nextCursor": "2026-10-08T21:48:56.984Z" } ``` Die Payloads stehen in der [Event-Referenz](/sdk/events#event-referenz). Erneut abgespielte Events stehen als eigene Zeile drin, erkennbar an `data.replay: true`. ## `GET /subs` [#get-subs] Aktuelle Sub-Zahl direkt von Twitch, nicht aus dem Eventlog. Braucht `subs:read` und einen verbundenen Twitch-Kanal. pixl fragt Twitch höchstens einmal pro Minute, die Zahl ist also bis zu 60 Sekunden alt. ```json { "total": 312, "points": 340, "at": "2026-10-20T08:15:00.000Z" } ``` `total` zählt wie bei Twitch den Streamer selbst mit. `points` sind die Sub-Punkte (Stufe 1 = 1, Stufe 2 = 2, Stufe 3 = 6). Ändert sich die Zahl, kommt im Stream `subs.updated` mit denselben Feldern, meist wenige Sekunden nach dem Sub. Ohne verbundenen Kanal oder mit abgelaufener Twitch-Verbindung antwortet der Endpunkt mit `409`. ## `GET /spotify/now-playing` [#get-spotifynow-playing] Was gerade auf der verbundenen Spotify-Wiedergabe des Kanals läuft. Braucht `spotify:read` und das Modul Musik (sonst `403` mit `problem: "module_disabled"`). ```json { "playing": true, "progressMs": 83000, "at": "2026-10-20T18:02:11.000Z", "track": { "uri": "spotify:track:4cOdK2wGLETKBW3PvgPWqT", "name": "Never Gonna Give You Up", "artists": ["Rick Astley"], "album": "Whenever You Need Somebody", "image": "https://i.scdn.co/image/…", "durationMs": 213573, "explicit": false, "url": "https://open.spotify.com/track/4cOdK2wGLETKBW3PvgPWqT" }, "addedBy": { "name": "lagametv" } } ``` `progressMs` gilt zum Zeitpunkt `at`. Läuft der Song (`playing`), rechnest du selbst weiter, das SDK hat dafür `Pixl.progress()`. Läuft Spotify auf keinem Gerät, ist `track` null. `addedBy` ist gesetzt, wenn jemand den Song über pixl in die Queue gelegt hat. Ändert sich etwas (anderer Song, Play/Pause, Sprung im Song), kommt im Stream `spotify.now_playing` mit denselben Feldern. pixl fragt Spotify nur ab, solange ein Widget mit `spotify:read` am Stream hängt: während des Streams alle 5 Sekunden und direkt zum Songende, offline alle 30 Sekunden. Ohne Spotify-Verbindung oder mit abgelaufener Verbindung antwortet der Endpunkt mit `409`, bremst Spotify gerade, mit `429`. ## `GET /emotes` [#get-emotes] Alle Emotes von 7TV, BTTV und FFZ im Kanal, Kanal- und globale Sets. Braucht `events:read` und einen verbundenen Twitch-Kanal. Gibt es einen Namen mehrfach, steht nur der Gewinner drin (7TV vor BTTV vor FFZ, Kanal vor global). Twitch-Emotes fehlen, die kommen direkt mit jeder Nachricht. ```json { "emotes": [{ "provider": "7tv", "id": "01G3WEGZN0000ET2J0MQP5YJ0G", "name": "GAMBA", "animated": true }] } ``` ## `GET /7tv/paints` [#get-7tvpaints] Definitionen von 7TV-Paints (farbige Namen), z.B. `?ids=01JHXJFACX42RV996VE9933TB8`. Braucht `events:read`, höchstens 100 IDs pro Abfrage. Welchen Paint ein Chatter trägt, steht als `paint` in `twitch.chat_message`. ```json { "paints": [{ "id": "01JHXJFACX42RV996VE9933TB8", "name": "Emerald Doppler", "layers": [{ "type": "image", "url": "https://cdn.7tv.app/paint/…/1x.webp", "animated": true, "opacity": 1 }], "shadows": [{ "color": "#39D21EFF", "x": 0, "y": 0, "blur": 0.1 }] }] } ``` Ebenen gibt es als `linear-gradient` (`angle`, `repeating`, `stops`), `radial-gradient` (`shape`, `repeating`, `stops`), `color` und `image`. Farben als `#RRGGBBAA`, `stops[].at` von 0 bis 1, Schatten in px. Das SDK macht daraus CSS (`pixl.chat.paintStyle()`). ## `GET /streamelements/{pfad}` [#get-streamelementspfad] Lesender Zugriff auf den StreamElements-Kanal, der mit deinem Kanal verbunden ist. Braucht `streamelements:read` und einen in den Einstellungen hinterlegten StreamElements Overlay Token. pixl reicht die Anfrage mit diesem Token an StreamElements weiter, der Token selbst bleibt bei pixl. Query-Parameter gehen unverändert mit, die Antwort ist die von StreamElements. | Pfad | StreamElements-Endpunkt | |---|---| | `activities` | Activity-Historie (Parameter `after`, `before`, `limit`, `types` usw. wie bei StreamElements) | | `sessions` | Session-Daten: letzter Tip, Top-Spender, Goals, Zähler | | `tips/leaderboard` | Spender-Rangliste | | `points/top`, `points/alltime` | Loyalty-Punkte-Ranglisten | | `store/items` | Artikel im Loyalty-Store | | `contests` | Contests | | `songrequest/queue`, `songrequest/playing` | Songrequest-Warteschlange und aktueller Song | Andere Pfade antworten mit `404`. Ist StreamElements nicht verbunden, kommt `409`. Zusätzlich zum Limit pro Schlüssel gilt hier eins pro Kanal (60 Anfragen pro Minute), weil sich alle Schlüssel denselben StreamElements-Zugang teilen. ```bash curl -H "Authorization: Bearer pixl_…" "https://pixl.lennox.zip/api/v1/streamelements/tips/leaderboard" ``` ## `GET /stream` [#get-stream] Live-Verbindung als [Server-Sent Events](https://developer.mozilla.org/de/docs/Web/API/Server-sent_events). Jede Nachricht ist eine `data:`-Zeile mit JSON: ``` data: {"orgId":"…","event":"twitch.cheer","data":{"username":"…","bits":100},"id":"…","at":"2026-10-11T19:00:00.000Z"} ``` - Was ankommt, hängt von den Scopes ab: `twitch.*`-, `stream.*`-, `streamelements.*`- und `seventv.*`-Events mit `events:read`, `tts.ready`, `tts.skip` und `tts.clear` mit `tts:read`, `subs.updated` mit `subs:read` und `spotify.now_playing` mit `spotify:read`. - Werden die Scopes geändert, endet die Verbindung ohne weitere Nachricht. Einfach neu verbinden, dann gelten die neuen Rechte. - `replay: true` markiert erneut abgespielte Events, `id` und `at` gibt es bei Events aus dem Eventlog. Über die `id` erkennst du Events, die du schon per `/events` geladen hast. - Alle 15 Sekunden kommt ein Kommentar (`: ping`), damit die Verbindung offen bleibt. Kommt länger als 45 Sekunden gar nichts, ist die Verbindung tot, auch wenn sie noch offen aussieht. Dann einfach neu verbinden. - Bei einem Update von pixl endet die Verbindung, und der Server ist ein paar Sekunden nicht erreichbar. Verbinde bei jeder Antwort außer `401` und `403` mit kurzer Wartezeit neu, auch bei `502` oder `503`. - Wird der Schlüssel gelöscht, kommt `api_key.revoked` und die Verbindung endet. Danach nicht neu verbinden. Weil der Schlüssel als Header mitgeht, funktioniert der Browser-eigene `EventSource` hier nicht. Nutz das SDK, `fetch()` mit Streaming oder eine SSE-Bibliothek, die Header unterstützt. ```bash curl -N -H "Authorization: Bearer pixl_DEIN_SCHLÜSSEL" https://pixl.lennox.zip/api/v1/stream ``` ## TTS [#tts] Wie das Ganze zusammenspielt, steht unter [TTS](/sdk/tts). ### `POST /tts` [#post-tts] Text vorlesen lassen. Braucht `tts:write`, Body als JSON (`Content-Type: application/json`): ```bash curl -X POST -H "Authorization: Bearer pixl_…" -H "Content-Type: application/json" -d '{"text": "Danke fürs Einlösen!", "voice": "polly:hans"}' "https://pixl.lennox.zip/api/v1/tts" ``` Optional `filters`: `false` filtert weder Links noch Wiederholungen noch gesperrte Wörter, oder einzeln als Objekt, z.B. `{"links": false}` (Schlüssel `links`, `repeats`, `blockedWords`, fehlend = an). Unsichtbare Zeichen und die Längengrenze gelten immer. Antwort (`201`): `{ "id": "…", "text": "Danke fürs Einlösen!", "voice": "polly:hans" }`. `text` ist der bereinigte Text. Danach geht `tts.ready` an alle Verbindungen mit `tts:read`. Fehler kommen mit `problem`, die Werte stehen unter [TTS](/sdk/tts#selbst-vorlesen-lassen). ### `GET /tts/voices` [#get-ttsvoices] Verfügbare Stimmen und die Standardstimme des Kanals. Braucht `tts:read` oder `tts:write`. ```json { "voices": [{ "id": "polly:hans", "name": "Hans", "language": "de-DE", "gender": "male", "description": "…" }], "default": "polly:hans" } ``` ### `GET /tts/{id}/audio` [#get-ttsidaudio] Das Audio zu einem `tts.ready` als MP3. Braucht `tts:read`. Nach etwa einem Tag gibt es `404`. ### `GET /tts/{id}/marks` [#get-ttsidmarks] Zeitmarken pro Wort zum Audio, für Untertitel, die mitlaufen. Braucht `tts:read`. Entstehen beim ersten Abruf und kosten dann beim Anbieter extra, danach kommen sie aus dem Zwischenspeicher. Nach etwa einem Tag gibt es `404`. ```json { "marks": [{ "word": "Lisa", "start": 6, "end": 286 }, { "word": "hat", "start": 286, "end": 473 }, { "word": "Stream", "start": 2654, "end": null }] } ``` Zeiten in Millisekunden ab Start des Audios. `end` ist `null`, wenn es nicht bekannt ist (beim letzten Wort: Ende des Audios). ### Events im Stream [#events-im-stream] ``` data: {"orgId":"…","event":"tts.ready","data":{"id":"…","voice":"polly:hans","text":"Danke fürs Einlösen!","source":"api"},"at":"…"} data: {"orgId":"…","event":"tts.skip","data":{"id":"…"}} data: {"orgId":"…","event":"tts.clear","data":{}} ``` `tts.skip` ohne `id` heißt: überspring, was gerade läuft. `tts.clear` heißt: brich ab, was läuft, und verwirf alles, was wartet. `replay: true` steht an erneut gesendeten Einträgen. # Chat (https://docs.pixl.lennox.zip/sdk/chat) > Chat-Nachrichten mit 7TV-, BTTV- und FFZ-Emotes und 7TV-Paints `twitch.chat_message` bringt jede Nachricht aus dem Chat mit Badges und allen Emotes: die von Twitch selbst und die von 7TV, BTTV und FFZ, die der Kanal oder alle nutzen. Heißen zwei gleich, gewinnt 7TV vor BTTV vor FFZ, und das Emote des Kanals vor dem globalen. Der Schlüssel braucht **Events lesen** (`events:read`). ## Nachricht als HTML [#nachricht-als-html] Das SDK hängt an jede Nachricht `html` an: der Text ist escaped, Emotes sind Bilder mit der Klasse `pixl-emote`. Zero-width Emotes (z.B. Hüte) liegen zusammen mit dem Emote davor in ``. ```html
``` `msg.html` und `badgesHtml()` sind sicher für `innerHTML`, `msg.username` dagegen nicht: den immer über `textContent` setzen. ## Felder [#felder] | Feld | Inhalt | |---|---| | `text` | Nachricht als reiner Text | | `html` | Nachricht mit Emotes als HTML (baut das SDK) | | `emotes` | Alle Emotes der Nachricht, Name → `{ provider, id, name, animated?, zeroWidth? }`. `provider` ist `twitch`, `7tv`, `bttv` oder `ffz` | | `badges` | Badges vor dem Namen: erst die von Twitch (Mod, Sub, VIP, ...), dann das 7TV-Badge. Je `{ provider, set?, id, title, url, info? }` | | `paint` | ID des 7TV-Paints des Chatters oder `null` | | `username`, `login`, `userId` | Anzeigename, Login und Twitch-ID des Chatters | | `messageId` | ID der Nachricht, passt zu `twitch.chat_message_deleted` | | `color` | Twitch-Farbe des Namens | Ohne SDK teilst du `text` an Leerzeichen und schaust jedes Wort in `emotes` nach. Die Bild-URL baut `pixl.chat.emoteUrl(emote, size)` mit `size` 1, 2 oder 4. ## 7TV-Paints [#7tv-paints] `pixl.chat.paintStyle(paintId, fallback)` gibt einen Inline-Style für den Namen zurück. Die Definitionen lädt das SDK beim ersten Mal von [`GET /7tv/paints`](/sdk/api#get-7tvpaints) und merkt sie sich. Brauchst du die Rohdaten, gibt es `pixl.chat.paint(id)`. ## Emote-Änderungen live [#emote-änderungen-live] Fügt der Kanal bei 7TV ein Emote hinzu, entfernt oder benennt es um, kommt das sofort an. Neue Nachrichten haben das Emote dann schon, die Events brauchst du nur, wenn dein Widget Emotes selbst anzeigt. ```js pixl.on('seventv.emote_added', ({ emote, actor }) => { console.log(`${actor} hat ${emote.name} hinzugefügt`, pixl.chat.emoteUrl(emote, 4)); }); pixl.on('seventv.emote_removed', ({ emote }) => console.log(`${emote.name} ist weg`)); pixl.on('seventv.emote_renamed', ({ emote, oldName }) => console.log(`${oldName} heißt jetzt ${emote.name}`)); pixl.on('seventv.emote_set_changed', ({ name }) => console.log(`Neues Set: ${name}`)); ``` Alle Emotes des Kanals auf einmal liefert `await pixl.chat.emotes()` ([`GET /emotes`](/sdk/api#get-emotes)). Änderungen bei BTTV und FFZ kommen nicht live, pixl lädt sie alle 10 Minuten neu. # Events (https://docs.pixl.lennox.zip/sdk/events) > Auf alles reagieren, was in deinem Kanal passiert Mit `pixl.on()` meldest du einen Handler für ein Event an. Sobald auf deinem Twitch-Kanal etwas passiert, ruft das SDK ihn mit den Daten des Events auf. Alle Funktionen rund um Events stehen in der [SDK-Referenz](/sdk/reference#events). ```js pixl.on('twitch.follow', (data) => { showAlert(`${data.username} folgt jetzt!`); }); pixl.on('twitch.cheer', (data) => { showAlert(`${data.username} hat ${data.bits} Bits geschickt`); }); ``` `on()` gibt die SDK-Instanz zurück, du kannst die Aufrufe also verketten. Mit `pixl.on('*', …)` bekommst du alle Events. ## Event-Referenz [#event-referenz] Über `pixl.on()` kommt jedes Event aus der Tabelle an. Die mit ✅ in der Spalte **Eventlog** speichert pixl außerdem: Sie stehen im Dashboard unter **Events**, lassen sich dort wiederholen und kommen auch über `pixl.events()` und [`pixl.feed()`](/sdk/feed), jeweils mit `id` und Zeitpunkt `at`. Zwischenstände (`*_progress`), Chat und Moderation speichert pixl nicht, die gibt es nur live. | Event | Daten | Wann | Eventlog | |---|---|---|:-:| | `twitch.follow` | `{ username }` | Neuer Follower | ✅ | | `twitch.sub` | `{ username, login, tier, months?, streak?, durationMonths?, message? }` | Neuer Sub oder Resub. `tier`: `Prime`, `1000`, `2000` oder `3000`. Beim Resub: `months` = Monate insgesamt, `streak` = Monate am Stück (fehlt, wenn die Person sie nicht teilt), `message` = ihre Nachricht. `durationMonths` = im Voraus bezahlte Monate. Verschenkte Subs kommen nur als `twitch.sub_gift`, nie zusätzlich als `twitch.sub` | ✅ | | `twitch.sub_gift` | `{ username, login?, tier, amount, community?, months?, recipient?, recipientLogin?, recipients?, totalGifted? }` | Verschenkte Subs, `amount` = Anzahl. Mit `community: true` gingen sie an zufällige Zuschauer (immer ein Monat, auch bei `amount: 1`). Die Empfänger trägt pixl danach in `recipients` (`{ username, login? }[]`) ein: Im Live-Event fehlen sie noch, nach ein paar Sekunden stehen sie vollständig in der Historie (`pixl.events()`). Sonst ein Geschenk an eine Person: `recipient` und `recipientLogin`, `months` = geschenkte Monate. `totalGifted` = alle bisher verschenkten Subs der Person, bei anonymen Geschenken ist `username` `Anonym` | ✅ | | `twitch.cheer` | `{ username, bits }` | Bits geschickt | ✅ | | `twitch.raid` | `{ from, viewerCount }` | Jemand raidet deinen Kanal | ✅ | | `twitch.channel_point` | `{ username, reward, cost, input }` | Belohnung mit Kanalpunkten eingelöst, eigene oder von Twitch (z.B. Sub-Emote freischalten) | ✅ | | `twitch.channel_point_resolved` | `{ username, reward, status }` | Einlösung erledigt oder erstattet. `status`: `fulfilled` oder `canceled` | | | `twitch.power_up` | `{ username, title, bits, input }` | Eigenes Power-up eingelöst | ✅ | | `twitch.poll_started` | `{ title, choices }` | Umfrage gestartet | ✅ | | `twitch.poll_progress` | `{ title, results }` | Zwischenstand bei jeder Stimme, `results` = `{ title, votes }[]` | | | `twitch.poll_ended` | `{ title, status, results }` | Umfrage beendet. `status`: `completed`, `archived` oder `terminated`, `results` = `{ title, votes }[]` | ✅ | | `twitch.prediction_started` | `{ title, outcomes }` | Vorhersage gestartet | ✅ | | `twitch.prediction_progress` | `{ title, results }` | Zwischenstand bei jedem Einsatz, `results` = `{ title, points }[]` | | | `twitch.prediction_locked` | `{ title, results }` | Einsätze geschlossen, aber noch nicht aufgelöst (siehe `twitch.prediction_ended`). `results` = `{ title, points }[]`, eingesetzte Kanalpunkte je Ausgang | ✅ | | `twitch.prediction_ended` | `{ title, status, winningOutcome?, results }` | Vorhersage aufgelöst oder abgebrochen. `status`: `resolved` oder `canceled`, `results` wie oben | ✅ | | `twitch.hype_train_started` | `{ level, progress, goal, total, expiresAt, type, startedAt, shared, participants, topContributions }` | Hype Train beginnt | ✅ | | `twitch.hype_train_progress` | wie `twitch.hype_train_started` | Hype Train kommt voran | | | `twitch.hype_train_ended` | `{ level, total, progress?, goal?, type, startedAt, shared, participants, topContributions }` | Hype Train endet. `progress` und `goal` = letzter Stand im letzten Level (fehlen, wenn pixl während des Zugs neu gestartet ist) | ✅ | | `twitch.goal_started` | `{ type, description, current, target }` | Ziel des Kanals beginnt | | | `twitch.goal_progress` | `{ current, target }` | Ziel kommt voran | | | `twitch.goal_ended` | `{ type, description, current, target, achieved }` | Ziel endet | | | `twitch.charity_started` | `{ name, target, currency, decimalPlaces }` | Spendenaktion gestartet | ✅ | | `twitch.charity_progress` | `{ current, target, currency, decimalPlaces }` | Spendenaktion kommt voran | | | `twitch.charity_donated` | `{ username, name, amount, currency, decimalPlaces }` | Spende an die Spendenaktion | ✅ | | `twitch.charity_stopped` | `{ name, current, target, currency, decimalPlaces }` | Spendenaktion beendet | ✅ | | `twitch.ad_break` | `{ durationSec, isAutomatic, startedAt? }` | Werbepause beginnt. Sie endet bei `startedAt` + `durationSec` (`startedAt` fehlt in alten Einträgen) | ✅ | | `streamelements.tip` | `{ username, amount, currency, message, tipId }` | Spende über [StreamElements](/streamelements). `amount` ist schon ein Dezimalbetrag (`4.2`), `username` der Name, den die Person angegeben hat (kein Twitch-Login), `tipId` die ID bei StreamElements. Testspenden aus dem StreamElements-Dashboard haben `data.test: true` und kommen mit `replay: true`, live wie aus der Historie, damit Zähler sie wie Wiederholungen überspringen | ✅ | | `twitch.shoutout_sent` | `{ to, viewerCount }` | Du gibst einen Shoutout | | | `twitch.title_changed` | `{ title }` | Titel ändert sich während des Streams. pixl meldet nur, was sich wirklich geändert hat, auch wenn Twitch Titel und Kategorie immer zusammen schickt | ✅ | | `twitch.category_changed` | `{ category }` | Kategorie ändert sich während des Streams | ✅ | | `twitch.ban` | `{ username, moderator, reason, isPermanent, endsAt }` | Jemand wird gebannt oder bekommt einen Timeout | | | `twitch.unban` | `{ username, moderator }` | Bann aufgehoben | | | `twitch.vip_added` / `twitch.vip_removed` | `{ username }` | VIP vergeben oder entzogen | | | `twitch.moderator_added` / `twitch.moderator_removed` | `{ username }` | Mod-Status vergeben oder entzogen | | | `twitch.watch_streak` | `{ username, streak, channelPoints }` | Jemand hat eine bestimmte Zahl von Streams am Stück geschaut | ✅ | | `twitch.modiversary` | `{ username, months }` | Jemand ist seit `months` Monaten Mod in deinem Kanal | | | `stream.online` | `{ streamId, title, category, url, thumbnailUrl }` | Du bist live gegangen | ✅ | | `stream.offline` | `{ durationSec? }` | Stream beendet | ✅ | | `subs.updated` | `{ total, points, at }` | Die Sub-Zahl laut Twitch hat sich geändert (siehe `pixl.subs()`). Braucht `subs:read` statt `events:read` | | | `spotify.now_playing` | `{ playing, progressMs, at, track, addedBy }` | Anderer Song, Pause, Fortsetzen oder Sprung im Song (siehe `pixl.nowPlaying()`). Braucht `spotify:read` statt `events:read` | | | `twitch.chat_message` | `{ username, login, userId, messageId, text, color, emotes, badges, paint, html }` | Chat-Nachricht mit Badges und Emotes von Twitch, 7TV, BTTV und FFZ. `html` baut das SDK, siehe [Chat](/sdk/chat) | | | `twitch.chat_cleared` | `{ }` | Chat komplett geleert | | | `twitch.chat_user_cleared` | `{ username }` | Alle Nachrichten einer Person gelöscht | | | `twitch.chat_message_deleted` | `{ username, messageId }` | Einzelne Nachricht gelöscht | | | `twitch.chat_settings_updated` | `{ emoteMode, followerMode, followerModeDuration, slowMode, slowModeWait, subscriberMode, uniqueChatMode }` | Chat-Einstellungen geändert | | | `twitch.chat_message_held` | `{ username, text, messageId }` | AutoMod hält eine Nachricht zurück | | | `twitch.chat_message_held_updated` | `{ username, status, messageId }` | Status einer zurückgehaltenen Nachricht ändert sich, z.B. freigegeben | | | `seventv.emote_added` / `seventv.emote_removed` | `{ emote, actor }` | Emote im 7TV-Set des Kanals hinzugefügt oder entfernt | | | `seventv.emote_renamed` | `{ emote, oldName, actor }` | Emote im 7TV-Set umbenannt | | | `seventv.emote_set_changed` | `{ setId, name, actor }` | Kanal nutzt ein anderes 7TV-Set | | | `twitch.shared_chat_started` / `twitch.shared_chat_updated` | `{ sessionId, participants }` | Geteilter Chat beginnt oder ändert sich | | | `twitch.shared_chat_ended` | `{ sessionId }` | Geteilter Chat endet | | | `twitch.guest_star_session_started` / `twitch.guest_star_session_ended` | `{ sessionId }` | Guest-Star-Sitzung beginnt oder endet | | | `twitch.guest_star_guest_updated` | `{ sessionId, guest, state, slotId }` | Status eines Gastes ändert sich | | | `twitch.guest_star_settings_updated` | `{ slotCount, groupLayout }` | Guest-Star-Einstellungen geändert | | ## Beispiel: Raid-Alert [#beispiel-raid-alert] ```js const pixl = new Pixl({ key: 'pixl_DEIN_SCHLÜSSEL' }); pixl.on('twitch.raid', (data, event) => { if (event.replay) return; // im Dashboard wiederholte Raids ignorieren document.querySelector('#alert').textContent = `${data.from} raidet mit ${data.viewerCount} Leuten!`; }); ``` Für Widgets wie „Bits seit Stream-Start“ nimmst du [Historie + live](/sdk/feed). # Historie + live (https://docs.pixl.lennox.zip/sdk/feed) > Alle Events seit einem Zeitpunkt und danach live, jedes genau einmal Für Widgets wie „Bits seit Stream-Start“ brauchst du zwei Dinge: alles, was bisher passiert ist, und alles, was ab jetzt passiert. `pixl.feed()` liefert beides an einen einzigen Handler, ohne dass ein Event fehlt oder doppelt ankommt. ```js let bits = 0; pixl.feed({ since: 'stream', types: ['twitch.cheer'] }, (data) => { bits += data.bits; document.querySelector('#bits').textContent = bits; }); ``` Das SDK rechnet dabei selbst nichts aus. Es liefert nur die Events, was du daraus machst (zählen, auflisten, Alerts zeigen), entscheidest du im Handler. ## Optionen [#optionen] | Option | Bedeutung | |---|---| | `since` | Pflicht. Ab wann: `Date`, ISO-String, Unix-ms oder `'stream'` (Start des letzten Streams) | | `types` | Nur diese Event-Typen, z.B. `['twitch.sub', 'twitch.sub_gift']` | ## Der Handler [#der-handler] Derselbe wie bei [`pixl.on()`](/sdk/reference#pixlonevent-handler): `(data, event)`. Zusätzlich ist `event.history` `true`, wenn das Event aus der Historie nachgeladen wurde. Damit spielst du z.B. Alerts nur für neue Events ab: ```js pixl.feed({ since: 'stream', types: ['twitch.raid'] }, (data, event) => { addToList(data); if (!event.history) playAlert(data); }); ``` Die Historie kommt zuerst und in zeitlicher Reihenfolge, älteste zuerst. Danach kommen die neuen Events live. ## Darauf solltest du achten [#darauf-solltest-du-achten] - **Replays**: Wird ein Event im Dashboard erneut abgespielt, kommt es mit `event.replay === true` an, auch in der Historie. Willst du nur echte Events zählen, überspring diese. In der Historie gilt das auch für simulierte Events. Events, die im Dashboard ausgeblendet wurden (z.B. erstattete Subs), kommen gar nicht mehr an. Ein laufender Feed bekommt davon nichts mit: was er schon live erhalten hat, bleibt bis zum Neuladen des Widgets drin. - **Neuer Stream**: Bei `since: 'stream'` wird die Historie nur beim Verbinden geladen. Willst du bei einem neuen Stream wieder bei 0 anfangen, nimm `stream.online` in `types` auf und setz dort deine Werte zurück. - **Verbindungsabbrüche**: Nach jedem Neuverbinden gleicht das SDK die Historie erneut ab. Events aus der Lücke kommen also nach, schon bekannte nicht noch einmal. Klappt das Laden nicht sofort, versucht das SDK es weiter, solange die Verbindung steht. - **Große Zeiträume**: Die Historie wird seitenweise geladen (200 Events pro Anfrage). Für einen Stream ist das nichts, für ein ganzes Jahr ohne `types`-Filter schon einiges. - **Nur Eventlog-Events haben Historie**: Reine Live-Events wie Chat-Nachrichten kommen nur live an. ## Funktionen [#funktionen] `pixl.feed()` gibt ein Objekt zurück: | | | |---|---| | `feed.ready` | `true`, sobald die Historie einmal komplett ausgeliefert wurde | | `feed.onError(fn)` | Fehler beim Laden der Historie, z.B. bei `since: 'stream'`, wenn noch nie gestreamt wurde, oder wenn dem Schlüssel `events:read` fehlt. Live-Events kommen trotzdem weiter an, soweit der Schlüssel sie sehen darf. | | `feed.error` | Letzter Fehler oder `null` | | `feed.stop()` | Feed beenden, der Handler wird danach nicht mehr aufgerufen | ## Beispiel: Subs und Bits seit Stream-Start [#beispiel-subs-und-bits-seit-stream-start] ```js const pixl = new Pixl({ key: 'pixl_DEIN_SCHLÜSSEL' }); let subs = 0; let bits = 0; function render() { document.querySelector('#subs').textContent = subs; document.querySelector('#bits').textContent = bits; } pixl.feed( { since: 'stream', types: ['stream.online', 'twitch.sub', 'twitch.sub_gift', 'twitch.cheer', 'twitch.power_up'] }, (data, event) => { if (event.replay) return; switch (event.event) { case 'stream.online': if (!event.history) { subs = 0; bits = 0; } // neuer Stream, neu anfangen break; case 'twitch.sub': subs += 1; break; case 'twitch.sub_gift': subs += data.amount ?? 0; break; case 'twitch.cheer': case 'twitch.power_up': bits += data.bits ?? 0; break; } render(); }, ); ``` Welche Felder die Events haben, steht in der [Event-Referenz](/sdk/events#event-referenz). # Überblick (https://docs.pixl.lennox.zip/sdk) > Was das SDK kann und wie du anfängst Das pixl SDK ist eine kleine JavaScript-Bibliothek, mit der du die Events deines Kanals live in eigenen Widgets nutzt. Es läuft überall, wo ein Browser läuft: als Custom Widget in StreamElements, als Browserquelle in OBS oder auf deiner eigenen Seite. Du brauchst dafür nur einen [API-Schlüssel](/sdk/api-keys). Den legst du im Dashboard auf der Seite **API & SDK** an. ## Ein kleines Widget [#ein-kleines-widget] ```html
0
``` Das SDK verbindet sich von selbst, hält die Verbindung offen und baut sie nach einem Abbruch wieder auf. ## Was das SDK kann [#was-das-sdk-kann] - Events live empfangen mit [`pixl.on()`](/sdk/events) - Bisherige Events abfragen und mit [Historie + live](/sdk/feed) ohne Lücke weiterzählen - [Chat](/sdk/chat) mit Emotes von Twitch, 7TV, BTTV und FFZ anzeigen und [TTS](/sdk/tts) im Overlay abspielen - Die Sub-Zahl laut Twitch und den laufenden Spotify-Song lesen - Alles davon geht auch ohne SDK über die [HTTP-API](/sdk/api) Jede Seite dieser Doku gibt es auch als Markdown für ChatGPT, Claude und andere Assistenten. Wie das geht, steht unter [LLMs](/ai). # Installation (https://docs.pixl.lennox.zip/sdk/installation) > SDK per Script-Tag oder npm einbinden Es gibt nichts zu installieren. Das SDK ist eine einzelne Datei auf dem pixl-Server, die du per Script-Tag lädst. ## Per Script-Tag [#per-script-tag] ```html ``` Danach steht `Pixl` global zur Verfügung: ```js const pixl = new Pixl({ key: 'pixl_DEIN_SCHLÜSSEL' }); ``` Das SDK verbindet sich mit der pixl-Instanz, von der das Script kommt. Lädst du es von einer lokalen Instanz oder einem Tunnel, spricht es automatisch mit genau dieser. Kommt es von anderswo, etwa von jsDelivr, verbindet es sich mit pixl.lennox.zip. Du kannst die Adresse aber auch fest vorgeben: ```js const pixl = new Pixl({ key: 'pixl_DEIN_SCHLÜSSEL', baseUrl: 'https://pixl.lennox.zip', }); ``` ## Optionen [#optionen] | Option | Typ | Standard | Bedeutung | |---|---|---|---| | `key` | `string` | (Pflicht) | Dein [API-Schlüssel](/sdk/api-keys) | | `baseUrl` | `string` | die pixl-Instanz, von der das Script kommt, sonst `https://pixl.lennox.zip` | Adresse der pixl-Instanz | | `autoConnect` | `boolean` | `true` | Live-Verbindung sofort aufbauen. Bei `false` startest du sie selbst mit `pixl.connect()`. | | `logs` | `boolean` | `true` | Meldet in der Browser-Konsole, wenn die Live-Verbindung abbricht und wieder steht. Bei `false` bleibt die Konsole still. | ## Mit fester Version [#mit-fester-version] Unter `pixl.lennox.zip/sdk/pixl.js` kommt immer die Version, die pixl gerade ausliefert. Soll ein Widget bei einer bestimmten Version bleiben, lädst du das Script mit Versionsnummer von jsDelivr: ```html ``` Ab Version 0.1.1 verbindet es sich auch von dort mit pixl.lennox.zip, ohne `baseUrl`. ## Mit npm und Bundler [#mit-npm-und-bundler] Für eigene Widgets mit Bundler und TypeScript gibt es das SDK auch als [npm-Paket](https://www.npmjs.com/package/@pixl-app/sdk): ```bash npm i @pixl-app/sdk ``` ```ts import { Pixl } from '@pixl-app/sdk'; ``` Die Typen sind dabei. Was sich in einer Version geändert hat, steht in der Datei `CHANGELOG.md` im Paket. ## Browser-Unterstützung [#browser-unterstützung] Das SDK braucht `fetch` mit Streaming (`ReadableStream`). Das können alle aktuellen Browser, die Browserquellen in OBS und die Overlays von StreamElements. Auf `EventSource` verzichtet das SDK bewusst: So geht der Schlüssel als Header mit und steht nie in einer Adresse. # SDK-Referenz (https://docs.pixl.lennox.zip/sdk/reference) > Alle Funktionen der Pixl-Klasse Alle Beispiele gehen von einer Instanz aus: ```js const pixl = new Pixl({ key: 'pixl_DEIN_SCHLÜSSEL' }); ``` Optionen des Konstruktors stehen unter [Installation](/sdk/installation#optionen). ## Events [#events] ### `pixl.on(event, handler)` [#pixlonevent-handler] Registriert einen Handler für einen Event-Typ. Mit `'*'` bekommst du alle Events. Gibt die Instanz zurück, Aufrufe lassen sich also verketten. ```js pixl .on('twitch.cheer', (data) => console.log(`${data.username}: ${data.bits} Bits`)) .on('*', (data, event) => console.log(event.event, data)); ``` Der Handler bekommt zwei Argumente: | Argument | Inhalt | |---|---| | `data` | Daten des Events, siehe [Event-Referenz](/sdk/events#event-referenz) | | `event` | Das ganze Event: `{ event, data, replay, id?, at?, history? }` | `event.replay` ist `true`, wenn jemand das Event im Dashboard erneut abgespielt hat. Willst du Replays ignorieren, prüf darauf. `id` und `at` (ISO-Zeitpunkt) gibt es nur bei Events, die auch im Eventlog stehen. `history` setzt nur [`feed()`](/sdk/feed). ### `pixl.off(event, handler)` [#pixloffevent-handler] Entfernt einen Handler wieder. Du musst dieselbe Funktion übergeben wie bei `on()`. ### `pixl.events(query?)` [#pixleventsquery] Fragt das Eventlog ab, neueste Events zuerst. Gibt ein Promise zurück. ```js const { events, nextCursor } = await pixl.events({ since: 'stream', types: ['twitch.sub', 'twitch.sub_gift'], limit: 20, }); // Nächste Seite const page2 = await pixl.events({ since: 'stream', cursor: nextCursor }); ``` | Feld | Typ | Bedeutung | |---|---|---| | `since` | `Date`, ISO-String, Unix-ms oder `'stream'` | Ab wann. `'stream'` = Start des letzten Streams | | `until` | `Date`, ISO-String oder Unix-ms | Bis wann (exklusiv) | | `types` | `string[]` | Nur diese Event-Typen | | `limit` | `number` | 1 bis 200, Standard 50 | | `cursor` | `string` | `nextCursor` der vorherigen Seite | Ergebnis: `{ events: { id, event, data, at }[], nextCursor: string | null }`. Im Eventlog stehen nur die Events, die pixl speichert (siehe [Event-Referenz](/sdk/events#event-referenz)). Reine Live-Events wie Chat-Nachrichten oder Zwischenstände von Umfragen bekommst du nur über `pixl.on()`. ### `pixl.fetchAllEvents(query?)` [#pixlfetchalleventsquery] Wie `events()`, holt aber alle Seiten nacheinander und gibt die Events **älteste zuerst** zurück. Kennt `since`, `until` und `types`. ```js const cheers = await pixl.fetchAllEvents({ since: 'stream', types: ['twitch.cheer'] }); ``` ### `pixl.feed(options, handler)` [#pixlfeedoptions-handler] Erst alle Events seit `since`, danach live, jedes genau einmal. Ausführlich unter [Historie + live](/sdk/feed). ```js pixl.feed({ since: 'stream', types: ['twitch.cheer'] }, (data, event) => { console.log(event.history ? 'bisher' : 'neu', data.bits); }); ``` ## Kanal [#kanal] ### `pixl.org()` [#pixlorg] Infos zum Kanal, dem der Schlüssel gehört, und welche [Rechte](/sdk/api-keys#rechte) er hat: `{ id, name, logo, twitchChannelLogin, scopes }`. Praktisch, um zu prüfen, ob ein Schlüssel funktioniert. ## Subs [#subs] ### `pixl.subs()` [#pixlsubs] Aktuelle Sub-Zahl direkt von Twitch, unabhängig vom Eventlog: `{ total, points, at }`. Braucht `subs:read`. Änderungen kommen live als `subs.updated` mit denselben Feldern, Details unter [`GET /subs`](/sdk/api#get-subs). ```js const show = ({ total }) => (el.textContent = total); pixl.subs().then(show); pixl.on("subs.updated", show); ``` ## Musik [#musik] ### `pixl.nowPlaying()` [#pixlnowplaying] Was gerade auf Spotify läuft: `{ playing, progressMs, at, track, addedBy }`. Braucht `spotify:read` und das Modul Musik. Ohne aktives Gerät ist `track` null. Änderungen kommen live als `spotify.now_playing` mit denselben Feldern, Details unter [`GET /spotify/now-playing`](/sdk/api#get-spotifynow-playing). ### `Pixl.progress(nowPlaying, now?)` [#pixlprogressnowplaying-now] Fortschritt in Millisekunden zum Zeitpunkt `now` (Standard: jetzt). Läuft der Song, rechnet es ab `at` weiter, höchstens bis zur Songlänge. Damit läuft ein Fortschrittsbalken flüssig, ohne dass pixl jede Sekunde etwas schicken muss. ```js let current = null; const show = (np) => { current = np; title.textContent = np.track ? `${np.track.name} · ${np.track.artists.join(", ")}` : ""; cover.src = np.track?.image ?? ""; }; pixl.nowPlaying().then(show); pixl.on("spotify.now_playing", show); setInterval(() => { if (current?.track) bar.style.width = `${(Pixl.progress(current) / current.track.durationMs) * 100}%`; }, 250); ``` ## StreamElements [#streamelements] ### `pixl.streamElements(path, params?)` [#pixlstreamelementspath-params] Fragt Daten aus dem verbundenen StreamElements-Kanal ab, z.B. `pixl.streamElements('sessions')` oder `pixl.streamElements('points/top', { limit: 10 })`. Braucht `streamelements:read`. Erlaubte Pfade und was dahinter steckt: [`GET /streamelements/{pfad}`](/sdk/api#get-streamelementspfad). Die Antwort kommt unverändert von StreamElements. ## TTS [#tts] ### `pixl.tts.player(options?)` [#pixlttsplayeroptions] Spielt jedes vorgelesene `tts.ready` der Reihe nach ab und hält sich an „Überspringen“ aus dem Dashboard. Braucht `tts:read`. Optionen und Beispiele unter [TTS](/sdk/tts#abspielen-im-overlay). ### `pixl.tts.speak(text, { voice?, filters? })` [#pixlttsspeaktext--voice-filters-] Lässt einen Text vorlesen, abgespielt wird er dann in jedem Overlay mit Player. `filters: false` oder z.B. `{ links: false }` schaltet Filter ab, siehe [TTS](/sdk/tts#selbst-vorlesen-lassen). Braucht `tts:write` und verbraucht Credits des Kanals. Gibt `{ id, text, voice }` zurück. ### `pixl.tts.voices()` / `pixl.tts.audio(id)` / `pixl.tts.marks(id)` [#pixlttsvoices--pixlttsaudioid--pixlttsmarksid] Verfügbare Stimmen samt Standardstimme des Kanals, das Audio eines Eintrags als `Blob` (MP3) bzw. seine Zeitmarken pro Wort (`[{ word, start, end }]`, kosten beim ersten Abruf extra). ## Verbindung [#verbindung] ### `pixl.status` [#pixlstatus] Aktueller Verbindungsstatus: | Status | Bedeutung | |---|---| | `connecting` | Verbindung wird aufgebaut, auch beim Neuverbinden | | `connected` | Live-Verbindung steht | | `disconnected` | Verbindung weg, das SDK versucht es gleich wieder, auch während pixl gerade aktualisiert wird (spätestens alle 10 Sekunden) | | `unauthorized` | Schlüssel ungültig oder gelöscht. Es wird nicht neu verbunden | | `closed` | Mit `disconnect()` beendet, oder noch nicht gestartet | ### `pixl.onStatus(handler)` [#pixlonstatushandler] Ruft `handler` sofort mit dem aktuellen Status auf und danach bei jedem Wechsel. Gibt eine Funktion zum Abmelden zurück. ```js pixl.onStatus((status) => { document.body.classList.toggle('offline', status !== 'connected'); }); ``` ### `pixl.connect()` / `pixl.disconnect()` [#pixlconnect--pixldisconnect] Startet bzw. beendet die Live-Verbindung. Nach Abbrüchen verbindet das SDK automatisch neu, erst nach einer halben Sekunde, dann mit wachsendem Abstand bis höchstens 30 Sekunden. ### `pixl.request(path, params?)` [#pixlrequestpath-params] Rohzugriff auf einen Endpunkt der [HTTP-API](/sdk/api), z.B. `pixl.request('/events', { limit: '5' })`. Für Fälle, die die anderen Funktionen noch nicht abdecken. ## Fehler [#fehler] HTTP-Funktionen werfen bei Fehlern einen `PixlApiError` mit `status` (HTTP-Code) und `message`, bei TTS zusätzlich `problem`. `403` heißt, dem Schlüssel fehlt ein [Recht](/sdk/api-keys#rechte): ```js try { await pixl.events({ since: 'stream' }); } catch (e) { if (e.status === 404) console.log('Noch kein Stream gelaufen'); } ``` Fehler in deinen eigenen Handlern landen in der Konsole, brechen aber die Verbindung nicht ab. # StreamElements (https://docs.pixl.lennox.zip/sdk/streamelements) > Subs und Bits seit Stream-Start als StreamElements Custom Widget So baust du in StreamElements ein Widget, das live zeigt, wie viele Subs und Bits seit Stream-Start reingekommen sind. Leg in pixl auf der Seite **API & SDK** einen Schlüssel an, z.B. „StreamElements“. Als Recht reicht **Events lesen**. Öffne in StreamElements dein Overlay, füge über **+ → Static/Custom → Custom widget** ein Custom Widget hinzu und klick auf **Open Editor**. Trag in die Tabs **HTML**, **CSS**, **JS** und **Fields** den Code von unten ein. Schließ den Editor und füge in den Widget-Einstellungen links deinen Schlüssel ins Feld **pixl API-Schlüssel** ein. ## HTML [#html] ```html
0 Subs
0 Bits
``` ## CSS [#css] ```css .counter { font-family: sans-serif; font-size: 36px; color: white; text-shadow: 0 2px 6px rgba(0, 0, 0, 0.6); } ``` ## JS [#js] StreamElements ruft `onWidgetLoad` auf, sobald das Widget bereit ist. Dort stehen auch die Werte aus den Fields. ```js window.addEventListener('onWidgetLoad', (obj) => { const { pixlKey, since } = obj.detail.fieldData; if (!pixlKey) return; const pixl = new Pixl({ key: pixlKey }); let subs = 0; let bits = 0; pixl.feed( { since: since || 'stream', types: ['twitch.sub', 'twitch.sub_gift', 'twitch.cheer', 'twitch.power_up'] }, (data, event) => { if (event.replay) return; // erneut abgespielte Events nicht mitzählen if (event.event === 'twitch.sub') subs += 1; if (event.event === 'twitch.sub_gift') subs += data.amount ?? 0; if (event.event === 'twitch.cheer' || event.event === 'twitch.power_up') bits += data.bits ?? 0; document.querySelector('#subs').textContent = subs; document.querySelector('#bits').textContent = bits; }, ); }); ``` ## Fields [#fields] ```json { "pixlKey": { "type": "text", "label": "pixl API-Schlüssel", "value": "" }, "since": { "type": "text", "label": "Zählen ab (leer = seit Stream-Start, sonst z.B. 2026-10-01)", "value": "" } } ``` ## Tipps [#tipps] - Der Schlüssel steht im Field statt im Code. So kannst du den Code weitergeben, ohne den Schlüssel mitzuschicken. - Was du zählst, bestimmst du selbst im Handler. Welche Felder die Events haben (z.B. `tier` bei Subs), steht in der [Event-Referenz](/sdk/events#event-referenz). - Wie `feed()` Historie und Live-Events zusammenführt, steht unter [Historie + live](/sdk/feed). - Für Alerts auf einzelne Events nutzt du `pixl.on('twitch.cheer', …)`, siehe [SDK-Referenz](/sdk/reference#events). # TTS (https://docs.pixl.lennox.zip/sdk/tts) > Texte vorlesen lassen und im Overlay abspielen pixl kann Texte von einer Stimme vorlesen lassen. Das Audio erzeugt pixl, abgespielt wird es in deinen Overlays. Dafür reichen ein paar Zeilen mit dem SDK. Was vorgelesen wird, kommt aus zwei Richtungen: - **Dein eigener Code** mit `pixl.tts.speak()`, z.B. für Kanalpunkte oder eigene Alerts - **Das Dashboard**, über „An Overlays senden“ auf der TTS-Seite Jedes Mal schickt pixl ein `tts.ready` an alle verbundenen Overlays. Ein Overlay mit `pixl.tts.player()` spielt es dann ab. ## Abspielen im Overlay [#abspielen-im-overlay] Der Schlüssel braucht das Recht **TTS abspielen** (`tts:read`). ```html ``` Das ist schon alles. Der Player spielt alles der Reihe nach ab und hält sich an „Überspringen“ und „Alles stoppen“ aus dem Dashboard. In OBS und in StreamElements Custom Widgets darf er sofort abspielen. In einem normalen Browserfenster musst du einmal auf die Seite klicken, sonst blockiert der Browser den Ton. ### Optionen [#optionen] | Option | Standard | Bedeutung | |---|---|---| | `volume` | `1` | Lautstärke von 0 bis 1 | | `ignoreReplays` | `false` | Erneut gesendete Einträge („Nochmal senden“ im Verlauf) nicht abspielen | | `gapMs` | `500` | Pause zwischen zwei Einträgen | | `maxQueue` | `20` | So viele dürfen warten, ältere fallen raus | | `onStart(item)` | | Läuft, bevor ein Eintrag startet, z.B. für Untertitel | | `onEnd(item, { skipped })` | | Läuft, wenn ein Eintrag fertig oder übersprungen ist | | `onWord(word, item)` | | Läuft bei jedem Wort, sobald es gesprochen wird. Siehe [Wort für Wort](#wort-für-wort) | `item` hat `id`, `text` (so wie vorgelesen, bei sehr langen Texten gekürzt, dann ist `textTruncated` gesetzt), `voice`, `source` (`api` oder `dashboard`), `replay` und mit `onWord` die Zeitmarken in `marks`. Der Player hat außerdem `skip()`, `clear()`, `setVolume(v)`, `pending` (wartende Einträge) und `destroy()`. ```js const subtitle = document.querySelector("#subtitle"); pixl.tts.player({ onStart: (item) => (subtitle.textContent = item.text), onEnd: () => (subtitle.textContent = ""), }); ``` ### Wort für Wort [#wort-für-wort] Mit `onWord` läuft der Text synchron zum Audio mit, z.B. für Untertitel im Karaoke-Stil. `word` hat `word` (das Wort), `index` (Position im Satz), `start` und `end` (Millisekunden ab Start des Audios). Die Zeitmarken aller Wörter stehen schon in `onStart` als `item.marks` bereit, so kannst du den ganzen Satz vorab aufbauen und dann Wort für Wort hervorheben: ```js const subtitle = document.querySelector("#subtitle"); pixl.tts.player({ onStart: (item) => { subtitle.innerHTML = (item.marks ?? []).map((m) => `${m.word}`).join(" "); }, onWord: (word) => subtitle.children[word.index]?.classList.add("spoken"), onEnd: () => (subtitle.innerHTML = ""), }); ``` Gut zu wissen: - Zeitmarken kosten beim Anbieter extra, ungefähr so viel wie das Audio noch einmal. Der Player lädt sie deshalb nur, wenn `onWord` gesetzt ist. Für denselben Text entstehen sie nur einmal. - Satzzeichen tauchen nicht als eigene Wörter auf, Zahlen mit Einheit können ein Wort sein („5 Euro“). Baue Untertitel deshalb aus `item.marks`, nicht aus `item.text`. - Gibt es keine Zeitmarken (Fehler, Stimme ohne diese Fähigkeit), wird trotzdem vorgelesen, nur `onWord` bleibt stumm. - Selbst abrufen geht mit `await pixl.tts.marks(id)`. ## Selbst vorlesen lassen [#selbst-vorlesen-lassen] Der Schlüssel braucht das Recht **TTS auslösen** (`tts:write`). Jeder Aufruf verbraucht Credits deines Kanals, gib das Recht also nur Schlüsseln, die es wirklich brauchen. ```js const spoken = await pixl.tts.speak("Danke fürs Einlösen!", { voice: "polly:vicki" }); // { id, text, voice } ``` Ohne `voice` gilt die Standardstimme des Kanals. `speak()` spielt nichts ab, das macht der Player im Overlay. Mit `filters` legst du fest, was pixl am Text bereinigt (siehe [Was pixl am Text macht](#was-pixl-am-text-macht)). Standardmäßig ist alles an: ```js pixl.tts.speak(text, { filters: false }); // Links, Wiederholungen und gesperrte Wörter nicht filtern pixl.tts.speak(text, { filters: { links: false } }); // nur Links drin lassen pixl.tts.speak(text, { filters: { blockedWords: false } }); // gesperrte Wörter ignorieren ``` | Filter | Bedeutung | |---|---| | `links` | Links werden weggelassen | | `repeats` | Wiederholungen werden gekürzt | | `blockedWords` | Text mit gesperrtem Wort wird abgelehnt | Unsichtbare Zeichen entfernt pixl immer, und der Text wird immer auf die eingestellte Länge gekürzt. Texte aus dem Dashboard filtert pixl immer, die Option gilt nur für deine eigenen Aufrufe. Geht etwas schief, wirft `speak()` einen `PixlApiError` mit `status` und `problem`: | `problem` | Status | Bedeutung | |---|---|---| | `disabled` | `403` | Kommt nicht mehr vor, ausgeschaltetes TTS meldet jetzt `module_disabled`. Prüf beide, wenn dein Widget schon auf `disabled` reagiert | | `module_disabled` | `403` | Das Modul TTS ist in den Kanal-Einstellungen aus. Gilt für alle TTS-Endpunkte, auch Audio und Stimmen | | `quota` | `429` | Credits für diesen Monat aufgebraucht | | `global_limit` | `429` | Der Server hat sein Tageslimit erreicht, morgen geht es wieder | | `blocked` | `422` | Der Text enthält ein gesperrtes Wort | | `empty` | `422` | Nach dem Bereinigen ist nichts übrig | | `unknown_voice` | `422` | Diese Stimme gibt es nicht | | `not_configured`, `unavailable` | `503` | TTS ist auf dem Server nicht eingerichtet oder gerade nicht erreichbar | ## Stimmen [#stimmen] Die Standardstimmen gibt es immer: | ID | Name | Sprache | |---|---|---| | `polly:hans` | Hans | Deutsch, die bekannte Spenden-Stimme | | `polly:marlene` | Marlene | Deutsch | | `polly:vicki` | Vicki | Deutsch | | `polly:brian` | Brian | Englisch | Dazu kommen KI-Stimmen (`tier: "ai"`, IDs wie `fish:3f2c…`), die pixl nach und nach in den Katalog aufnimmt. Sie klingen deutlich natürlicher und kosten mehr Credits pro Zeichen, wie viele steht in `creditsPerChar`. Eigene Stimmen, die im Kanal eingesprochen wurden (`tier: "custom"`), haben dasselbe Format und gibt es nur in diesem Kanal. Die vollständige Liste samt Standardstimme des Kanals liefert `await pixl.tts.voices()`. Die ID einer Stimme kopierst du in pixl unter TTS → Einstellungen direkt bei der Stimme. Du kannst sie fest in dein Widget schreiben. KI-Stimmen können aber wegfallen, dann antwortet `speak()` mit `unknown_voice`. Lass in dem Fall `voice` weg, dann liest die Standardstimme des Kanals vor. ## Was pixl am Text macht [#was-pixl-am-text-macht] Bevor etwas vorgelesen wird, bereinigt pixl den Text (bei `speak()` lassen sich Links, Wiederholungen und gesperrte Wörter [abschalten](#selbst-vorlesen-lassen)): - Links werden weggelassen - Wiederholungen werden gekürzt: aus „HYPEEEEEE“ wird „HYPEEE“, aus zehnmal demselben Wort dreimal - Unsichtbare Zeichen fliegen raus - Der Text wird auf die eingestellte Länge gekürzt - Enthält er ein gesperrtes Wort, wird er nicht vorgelesen. Erkannt werden auch Schreibweisen wie „b4d“ oder „b.a.d“ ## Credits [#credits] Jeder Kanal hat Credits, 1 Credit ist 1 Zeichen mit einer Standardstimme. KI-Stimmen kosten mehrere Credits pro Zeichen (`creditsPerChar` in `voices()`). Es gibt zwei Arten: ein **Monatsbudget**, das es jeden Monatsersten neu gibt (Reste verfallen), oder ein **Guthaben**, das nicht abläuft und beim Vorlesen abgebucht wird. `speak()` und Ausprobieren im Dashboard verbrauchen Credits. Derselbe Text mit derselben Stimme kostet nur einmal, solange er im Zwischenspeicher liegt. Wie viele noch übrig sind, siehst du oben auf der TTS-Seite in pixl. ## Ohne SDK [#ohne-sdk] Die Endpunkte stehen in der [HTTP-API](/sdk/api#tts).