Aufzeichnung
Wann verwenden
Greifen Sie auf diese Fähigkeit zurück, sobald Sie den Rekorder berühren: den Aufnahmeknopf, die Symbolleiste während der Aufnahme, Berechtigungsabfragen, den teilweisen Upload oder die Kamera-Blase. Wenn Sie Unterstützung für eine neue Quelle hinzufügen (z.B. Tab-Aufnahme, iPhone Continuity-Kamera) oder ändern, wie Chunks serverseitig abgeschlossen werden, ist dies Ihre Karte.
Berührtes Datenmodell
recordings— die Zeile wird erstellt, sobald der Benutzer auf „Aufnehmen“ drückt oder eine Quelle importiert. Native/Datei-Aufnahmen durchlaufen die Zuständeuploading→processing→ready(oderfailed).videoUrl,durationMs,videoSizeBytes,width,height,hasAudio,hasCamerawerden während des Upload-Streams befüllt. Loom-Importe verwendenimport-loom-recordingund werden entweder als Clips-gehostetes MP4 oder als Loom-Einbettung bereit, wenn der MP4-Export nicht verfügbar ist.application_state.record-intent— der Agent schreibt dies, wenn er eine Aufnahme starten möchte. Die Benutzeroberfläche liest und löscht es und fordert dann zur Berechtigung auf.application_state.navigation— wird auf{ view: "record" }gesetzt, während der Rekorder aktiv ist.
Binäre Uploads erfolgen über die benutzerdefinierten API-Routen (/api/uploads/:id/chunk und /api/uploads/:id/abort) anstatt über Aktionen, da Aktionen nicht für binäre Streaming-Daten geeignet sind. Der letzte Chunk ruft finalize-recording auf. Loom-URL-Importe sind nur Metadaten und sollten über die Aktion import-loom-recording abgewickelt werden.
Einige Aufnahmen sind mit einem Meeting verknüpft – wenn meeting_id in der Aufnahmezelle nicht null ist, wurde sie über start-meeting-recording erstellt und sowohl die recording- als auch die meetings-Fähigkeit sind anwendbar. Siehe die meetings-Fähigkeit für die bidirektionale Verknüpfung.
Lebenszyklus
- Absicht. Entweder klickt der Benutzer auf „Aufnehmen“ (global
Cmd+Shift+L) oder der Agent ruftpnpm action start-recording --mode=screenauf. Die Agent-Version schreibtrecord-intentin den Anwendungszustand; die Benutzeroberfläche nimmt es auf und initiiert denselben Ablauf wie ein Benutzerklick. - Berechtigung. Rufen Sie
navigator.mediaDevices.getDisplayMedia({ video, audio })für den Bildschirm auf,getUserMedia({ video, audio })für die Kamera. Fordern Sie nicht ohne eine Benutzergeste zur Berechtigung auf. Der Agent-Pfad verlässt sich auf den Button der Benutzeroberfläche – wir umgehen niemals das Berechtigungsmodell des Browsers. - Zeile erstellen. Sobald der Stream gewährt wird, rufen Sie
create-recordingauf, um die Zeile mitstatus: "uploading"und einer vorab generierten ID einzufügen. Diese ID wird für jeden nachfolgenden Chunk-Upload verwendet. - Aufnehmen. Starten Sie einen
MediaRecordermitmimeType: "video/webm;codecs=vp9,opus"(Fallback auf vp8, dann Browser-Standard). Verwenden Sietimeslice: 2000, damit Chunks alle 2 Sekunden eintreffen. - Jeden Chunk hochladen.
ondataavailablestreamt Bytes an/api/uploads/chunkmit den HeadernX-Recording-IdundX-Chunk-Index, während unabhängig davon die rohen Chunks als „best-effort“-Sicherung inIndexedDBgespiegelt werden. Ein vorübergehender Zustellfehler pausiert den Upload und setzt die In-Memory-Quelle nur fort, solange der aktuelle Browser-Tab geöffnet bleibt; es gibt keinen Hintergrund-Wiederholungs-Worker oder eine Wiederherstellungsgarantie für geschlossene Tabs. EinIndexedDB-Schreibfehler blockiert weder die Aufnahme noch den Upload. - Live-Transkription. Parallel zum MediaRecorder führt
useLiveTranscriptiondie Web Speech API aus, um Transkripttext in Echtzeit zu sammeln. Beim Stoppen ruft der Clientsave-browser-transcriptauf, um das Ergebnis sofort zu speichern – kein API-Schlüssel erforderlich. Desktop-Aufnahmen verwenden zuerst lokales Whisper/macOS-Spracherkennung, wenn verfügbar, und fallen im Webview auf Nicht-Mac-Geräten auf Web Speech zurück, bevor sie auf Upload-Transkription angewiesen sind. - Abschließen. Beim Stoppen senden Sie den letzten Chunk an
/api/uploads/:id/chunk?isFinal=1. Die Route ruftfinalize-recordingauf, das Chunks zusammensetzt, die Medien suchbar macht (siehe unten), die fertigen Medien hochlädt, wenn Speicher konfiguriert ist, denstatusaufreadysetzt und dannrequest-transcriptfür eine höhere Qualität startet (sieheai-video-tools). - Sofort navigieren. Desktop-Rekorder öffnen
/r/:id, sobald „Stop“ die Finalisierung startet. Die Aufnahmezelle existiert bereits, sodass die Seite den Titel, den Freigabelink und den Upload-Fortschritt anzeigen kann, während sie vonuploadingoderprocessingaufreadypollt. Warten Sie nicht auf die Upload-/Finalisierungs-Antwort, bevor Sie die Seite öffnen.
Lebenszyklus der mobilen Begleit-App
Die mobile App „Agent-Native“ verwendet dieselben Aufnahmezellen und binären Upload-Routen mit nativen Aufnahme-Primitiven:
- Kamera-Video/Import verwendet
expo-camera/ die System-Fotoauswahl; Meeting-Audio verwendetexpo-audiomit aktivierter Hintergrundaufnahme. - Die native Datei wird in das Dokumentenverzeichnis kopiert, und ein typisierter AsyncStorage-Aufnahmejob wird geschrieben, bevor der Upload beginnt.
create-recordingerhält eine stabile, clientseitig generierte ID,sourceAppName: "Agent-Native Mobile"und den Container-MIME-Typ.- Der Upload liest maximal 3 MiB über ein Expo FileHandle und persistiert den nächsten Chunk-Index nach jedem bestätigten POST. Das serverseitige Limit von 4 MiB gilt weiterhin.
- Die Synchronisierung im Vordergrund/bei Wiederaufnahme pollt
/api/uploads/:id/status; wiederholbare Fehler warten mit exponentiellem Backoff und verwerfen niemals die lokale Datei. Der Abschluss kann eine lokale Benachrichtigung auslösen, und die Aufnahme erscheint überall in Clips.
Mobiles Audio-M4A ist ein MP4-Container und verwendet den MP4-MIME-Alias der Aufnahmeroute. Die Bytes werden nicht konvertiert oder vollständig in den JavaScript-Speicher geladen.
Da das Telefon jede Audio-/Videodatei vor jeglicher Netzwerkarbeit persistiert und begrenzte Chunk-Uploads aus seiner dauerhaften Warteschlange fortsetzt, geht bei einem vorübergehenden Upload-Fehler nie eine Aufnahme verloren. Bitten Sie Benutzer nicht, einen Aufnahmebildschirm offen zu halten oder nach einem fehlgeschlagenen Upload neu aufzunehmen; sagen Sie ihnen, sie sollen Clips erneut verbinden und den gespeicherten Job von der mobilen Startseite aus wiederholen.
Mobile Meeting-Aufnahme ist nur Mikrofon; behaupten Sie niemals, dass sie die Desktop-Qualität von Mikrofon plus Systemaudio hat. Siehe die meetings-Fähigkeit für die Bedeutung bezüglich der Teilnehmerzuordnung.
Suchbare Wiedergabe (keine rohen MediaRecorder-Ausgaben ausliefern)
Rohe MediaRecorder-Dateien sind nicht für progressives HTTP-Streaming geeignet, was sich als „Clip braucht Minuten zum Laden“ und „puffert bei jedem Sprung neu“ äußert, obwohl die Datei problemlos heruntergeladen wird:
- MP4 wird mit dem
moov-Metadatenatom nachmdatgeschrieben, sodass ein Player die gesamte Datei abrufen muss, bevor er starten oder springen kann. - WebM ist ein Livestream ohne Cues (Suchindex) und unbekannter Segmentdauer, sodass Chrome
currentTime = Xnicht berücksichtigt und scannen/herunterladen muss.
finalize-recording behebt dies vor dem Upload: MP4 erhält einen reinen TS-Faststart (server/lib/faststart.ts), WebM erhält ein verlustfreies ffmpeg -c copy-Remux, das einen SeekHead + Cues + echte Dauer schreibt (server/lib/video-remux.ts). Beides ist eine „best-effort“-Operation – bei Fehlern laden wir das Original hoch und blockieren nie die Finalisierung. Aufnahmen über CLIPS_INLINE_REMUX_MAX_BYTES (Standard 200 MB) überspringen den Inline-Durchlauf und werden im Hintergrund repariert.
Der Streaming-/Wiederaufnehmbare Upload-Pfad leitet Rohbytes direkt zum Anbieter weiter und kann diese nicht inline umschreiben, daher plant finalize-recording für diese einen Hintergrunddurchlauf ensureRecordingSeekable.
Um Clips zu reparieren, die vor dieser Funktion (oder über Streaming) hochgeladen wurden, rufen Sie die Aktion reprocess-recording auf: --id, --ids='[...]' oder --all --limit=N. Sie ruft die Medien des Anbieters erneut ab, schreibt sie um, lädt sie erneut hoch und aktualisiert die Zeile. Sie ist idempotent (bereits suchbare Clips werden übersprungen, es sei denn, --force wird verwendet) und berührt nur die vom Anbieter gehosteten Clips, die sich im Besitz des Aufrufers befinden. Dies ist das richtige Werkzeug, wenn ein Benutzer einen bestimmten langsamen/puffernden Clip meldet.
Das Suchbarkeits-Remuxing kann eine Aufnahme nicht reparieren, deren Audio fortgesetzt wird, während die Videospur eine große Zeitstempellücke aufweist (häufig, wenn ein mobiler Browser die Kamera suspendiert, nachdem der Benutzer die App gewechselt hat). Für einen Clip, der einfriert oder scheinbar vor seiner angegebenen Dauer stoppt, rufen Sie reprocess-recording mit --normalizeTimeline=true auf. Dieser explizite Modus verwendet denselben besitzerbezogenen Abruf- und Upload-Fluss, transkodiert jedoch vollständig in ein konstantes 30-fps Faststart-MP4 (H.264 + AAC). Er bewahrt Audio und dupliziert den letzten dekodierten Videoframe durch Lücken fehlender Frames. Die Aktion lädt auf ein neues Medienobjekt hoch und aktualisiert die Zeile atomar erst, nachdem die verifizierte Ausgabe gespeichert wurde; jeder Fehler bei der Transkodierung, Audio-Verifizierung, beim Upload oder der gleichzeitigen Aktualisierung belässt die Original-URL und das Format unverändert.
Loom-Import
Verwenden Sie import-loom-recording für Loom-Freigabe- oder Einbettungs-URLs. Die Aktion validiert die Loom-URL, liest Loom-oEmbed-Metadaten vom öffentlichen Loom-Endpunkt und erstellt eine ready-Aufnahme mit der Loom-Einbettungs-URL, dem Thumbnail, Titel, der Dauer und den Abmessungen. Wenn Loom auf der Freigabeseite eine signierte öffentliche Transkript-JSON-URL bereitstellt, importiert die Aktion dieses Transkript in Clips und speichert normalisierte Segmente; speichern Sie niemals signierte Loom-CDN-URLs.
Wenn Loom ein herunterladbares öffentliches MP4 bereitstellt, lädt import-loom-recording es herunter, lädt die Bytes erneut in den Clips-Speicher hoch und erstellt eine bereite, abspielbare, von Clips gehostete Aufnahme, wobei das öffentliche Transkript von Loom importiert wird, wenn die Freigabeseite eines bereitstellt. Wenn Loom eine öffentliche Wiedergabe ermöglicht, aber kein MP4 bereitstellt, behält die Aktion eine bereite, einbettungsgestützte Aufnahme bei und importiert dennoch das Transkript, wenn verfügbar.
Einbettungsgestützte Loom-Importe rendern einen Loom-iframe und verbergen den nativen Clips-Editor. Neu hochgeladene Loom-Importe unterstützen natives Clips-Trimmen, Exporte, Frame-Extraktion und uploadbasierte Transkription; bitten Sie den Benutzer, die originale Videodatei hochzuladen, wenn diese Fähigkeiten für einen einbettungsgestützten Import benötigt werden.
Browser-Diagnostik und Rekorder-Installationsoptionen
Browser-Aufnahmen können begrenzte, geschwärzte Diagnosedaten über createBrowserDiagnosticsCapture speichern: Konsolenmeldungen plus Fetch/XHR-Methode, URL, Status, Dauer und Fehler. Der Browser-Rekorder erfasst nur Aktivitäten von der Rekorderseite selbst. Die Clips Chrome-Erweiterung ist der aktive Tab-Pfad für Browser-Protokolle: Sie startet /record mit einer Erweiterungs-Aufnahmesitzung und übergibt developerLogs=1/0, speichert dann Diagnosedaten mit Quelle extension.
Der Eintrag im Web Store ist live, daher wird die öffentliche Chrome-Erweiterungsoption standardmäßig angezeigt. UI-Eingabeaufforderungen, die ansonsten „Desktop-App herunterladen“ sagen, verwenden die gemeinsame Installationsauswahl-Komponente (CaptureInstallButton / CaptureInstallInlineLink), um zwei Optionen anzubieten: Chrome-Erweiterung für Browser-Protokolle und Desktop-App für die nahtloseste native Aufnahme. Setzen Sie VITE_CLIPS_CHROME_EXTENSION_ENABLED=0, um die Chrome-Option wieder auszublenden, oder VITE_CLIPS_CHROME_EXTENSION_URL, um auf einen anderen Eintrag zu verweisen.
save-browser-diagnostics ist UI/intern. Es speichert begrenzte Konsolenprotokolle plus Fetch/XHR-Methode, URL-Pfad/Query-Schlüssel, Status und Dauer. Es erfasst niemals Header, Bodies, Cookies oder Netzwerk-URL-Query-Werte. Konsolentext behält nützliche, nicht geheime Werte, während anmeldeinformationsverdächtige Schlüssel und Header geschwärzt werden. Verwenden Sie get-recording-player-data für die vollständigen Diagnose-Payloads, wenn Sie Editor-Zugriff haben; siehe die video-sharing-Fähigkeit für den engeren geschwärzten Stream, den der öffentliche Agent-Kontext bereitstellt.
Chrome-Erweiterung
Die Erweiterung befindet sich in chrome-extension/. Sie startet /record mit clipsExtensionId und clipsCaptureSessionId, und der Rekorder sendet CLIPS_CAPTURE_START / CLIPS_CAPTURE_STOP / CLIPS_CAPTURE_CANCEL zurück an die Erweiterung. Sie verwendet die Chrome Debugger API nur auf dem Tab, von dem aus der Benutzer gestartet hat, nur während eine Aufnahme aktiv ist, und gibt dieselbe geschwärzte Diagnoseform zurück, die von save-browser-diagnostics gespeichert wird.
Die Erweiterung verbessert auch GitHub-Issue- und PR-Markdown: Ein schmales github.com-Content-Skript erkennt Clips /r/, /share/ und /embed/-Links und rendert dann den vorhandenen /embed/:id-Player in einem erweiterungseigenen Vorschau-iframe, sodass das Video abspielbar ist, ohne GitHub zu verlassen. Beschränken Sie dies auf GitHub, es sei denn, es gibt eine bewusste Berechtigungsüberprüfung.
Ordner, Bereiche und Massenverschiebungen
Verwenden Sie move-recording sowohl für einzelne als auch für Massenordnerverschiebungen. Übergeben Sie id für einen Clip oder ids für die ausgewählten Clips und folderId: null, um sie in die Bibliothek oder den Bereichsstamm zu verschieben.
Screenshots
Ein Screenshot ist eine recordings-Zeile mit kind = "image" (isImageRecording in shared/recording-kind.ts). Er teilt Ordner, Bereiche, Freigabe, Kommentare und Suche mit Clips und hat keine der Videoteile: kein Transkript, Kapitel, Filmstreifen, Zeitleiste oder Audio. list-recordings --kind=image listet sie auf.
- Aufnahme, Annotation und Schwärzung sind Browserarbeit (
create-screenshot,save-screenshot-edits, beide nur UI). Der Agent liest einen Screenshot überget-recording-player-dataoder/api/agent-frame.jpg, das das Bild selbst für jedesatMszurückgibt. imageUrlist das ausgelieferte Bild;baseImageUrlist die unmarkierte Basis, auf die ein Editor zeichnet. Beide werden über/api/thumbnail/:idausgeliefert, das das Passwort, das Ablaufdatum und die Schwärzungssperre anwendet. Geben Sie niemals die Speicher-URLs weiter.- Die Schwärzung erfolgt zweistufig, wie bei Video: Boxen werden als ausstehende Overlays gespeichert (für Betrachter gesperrt), dann eingebrannt. Ein Brennvorgang schreibt
editsJson.burnInProgress, bevor das Original gelöscht wird, und löscht es danach;unreclaimedUrlslistet ältere Kopien auf, die noch nicht gelöscht wurden. Beides ist server-seitig (server/lib/screenshot-edits.ts) und darf niemals einen Betrachter erreichen. - Video-Edit-Aktionen lehnen Screenshots bei
assertNativeRecordingMediaab; nursave-screenshot-editsschreibt daseditsJsoneines Screenshots.
Pause / Fortsetzen
MediaRecorder.pause() / .resume() werden in allen modernen Browsern unterstützt. Behalten Sie eine einzige MediaRecorder-Instanz über Pausen hinweg bei – reißen Sie den Stream nicht ab, sonst wird die Berechtigungsabfrage erneut ausgelöst. Während der Pause leert der Upload-Worker weiterhin seinen Puffer, sodass wir vor dem Stoppen des Benutzers aufholen.
Kamera-Blase
Wenn der Modus screen+camera ist, ist „die Blase“ zwei verschiedene Dinge:
- Selbstansicht auf dem Bildschirm.
CameraBubblerendert ein einfaches, nicht aufgezeichnetes<video>-Element, das der Benutzer während des Countdowns/der Aufnahme zieht, um sich selbst im Bild zu positionieren. Es wird nie aufgezeichnet – es ist nur eine lokale Rahmungs-UI. - Aufgezeichnetes Compositing. Die Display-Erfassung kann dieses DOM-Element nicht einschließen, sobald der Benutzer ein anderes Fenster/eine andere App aufzeichnet, daher muss das gespeicherte Video den Kamera-Kreis einbrennen, bevor der `MediaRecorde