Compare commits

...

247 Commits

Author SHA1 Message Date
lukasIO
12cee3ed06
Update livekit dependencies (#512) 2026-02-19 17:07:12 +01:00
renovate[bot]
2220072d47
chore(deps): update dependency node to v24 (#491)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-19 16:56:32 +01:00
renovate[bot]
392ca136de
fix(deps): update dependency @livekit/krisp-noise-filter to v0.4.1 (#505)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-19 16:54:36 +01:00
renovate[bot]
3a75f3222f
fix(deps): update livekit dependencies (non-major) (#499)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-10 16:24:35 +01:00
vercel[bot]
f80673aba8
Fix React Server Components CVE vulnerabilities (#503)
Updated dependencies to fix Next.js and React CVE vulnerabilities.

The fix-react2shell-next tool automatically updated the following packages to their secure versions:
- next
- react-server-dom-webpack
- react-server-dom-parcel  
- react-server-dom-turbopack

All package.json files have been scanned and vulnerable versions have been patched to the correct fixed versions based on the official React advisory.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
2025-12-26 11:35:52 +01:00
vercel[bot]
690dc1011a
Update Next.js/React Flight RCE vulnerability patches (#501)
## React Flight / Next.js RCE Advisory - Security Update

### Summary
Updated the project to address the React Flight / Next.js RCE advisory (CVE-2024-50383) by upgrading Next.js to the patched version.

### Vulnerability Assessment
 **Project is affected by the advisory:**
- Uses **Next.js 15.2.x** (vulnerable version range)
- Does NOT use React Flight packages (react-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack)
- Uses React 18.3.1 (not vulnerable React 19.x versions)

### Changes Made

#### Modified Files:
1. **package.json**
   - Upgraded `next` from `15.2.4` to `15.2.6` (patched version for 15.2.x)
   - No React or React DOM changes required (Next.js manages its own patched React versions)

2. **pnpm-lock.yaml**
   - Updated lockfile to reflect `next@15.2.6` installation
   - All dependencies resolved correctly with patched versions

### Implementation Details
- This project is a Next.js 15 application without React Server Components/Flight
- The RCE vulnerability in Next.js 15.2.x is addressed by upgrading to 15.2.6
- No React Flight packages required updating since they are not used
- React versions (18.3.1) are not affected by this vulnerability

### Build Status
⚠️ **Note on Pre-existing Issue:**
The build fails due to corrupted image files in `public/background-images/` (pre-existing issue):
- `ali-kazal-tbw_KQE3Cbg-unsplash.jpg` (130 bytes - should be larger)
- `samantha-gades-BlIhVfXbi9s-unsplash.jpg` (132 bytes - should be larger)

This image corruption issue exists in the original codebase and is unrelated to the security update. The Next.js upgrade to 15.2.6 itself is successful and the patched version is correctly installed.

### Testing
- Verified dependency installation with `pnpm install`
- Confirmed lockfile contains `next@15.2.6`
- Confirmed no React Flight packages are used
- Pre-existing image corruption prevents full build, but dependency upgrade is verified

### Security Impact
 **Successfully patched against CVE-2024-50383**
- Next.js upgraded to 15.2.6 (patched version for 15.2.x)
- No vulnerable React Flight packages in use
- React versions remain compatible and secure

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
2025-12-08 12:50:11 +01:00
renovate[bot]
6de1bc8cc6
fix(deps): update dependency livekit-server-sdk to v2.14.2 (#495)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-04 09:33:18 +00:00
renovate[bot]
563925f757
chore(deps): update actions/checkout action to v6 (#497)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 22:31:46 -08:00
renovate[bot]
0b62ed930e
chore(deps): update devdependencies (non-major) (#480)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-29 20:58:17 -08:00
lukasIO
baa4e787a2
Default to dual peer connection for custom tab (#496) 2025-11-20 15:55:51 +01:00
renovate[bot]
dc82cc23b9
fix(deps): update dependency livekit-client to v2.16.0 (#494)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-19 09:56:42 +01:00
renovate[bot]
e9b037bac1
fix(deps): update livekit dependencies (non-major) (#492)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-15 08:20:55 +01:00
lukasIO
49b83637dc
Enable singlePC mode for meet also on prod (#493)
* Enable singlePC mode for meet also on prod

* fix
2025-11-10 11:04:29 +01:00
renovate[bot]
aa9be8cdc0
fix(deps): update dependency livekit-client to v2.15.13 (#487)
* fix(deps): update dependency livekit-client to v2.15.12

* bump

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: lukasIO <mail@lukasseiler.de>
2025-10-21 19:25:41 +02:00
lukasIO
03aac6591a
Enable single pc connection on staging (#488)
* Enable single pc connection on staging

* fix deps

* 'security'

* vp9

* use util
2025-10-16 10:32:49 +02:00
lukasIO
83424b27d5
Revert "Use single pc (#483)" (#484)
* Revert "Update livekit client and use single pc (#483)"

This reverts commit 55adec00d31c25ef40e10f67ef7dd4880c9e81a6.

* still update livekit client
2025-10-13 17:53:27 +02:00
lukasIO
55adec00d3
Update livekit client and use single pc (#483) 2025-10-13 16:57:59 +02:00
renovate[bot]
5ff6fa32ac
chore(deps): update pnpm to v10.18.2 (#408)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 15:38:57 +02:00
renovate[bot]
8e66391a01
fix(deps): update livekit dependencies (non-major) (#481)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-02 17:20:43 +02:00
renovate[bot]
e9dba9861a
fix(deps): update dependency react-hot-toast to v2.6.0 (#473)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 13:21:40 +02:00
renovate[bot]
76234cdf93
fix(deps): update livekit dependencies (non-major) (#475)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-11 14:28:39 +02:00
Tobias Fried
0b4af83a3f
chore(ci): tag deployment versions (#478) 2025-09-09 00:48:31 -06:00
renovate[bot]
6fdf7f0b9a
fix(deps): update livekit dependencies (non-major) (#474)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-29 14:04:10 +02:00
renovate[bot]
372cdfe760
chore(deps): update dependency node to v22 (#470)
* chore(deps): update dependency node to v22

* Update test.yaml

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: lukasIO <mail@lukasseiler.de>
2025-08-15 12:37:50 +02:00
renovate[bot]
fcec3a2459
chore(deps): update devdependencies (non-major) (#451)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-15 12:36:16 +02:00
renovate[bot]
7d1d62b6c3
fix(deps): update dependency livekit-client to v2.15.5 (#472)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-15 12:35:54 +02:00
renovate[bot]
aa310ade64
fix(deps): update livekit dependencies (non-major) (#463)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 17:11:12 +02:00
ThomFoolery
762f1c4a6e
feat: add template media (#465) 2025-07-21 17:50:03 -04:00
ThomFoolery
6ce2570868
feat: add template media (#464) 2025-07-21 16:53:56 -04:00
renovate[bot]
785359f977
fix(deps): update dependency @livekit/components-react to v2.9.13 (#459)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-04 17:52:35 +02:00
lukasIO
26d90de86c
Don't enable krisp by default on low power devices (#457) 2025-07-04 10:14:27 +02:00
David Zhao
f13f8df08e
Dynamic handling of low-power devices (#450) 2025-07-03 19:03:38 +02:00
renovate[bot]
4dd11f412b
fix(deps): update dependency livekit-client to v2.15.2 (#456)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-03 13:10:01 +02:00
renovate[bot]
a8a48d5a7f
fix(deps): update dependency livekit-client to v2.15.1 (#455)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-03 08:06:49 +02:00
renovate[bot]
68046da53c
fix(deps): update dependency @livekit/krisp-noise-filter to v0.3.4 (#453)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 18:41:54 +02:00
lukasIO
8826f588a0
Add cross origin headers for shared memory usage (#454) 2025-07-02 18:37:36 +02:00
lukasIO
3f4b5a14b9
Revert "fix(deps): update dependency @livekit/krisp-noise-filter to v0.3.2 (#446)" (#452)
This reverts commit f6cdb176e9eb5119220e5e092f891871ed96ba28.
2025-07-02 17:34:13 +02:00
renovate[bot]
f6cdb176e9
fix(deps): update dependency @livekit/krisp-noise-filter to v0.3.2 (#446)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 16:54:10 +02:00
renovate[bot]
3260877886
fix(deps): update livekit dependencies (non-major) (#448)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 16:12:42 +02:00
renovate[bot]
e1954a739d
fix(deps): update livekit dependencies (non-major) (#447)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 11:39:17 +02:00
renovate[bot]
03fda24b39
fix(deps): update dependency livekit-client to v2.13.8 (#444)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-23 17:56:54 +02:00
renovate[bot]
2e35ce825e
fix(deps): update livekit dependencies (non-major) (#443)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-19 16:06:06 +02:00
renovate[bot]
62df7245a3
fix(deps): update dependency livekit-client to v2.13.6 (#442)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-19 12:03:42 +02:00
lukasIO
b650fecdd4
Derive region url from project url (#441)
* Derive region url from project url

* add tests

* test workflow

* fix workflow

* ugh

* fix

* fix staging/prod
2025-06-18 17:03:40 +02:00
renovate[bot]
5230af4fb6
chore(deps): update devdependencies (non-major) (#436)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 08:20:23 +02:00
renovate[bot]
8736088a7e
fix(deps): update dependency livekit-client to v2.13.5 (#439)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-15 21:36:47 -07:00
renovate[bot]
5619c99aa9
fix(deps): update dependency livekit-client to v2.13.4 (#437)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-14 22:52:07 -07:00
lukasIO
c99a780f58
optimizations for low-powered devices (#438)
* ensure low power devices don't use high quality for krisp

* Update MicrophoneSettings.tsx

* use lower video publish settings

* fix import

* comments

---------

Co-authored-by: David Zhao <dz@livekit.io>
2025-06-14 17:23:36 -07:00
lukasIO
c4ea8a31ec
Unify e2ee setup on demo and custom page (#434)
* Unify e2ee setup on demo and custom page

* fix
2025-05-27 16:40:42 +02:00
lukasIO
bfde08ea91
fix: recreate options when e2ee enabled changes (#432) 2025-05-27 10:08:33 +02:00
renovate[bot]
96b193098d
fix(deps): update livekit dependencies (non-major) (#431)
* fix(deps): update livekit dependencies (non-major)

* update settings

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: lukasIO <mail@lukasseiler.de>
2025-05-23 16:50:31 +02:00
renovate[bot]
b3b8901cf7
fix(deps): update dependency @livekit/components-react to v2.9.8 (#429)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-23 09:17:03 +02:00
renovate[bot]
e418eeaac4
fix(deps): update livekit dependencies (non-major) (#428)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-22 11:02:47 +02:00
lukasIO
489ee7896b
fix mobile settings view (#427) 2025-05-22 11:02:24 +02:00
Tobias Fried
8a9a5a0aef
feat: add common keyboard shortcuts (#424)
Co-authored-by: lukasIO <mail@lukasseiler.de>
2025-05-21 09:59:03 -06:00
renovate[bot]
71f62858b9
fix(deps): update dependency livekit-client to v2.13.1 (#425) 2025-05-21 08:19:02 +02:00
renovate[bot]
851079eaf0
chore(deps): update devdependencies (non-major) (#417)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-19 08:15:35 +02:00
renovate[bot]
2880d3685d
fix(deps): update dependency livekit-client to v2.13.0 (#421)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-16 17:27:07 +02:00
renovate[bot]
fcd941d859
fix(deps): update dependency @livekit/krisp-noise-filter to v0.3.0 (#420)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-15 14:53:36 +02:00
renovate[bot]
6b6e7c7ee7
fix(deps): update livekit dependencies (non-major) (#419)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-14 18:50:41 +02:00
renovate[bot]
edcb266dc4
fix(deps): update livekit dependencies (non-major) (#416) 2025-04-28 14:41:57 +02:00
lukasIO
680633c33c
chore: update @livekit/track-processors (#414) 2025-04-18 10:17:28 +02:00
lukasIO
efac802d7b
Add background filters to settings menu (#413) 2025-04-15 20:26:22 +02:00
lukasIO
8b2ee6c324
Adopt room context provider pattern (#412) 2025-04-15 20:26:07 +02:00
renovate[bot]
7396247483
fix(deps): update dependency livekit-client to v2.11.2 (#411)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-11 16:27:28 +02:00
renovate[bot]
ffef3846b8
fix(deps): update livekit dependencies (non-major) (#410)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-10 21:29:06 +02:00
lukasIO
52d6f6a416
Use toast instead of window.alert as a recording indicator (#409)
* Use toast instead of window.alert as a recording indicator

* fix color
2025-04-08 08:37:56 +02:00
renovate[bot]
17ff1d6092
fix(deps): update nextjs monorepo to v15 (major) (#406)
* fix(deps): update nextjs monorepo to v15

* fix code for v15 upgrade

* fix param await

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: lukasIO <mail@lukasseiler.de>
2025-04-08 08:09:47 +02:00
renovate[bot]
fca845d3a4
fix(deps): update dependency @datadog/browser-logs to v5.35.1 (#397)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:57:00 +02:00
renovate[bot]
38b3951e7f
fix(deps): update dependency next to v14.2.26 [security] (#407)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:55:46 +02:00
renovate[bot]
edb22f66d1
chore(deps): update devdependencies (non-major) (#400)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:55:21 +02:00
renovate[bot]
4619ccc038
fix(deps): update dependency next to v14.2.25 [security] (#405)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:54:33 +02:00
renovate[bot]
f5355b911f
fix(deps): update livekit dependencies (non-major) (#401)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:52:26 +02:00
renovate[bot]
4a3c54d84a
fix(deps): update livekit dependencies (non-major) (#399)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 14:14:38 +00:00
renovate[bot]
6753a96b91
fix(deps): update dependency livekit-client to v2.9.4 (#398) 2025-02-24 21:49:45 +01:00
renovate[bot]
0a7d440a54
fix(deps): update dependency next to v14.2.24 (#395)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 00:31:26 -06:00
renovate[bot]
c32ba3ae87
fix(deps): update livekit dependencies (non-major) (#396)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-19 13:58:13 +01:00
renovate[bot]
19115417db
fix(deps): update dependency livekit-client to v2.9.1 (#394) 2025-02-11 14:27:47 +01:00
renovate[bot]
a47447c592
fix(deps): update livekit dependencies (non-major) (#390)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 14:22:45 -06:00
Scott Brenner
b97de89158
Correct LiveKit Cloud hyperlink in README.md (#392)
* Correct hyperlink in README.md

* Update README.md
2025-02-03 22:39:53 -08:00
renovate[bot]
2d99e7703e
fix(deps): update dependency @livekit/krisp-noise-filter to v0.2.16 (#389) 2025-01-27 14:18:43 +01:00
renovate[bot]
63747dad36
fix(deps): update livekit dependencies (non-major) (#388)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 16:52:52 +01:00
renovate[bot]
210595ad93
chore(deps): update devdependencies (non-major) (#371)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 11:10:51 +01:00
Jonas Schell
10a0ac4a35
feat(api): add cookie handling for random participant postfix in connection details (#385) 2025-01-14 14:36:03 +01:00
renovate[bot]
686b749282
fix(deps): update dependency @datadog/browser-logs to v5.35.0 (#384) 2025-01-13 19:23:48 +01:00
renovate[bot]
f4ce8f53c3
fix(deps): update dependency @datadog/browser-logs to v5.34.1 (#376)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 18:47:50 +01:00
renovate[bot]
aef4ba5e91
fix(deps): update dependency @livekit/krisp-noise-filter to v0.2.14 (#381)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 18:47:12 +01:00
renovate[bot]
96cac3c5ec
fix(deps): update dependency next to v14.2.23 (#383)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 18:46:54 +01:00
renovate[bot]
f07bef61cf
fix(deps): update livekit dependencies (non-major) (#382)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 17:45:12 +00:00
renovate[bot]
09a45809a9
fix(deps): update dependency next to v14.2.22 (#372)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 14:31:56 +01:00
renovate[bot]
84ee6ac6b0
fix(deps): update dependency livekit-server-sdk to v2.9.5 (#380)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 13:42:56 +01:00
renovate[bot]
18ef3c70f1
fix(deps): update livekit dependencies (non-major) (#377)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-03 23:53:01 -06:00
renovate[bot]
53d309b74f
fix(deps): update dependency next to v14.2.21 [security] (#379)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-03 23:51:47 -06:00
renovate[bot]
23d8fbf5b0
fix(deps): update dependency livekit-server-sdk to v2.9.3 (#374)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-05 10:25:07 -06:00
renovate[bot]
fb4a8b0e11
fix(deps): update dependency livekit-client to v2.7.3 (#373)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-03 21:52:01 +01:00
renovate[bot]
f981960a08
fix(deps): update livekit dependencies (non-major) (#370)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-03 18:05:58 +01:00
renovate[bot]
7c4597d865
fix(deps): update dependency @datadog/browser-logs to v5.32.0 (#369)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-27 14:29:31 +01:00
renovate[bot]
b3548ad265
fix(deps): update dependency livekit-client to v2.7.0 (#368)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-26 15:58:24 +01:00
renovate[bot]
bb949d2b73
fix(deps): update dependency @datadog/browser-logs to v5.31.1 (#366)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 15:00:20 +01:00
renovate[bot]
5bf838a880
fix(deps): update livekit dependencies (non-major) (#367)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 15:00:03 +01:00
renovate[bot]
6bea1c4852
fix(deps): update livekit dependencies (non-major) (#365)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-19 11:56:48 +01:00
renovate[bot]
294a478d7a
fix(deps): update dependency @datadog/browser-logs to v5.30.1 (#364)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 16:38:05 +01:00
renovate[bot]
436f4ebefc
fix(deps): update dependency @datadog/browser-logs to v5.30.0 (#363)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:28:21 -07:00
renovate[bot]
1f08a442bb
fix(deps): update dependency @livekit/krisp-noise-filter to v0.2.13 (#362)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:27:58 -07:00
renovate[bot]
227378884e
fix(deps): update dependency next to v14.2.18 (#350)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:27:39 -07:00
renovate[bot]
6e8081abba
fix(deps): update dependency @datadog/browser-logs to v5.30.0 (#352)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:07:21 -07:00
renovate[bot]
0df1f08871
chore(deps): update devdependencies (non-major) (#360)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:06:44 -07:00
renovate[bot]
ae5352331f
fix(deps): update livekit dependencies (non-major) (#357)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:03:04 -07:00
renovate[bot]
86038b2e0d
chore(deps): update dependency @types/node to v22 (#359)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:02:24 -07:00
rektdeckard
4c209e7cfa chore(meta): add template graphic 2024-10-17 20:49:29 -06:00
renovate[bot]
ffbbea062b
fix(deps): update dependency @livekit/components-styles to v1.1.4 (#355)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-15 13:55:39 +02:00
renovate[bot]
da88946c11
fix(deps): update livekit dependencies (non-major) (#354)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 22:30:17 +02:00
renovate[bot]
0bc4c00627
fix(deps): update dependency livekit-client to v2.5.8 (#353)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-10 17:06:33 +02:00
renovate[bot]
8ddaec6cec
fix(deps): update dependency livekit-server-sdk to v2.7.1 (#351)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-09 15:58:04 +02:00
renovate[bot]
45dfb8e2e5
fix(deps): update livekit dependencies (non-major) (#349)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 16:20:05 +02:00
renovate[bot]
b0b0a1db32
chore(deps): update devdependencies (non-major) (#347)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-02 15:40:14 +02:00
renovate[bot]
c1cbbd943d
fix(deps): update livekit dependencies (non-major) (#344)
* fix(deps): update livekit dependencies (non-major)

* Update import path for krisp

* irngore misonfigured tinykeys package

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: lukasIO <mail@lukasseiler.de>
2024-10-02 15:37:41 +02:00
renovate[bot]
75b144497c
fix(deps): update dependency @datadog/browser-logs to v5.27.0 (#334)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-02 15:29:01 +02:00
renovate[bot]
50fe5d6208
fix(deps): update dependency next to v14.2.14 (#337)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-02 15:28:44 +02:00
lukasIO
37e190bc99
use krisp noise filter hook (#346) 2024-09-30 20:40:27 +02:00
renovate[bot]
737b40b059
fix(deps): update dependency livekit-client to v2.5.5 (#343)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-27 12:34:26 -07:00
lukasIO
7964ba6778
Ensure encryption key has been set before enabling e2ee (#342) 2024-09-27 10:25:25 +02:00
lukasIO
398b172400
Surface errors via alert (#341) 2024-09-26 18:02:29 +02:00
renovate[bot]
dbab065056
fix(deps): update dependency livekit-client to v2.5.4 (#340)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-24 14:56:29 +02:00
renovate[bot]
4ff13a70c4
fix(deps): update dependency @livekit/components-react to v2.6.0 (#339)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 08:05:52 +02:00
lukasIO
bf86c02e59
Revert "feat(app): add connection details headers (#335)" (#338)
This reverts commit 8e7c8adab85ab8f98b76aeed681cd6dbea185091.
2024-09-20 17:16:42 +02:00
lukasIO
c24b42eaad
Fix height of custom page (#336) 2024-09-19 18:06:00 +02:00
Tobias Fried
8e7c8adab8
feat(app): add connection details headers (#335) 2024-09-18 22:01:26 -06:00
renovate[bot]
e9b103be4b
fix(deps): update dependency next to v14.2.12 (#333)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-18 10:59:21 +02:00
renovate[bot]
e0001e4100
fix(deps): update dependency livekit-client to v2.5.2 (#332)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-17 21:56:53 +00:00
renovate[bot]
2c752affcb
fix(deps): update dependency @livekit/components-react to v2.5.4 (#330)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-17 15:58:10 +02:00
renovate[bot]
a3b3ad5aee
fix(deps): update dependency next to v14.2.11 (#321)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-17 15:50:52 +02:00
renovate[bot]
889c5bfa89
chore(deps): update actions/checkout action to v4 (#329)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-13 17:22:11 -07:00
Jonas Schell
6fd2550f00
Add GitHub workflow for syncing main to sandbox-production (#327) 2024-09-13 12:10:20 +02:00
renovate[bot]
64bc61e2fe
fix(deps): update dependency livekit-server-sdk to v2.6.2 (#328)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-13 00:10:45 +00:00
renovate[bot]
0aa40d775c
fix(deps): update livekit dependencies (non-major) (#326)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-12 15:53:55 +00:00
Jonas Schell
aa9ce71ae8
Remove fixed positioning from footer component (#325) 2024-09-12 16:28:46 +02:00
lukasIO
dad25ad9b0
Enable lib check in TS (#324) 2024-09-12 16:21:13 +02:00
renovate[bot]
e5fca2c816
fix(deps): update dependency @livekit/components-react to v2.5.2 (#320)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-11 18:37:47 +02:00
renovate[bot]
ff50608d10
fix(deps): update dependency @datadog/browser-logs to v5.26.0 (#315)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-11 16:24:39 +02:00
renovate[bot]
4f9295c8a6
fix(deps): update livekit dependencies (non-major) (#319)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-11 16:23:20 +02:00
renovate[bot]
fccb386450
fix(deps): update dependency next to v14.2.9 (#314)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-11 16:23:10 +02:00
renovate[bot]
5bd6caaf01
fix(deps): update dependency @livekit/components-styles to v1.1.0 (#318)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-11 15:46:51 +02:00
renovate[bot]
55b029c865
fix(deps): update dependency @livekit/components-react to v2.5.0 (#317)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-11 15:38:46 +02:00
Jonas Schell
c35dea2f97
refactor: update URL construction in handlePreJoinSubmit function (#316) 2024-09-09 18:21:56 +02:00
renovate[bot]
d09ef1f163
fix(deps): update dependency @datadog/browser-logs to v5.25.0 (#313)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 10:01:56 +02:00
renovate[bot]
c95828b4ca
fix(deps): update dependency next to v14.2.7 (#307)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 10:00:06 +02:00
renovate[bot]
44d22674a4
fix(deps): update dependency @datadog/browser-logs to v5.24.0 (#310)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 09:59:42 +02:00
renovate[bot]
9f7432c93f
chore(deps): update devdependencies (non-major) (#311)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 09:59:00 +02:00
renovate[bot]
4d62818aea
fix(deps): update dependency livekit-client to v2.5.1 (#312)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 09:58:31 +02:00
lukasIO
0578ad9ea1
Avoid destroying + recreating KrispNoiseFilter when toggling setting (#309) 2024-08-29 16:10:07 +02:00
renovate[bot]
6ba2656d87
fix(deps): update dependency next to v14.2.6 (#303)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-26 23:36:19 +02:00
Jonas Schell
9f44291bc9
chore: update participant identity generation (#306)
* chore: Update participant identity generation in connection-details route

* chore: Update and cleanup dependencies
2024-08-26 17:06:09 +02:00
Jonas Schell
f39a4f4d6e
fix broken region handling (#305)
* fix broken region handling

* chore: Move getLiveKitURL function to local scope
2024-08-23 14:54:21 +02:00
Jonas Schell
f2f4ada03d
Refactor app use connection details endpoint (#302)
* update to connection-details endpoint

* chore: Update .env.example

* chore: Update .env.example

* remove fallback logic

* chore: Update connection details property names
2024-08-23 09:48:49 +02:00
Jonas Schell
15e58cd797
Migrate to Next app router (#297)
* Migrate Home Page to App Router

* Update themeColor from layout.tsx

* port room page to app router

* small changes

* port custom page to app router

* port token and url api routes

* port start stop routes

* Refactor error handling in GET function

* delete pages folder

* remove unused function

* remove deprecated field

from docs: @deprecated — will be enabled by default and removed in Next.js 15

* wrap useSearchParams in Suspense

* split up custom page into server and client component

* update imports

* simplify

* Refactor error handling in GET function

* refactor to use props for components

* Refactor video codec validation and handling

* Refactor LiveKitRoom component to handle null liveKitUrl

* refactor: improve video codec validation and handling

* add video codec typeguard

* fix isVideoCodec
2024-08-21 14:05:42 +02:00
renovate[bot]
2883de2531
Update dependency livekit-server-sdk to v2.6.1 (#299)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-15 22:50:24 -07:00
renovate[bot]
6a8f03b1ca
Update dependency @datadog/browser-logs to v5.23.3 (#296)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-14 15:12:15 +02:00
renovate[bot]
601c61f595
Update dependency @datadog/browser-logs to v5.21.0 (#251)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-14 15:04:14 +02:00
renovate[bot]
79781743ee
Update dependency livekit-client to v2.5.0 (#295)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-14 13:32:15 +02:00
lukasIO
ff15c2ee31
Add recording support (#290)
* WIP add recording support

* Add region env var

* Add recording indicator

* Indicator and support for stopping recording

* remove logs

* rename server functions
2024-08-13 11:02:28 +02:00
renovate[bot]
84c6151e4b
Update devDependencies (non-major) (#294)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-02 08:31:57 +02:00
renovate[bot]
9786e27b5f
Update dependency @livekit/components-react to v2.4.3 (#293)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-30 13:54:26 +00:00
renovate[bot]
3593948247
Update dependency livekit-client to v2.4.2 (#292)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-30 11:45:37 +02:00
renovate[bot]
98993dcf66
Update dependency livekit-client to v2.4.1 (#291)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-26 16:03:51 -07:00
renovate[bot]
9a1183fdd4
Update dependency @livekit/components-react to v2.4.1 (#288)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-26 09:57:53 +02:00
renovate[bot]
cbcdfd7878
Update dependency @livekit/krisp-noise-filter to v0.2.5 (#289)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-26 09:57:45 +02:00
renovate[bot]
49cf9ef342
Update dependency @livekit/components-react to v2.4.0 (#287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-24 12:48:41 +02:00
renovate[bot]
fa3bca7ec3
Update dependency livekit-server-sdk to v2.6.0 (#286)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-23 13:15:48 -07:00
renovate[bot]
bcd8551782
Update dependency next to v14.2.5 (#283)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-20 11:57:07 -07:00
renovate[bot]
ed045fd8e2
Update dependency livekit-client to v2.4.0 (#284)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-19 10:52:44 +02:00
renovate[bot]
47847fd160
Update dependency @livekit/components-react to v2.3.6 (#282)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-10 18:53:46 +02:00
renovate[bot]
fe02509b3d
Update dependency livekit-client to v2.3.2 (#281)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-09 12:13:54 +02:00
renovate[bot]
e3f81b81a3
Update dependency @livekit/components-react to v2.3.5 (#279)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-03 19:47:02 +00:00
renovate[bot]
33e35b54e7
Update dependency livekit-server-sdk to v2.5.1 (#278)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-01 17:04:03 +02:00
renovate[bot]
b322632191
Update dependency @livekit/components-react to v2.3.4 (#277)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-01 14:54:00 +02:00
renovate[bot]
62b3e98ebc
Update devDependencies (non-major) (#276)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-01 12:56:33 +02:00
renovate[bot]
4c9daea92e
Update dependency livekit-server-sdk to v2.5.0 (#275)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-24 00:58:34 +00:00
renovate[bot]
82886f864e
Update dependency livekit-client to v2.3.1 (#274)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-20 10:31:36 +02:00
renovate[bot]
d145d84684
Update dependency next to v14.2.4 (#252)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-17 11:42:52 +02:00
renovate[bot]
3f5a57c58c
Update react monorepo to v18.3.1 (#254)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-17 11:36:11 +02:00
renovate[bot]
a2eac63b90
Update dependency livekit-client to v2.3.0 (#273)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-17 11:27:05 +02:00
renovate[bot]
d82f2d71ad
Update dependency @livekit/components-react to v2.3.3 (#272)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-14 16:14:28 +00:00
renovate[bot]
6dffa039ad
Update dependency @livekit/krisp-noise-filter to v0.2.4 (#271)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-12 16:12:06 +02:00
lukasIO
73bd6444b8
Add audio output selection (#270) 2024-06-11 08:31:43 +02:00
renovate[bot]
16283f5bf6
Update dependency livekit-server-sdk to v2.4.0 (#269)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-07 18:28:11 +00:00
renovate[bot]
325eea53a9
Update dependency @livekit/components-react to v2.3.2 (#268)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-06 17:03:11 +00:00
renovate[bot]
8c6ad377d7
Update devDependencies (non-major) (#267)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-03 22:11:27 -07:00
renovate[bot]
d528aa4526
Update dependency livekit-client to v2.2.0 (#266)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-31 12:53:13 +02:00
renovate[bot]
4d3b365deb
Update dependency @livekit/components-react to v2.3.1 (#265)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-22 10:11:34 -07:00
renovate[bot]
560a9760b7
Update dependency @livekit/components-react to v2.3.0 (#264)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-22 03:07:43 +00:00
renovate[bot]
40436ecbd5
Update dependency livekit-client to v2.1.5 (#262)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-15 22:49:30 +02:00
renovate[bot]
c25f5b2bb3
Update dependency eslint to v9 (#261)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-13 12:28:33 +02:00
renovate[bot]
7945722304
Update dependency livekit-client to v2.1.3 (#260)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-06 16:30:29 +02:00
renovate[bot]
2ee61fbea8
Update LiveKit dependencies (non-major) (#259)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-03 09:53:38 +02:00
renovate[bot]
38b28224e9
Update dependency livekit-server-sdk to v2.3.0 (#258)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-02 13:20:33 -07:00
renovate[bot]
dfcaa6e31f
Update devDependencies (non-major) (#257)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-02 16:23:16 +02:00
renovate[bot]
f8b4b7bebd
Update dependency livekit-client to v2.1.1 (#256)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-29 18:39:55 +02:00
renovate[bot]
00bb51cf5b
Update dependency livekit-server-sdk to v2.2.0 (#255)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-26 15:06:28 +02:00
renovate[bot]
f38560ddf7
Update dependency @livekit/components-react to v2.2.0 (#253)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-25 10:15:05 +02:00
renovate[bot]
6222c02b2a
Update dependency @livekit/components-react to v2.1.3 (#250)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-23 10:01:07 +02:00
renovate[bot]
97003e594d
Update dependency @datadog/browser-logs to v5.15.0 (#240)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-22 10:39:18 +02:00
renovate[bot]
b61375ed16
Update dependency next to v14.2.2 (#244)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-22 10:38:26 +02:00
renovate[bot]
32ca1d3ce6
Update dependency @livekit/krisp-noise-filter to v0.2.2 (#249)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-22 10:27:43 +02:00
renovate[bot]
b547609032
Update dependency @livekit/components-react to v2.1.2 (#248)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-19 11:40:01 +02:00
renovate[bot]
d54006bc65
Update LiveKit dependencies (non-major) (#246)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-18 20:00:11 +00:00
lukasIO
3fa395bbe1
Surface errors in PreJoin (#245)
* Don't use noSSR wrapper for prejoin

* wrap callbacks

* use client for room pages
2024-04-17 14:42:06 +02:00
David Zhao
51a47a02d8
Update .env.example 2024-04-08 22:51:20 -07:00
renovate[bot]
02abcffb2e
Update dependency @livekit/components-react to v2.0.6 (#241)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-05 18:37:59 +00:00
renovate[bot]
b0cab8ecc9
Update devDependencies (non-major) (#239)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-02 22:32:41 -07:00
David Zhao
6861c0ac61
Update @livekit/components-js to 2.0.5 (#237) 2024-03-29 11:26:15 -07:00
renovate[bot]
7f1e0de905
Update LiveKit dependencies (non-major) (#235)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-29 10:51:19 -07:00
renovate[bot]
161367e9fb
Update dependency @datadog/browser-logs to v5.13.0 (#234)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 13:03:31 -07:00
renovate[bot]
bce59fbd45
Update dependency @livekit/krisp-noise-filter to v0.2.1 (#233)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-24 08:47:40 -07:00
renovate[bot]
3e2e7e65a2
Update dependency livekit-client to v2.0.10 (#232) 2024-03-24 11:45:24 +01:00
renovate[bot]
fe85586555
Update dependency livekit-client to v2.0.9 (#231)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-23 03:15:57 +00:00
renovate[bot]
c4576336d3
Update dependency @datadog/browser-logs to v5.12.0 (#226)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-22 17:14:41 -07:00
renovate[bot]
b180ab3b0a
Update dependency next to v14.1.4 (#228)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-21 13:14:19 +01:00
renovate[bot]
4cc264cfc0
Update dependency livekit-server-sdk to v2.1.2 (#230) 2024-03-21 10:23:36 +01:00
renovate[bot]
7d976fa4c5
Update dependency @livekit/krisp-noise-filter to ^0.2.0 (#229) 2024-03-20 17:02:22 +01:00
renovate[bot]
c0d3c9a39b
Update dependency livekit-client to v2.0.8 (#227)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-19 19:53:47 +01:00
renovate[bot]
68cc79cb09
Update dependency @livekit/krisp-noise-filter to v0.1.6 (#225)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-18 12:40:55 -07:00
renovate[bot]
9c6ae0a7a6
Update dependency livekit-server-sdk to v2.1.1 (#224)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-14 18:19:16 +01:00
lukasIO
b21bba76f6 Revert "Update dependency livekit-server-sdk to v2.1.0 (#222)"
This reverts commit 1e90d8de0651495fa90c9c4d17dbf444ab6fb454.
2024-03-14 18:03:05 +01:00
renovate[bot]
1e90d8de06
Update dependency livekit-server-sdk to v2.1.0 (#222)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-14 17:58:03 +01:00
lukasIO
35f8a27715
Add optional datadog log ingest (#213) 2024-03-14 10:24:20 +01:00
renovate[bot]
b0086eec03
Update dependency livekit-client to v2.0.7 (#221)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-14 06:50:50 +00:00
renovate[bot]
c016cb4045
Update dependency livekit-client to v2.0.6 (#220)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-12 22:43:56 +01:00
lukasIO
3cc66288bc
Fix microphone menu label (#219) 2024-03-10 16:22:08 +01:00
renovate[bot]
5b9b1e9c21
Update dependency @livekit/krisp-noise-filter to v0.1.4 (#218) 2024-03-08 06:47:35 +01:00
lukasIO
f12eabd8aa
Order renovate rules from least to most important (#217) 2024-03-07 15:15:26 +01:00
renovate[bot]
de4a7f75a3
Update dependency @livekit/components-react to v2.0.3 (#216)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-07 12:39:36 +01:00
renovate[bot]
8ffdaf55dc
Update dependency next to v14.1.3 (#208)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-07 12:39:18 +01:00
renovate[bot]
05baad6069
Update devDependencies (non-major) (#207)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-07 12:38:56 +01:00
lukasIO
0f602cd410
Update noise filter package name (#215)
* Update noise filter package name

* fix import
2024-03-06 23:01:25 +01:00
renovate[bot]
8f8880b02d
Update dependency livekit-client to v2.0.5 (#214)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-06 11:19:36 +01:00
lukasIO
1b01e60424
Add settings menu (#209)
* Add settings menu

* update livekit deps

* Update components

* Handle unsupported browsers

* make settings model configurable via env

* update env

* better check against env
2024-03-05 15:48:46 +01:00
renovate[bot]
ccf818d34b
Update dependency @livekit/components-react to v2.0.2 (#212) 2024-03-04 22:36:27 +01:00
renovate[bot]
3af553a8a3
Update LiveKit dependencies (non-major) (#211)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-04 14:39:59 +00:00
renovate[bot]
93ba673802
Update dependency livekit-client to v2.0.4 (#210)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 21:16:01 +00:00
renovate[bot]
90181b2a03
Update dependency livekit-server-sdk to v2.0.4 (#205)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-25 13:42:54 +00:00
renovate[bot]
ce5f07faa3
Update dependency livekit-client to v2.0.3 (#203)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-21 21:26:48 +01:00
lukasIO
55f5525742
Update to components v2 (#200)
* Update to components v2

* Update debug component to v2
2024-02-20 13:05:10 +01:00
renovate[bot]
8afe3de6e3
Update dependency livekit-server-sdk to v2.0.3 (#199)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-19 01:35:49 +00:00
renovate[bot]
30ff3970a2
Update dependency livekit-server-sdk to v2.0.2 (#198)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-17 06:46:12 +00:00
renovate[bot]
0917ec8477
Update dependency livekit-server-sdk to v2.0.1 (#197)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-13 01:24:31 +00:00
renovate[bot]
016aa98778
Update dependency livekit-client to v1.15.12 (#195)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-09 16:01:28 +01:00
46 changed files with 5506 additions and 2753 deletions

View File

@ -1,12 +1,30 @@
# 1. Copy this file and rename it to .env.local
# 2. Update the enviroment variables below.
# API key and secret. If you use LiveKit Cloud this can be generated via the cloud dashboard.
LIVEKIT_API_KEY=devkey
LIVEKIT_API_SECRET=secret
# REQUIRED SETTINGS
# #################
# If you are using LiveKit Cloud, the API key and secret can be generated from the Cloud Dashboard.
LIVEKIT_API_KEY=
LIVEKIT_API_SECRET=
# URL pointing to the LiveKit server. (example: `wss://my-livekit-project.livekit.cloud`)
LIVEKIT_URL=
# URL pointing to the LiveKit server.
LIVEKIT_URL=wss://my-livekit-project.livekit.cloud
## PUBLIC
NEXT_PUBLIC_LK_TOKEN_ENDPOINT=/api/token
# OPTIONAL SETTINGS
# #################
# Recording
# S3_KEY_ID=
# S3_KEY_SECRET=
# S3_ENDPOINT=
# S3_BUCKET=
# S3_REGION=
# PUBLIC
# Uncomment settings menu when using a LiveKit Cloud, it'll enable Krisp noise filters.
# NEXT_PUBLIC_SHOW_SETTINGS_MENU=true
# NEXT_PUBLIC_LK_RECORD_ENDPOINT=/api/record
# Optional, to pipe logs to datadog
# NEXT_PUBLIC_DATADOG_CLIENT_TOKEN=client-token
# NEXT_PUBLIC_DATADOG_SITE=datadog-site

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
public/background-images/*.jpg filter=lfs diff=lfs merge=lfs -text

BIN
.github/assets/template-dark.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 B

31
.github/assets/template-graphic.svg vendored Normal file
View File

@ -0,0 +1,31 @@
<svg width="270" height="151" viewBox="0 0 270 151" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="270" height="151" fill="#070707"/>
<rect x="8.5" y="8.5" width="192" height="134" rx="1.5" fill="#131313"/>
<rect x="8.5" y="8.5" width="192" height="134" rx="1.5" stroke="#1F1F1F"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.167 71.4998C101.167 69.6589 102.66 68.1665 104.501 68.1665C106.342 68.1665 107.834 69.6589 107.834 71.4998C107.834 73.3408 106.342 74.8332 104.501 74.8332C102.66 74.8332 101.167 73.3408 101.167 71.4998Z" fill="#666666"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M97.834 82.1665C97.834 78.4846 100.819 75.4998 104.501 75.4998C108.183 75.4998 111.167 78.4846 111.167 82.1665V82.8332H97.834V82.1665Z" fill="#666666"/>
<rect x="209.5" y="8.5" width="52" height="38.6667" rx="1.5" fill="#131313"/>
<rect x="209.5" y="8.5" width="52" height="38.6667" rx="1.5" stroke="#1F1F1F"/>
<g clip-path="url(#clip0_834_19648)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M232.167 23.8333C232.167 21.9924 233.66 20.5 235.501 20.5C237.342 20.5 238.834 21.9924 238.834 23.8333C238.834 25.6743 237.342 27.1667 235.501 27.1667C233.66 27.1667 232.167 25.6743 232.167 23.8333Z" fill="#666666"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M228.834 34.5C228.834 30.8181 231.819 27.8333 235.501 27.8333C239.183 27.8333 242.167 30.8181 242.167 34.5V35.1667H228.834V34.5Z" fill="#666666"/>
</g>
<rect x="209.5" y="56.1665" width="52" height="38.6667" rx="1.5" fill="#131313"/>
<rect x="209.5" y="56.1665" width="52" height="38.6667" rx="1.5" stroke="#CCCCCC"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M232.167 71.4998C232.167 69.6589 233.66 68.1665 235.501 68.1665C237.342 68.1665 238.834 69.6589 238.834 71.4998C238.834 73.3408 237.342 74.8332 235.501 74.8332C233.66 74.8332 232.167 73.3408 232.167 71.4998Z" fill="#CCCCCC"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M228.834 82.1665C228.834 78.4846 231.819 75.4998 235.501 75.4998C239.183 75.4998 242.167 78.4846 242.167 82.1665V82.8332H228.834V82.1665Z" fill="#CCCCCC"/>
<rect x="209.5" y="103.833" width="52" height="38.6667" rx="1.5" fill="#131313"/>
<rect x="209.5" y="103.833" width="52" height="38.6667" rx="1.5" stroke="#1F1F1F"/>
<g clip-path="url(#clip1_834_19648)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M232.167 119.167C232.167 117.326 233.66 115.833 235.501 115.833C237.342 115.833 238.834 117.326 238.834 119.167C238.834 121.008 237.342 122.5 235.501 122.5C233.66 122.5 232.167 121.008 232.167 119.167Z" fill="#666666"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M228.834 129.833C228.834 126.152 231.819 123.167 235.501 123.167C239.183 123.167 242.167 126.152 242.167 129.833V130.5H228.834V129.833Z" fill="#666666"/>
</g>
<defs>
<clipPath id="clip0_834_19648">
<rect width="16" height="16" fill="white" transform="translate(227.5 19.8335)"/>
</clipPath>
<clipPath id="clip1_834_19648">
<rect width="16" height="16" fill="white" transform="translate(227.5 115.167)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
.github/assets/template-light.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

View File

@ -0,0 +1,16 @@
# .github/workflows/sync-to-production.yaml
name: Sync main to sandbox-production
on:
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: livekit-examples/sandbox-deploy-action@v1
with:
production_branch: 'sandbox-production'
token: ${{ secrets.GITHUB_TOKEN }}

32
.github/workflows/test.yaml vendored Normal file
View File

@ -0,0 +1,32 @@
name: Test
on:
push:
branches: [ main ]
pull_request:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- name: Use Node.js 22
uses: actions/setup-node@v4
with:
node-version: 24
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: ESLint
run: pnpm lint
- name: Prettier
run: pnpm format:check
- name: Run Tests
run: pnpm test

View File

@ -18,7 +18,7 @@
<br>
LiveKit Meet is an open source video conferencing app built on [LiveKit Components](https://github.com/livekit/components-js), [LiveKit Cloud](https://livekit.io/cloud), and Next.js. It's been completely redesigned from the ground up using our new components library.
LiveKit Meet is an open source video conferencing app built on [LiveKit Components](https://github.com/livekit/components-js), [LiveKit Cloud](https://cloud.livekit.io/), and Next.js. It's been completely redesigned from the ground up using our new components library.
![LiveKit Meet screenshot](./.github/assets/livekit-meet.jpg)

View File

@ -0,0 +1,89 @@
import { randomString } from '@/lib/client-utils';
import { getLiveKitURL } from '@/lib/getLiveKitURL';
import { ConnectionDetails } from '@/lib/types';
import { AccessToken, AccessTokenOptions, VideoGrant } from 'livekit-server-sdk';
import { NextRequest, NextResponse } from 'next/server';
const API_KEY = process.env.LIVEKIT_API_KEY;
const API_SECRET = process.env.LIVEKIT_API_SECRET;
const LIVEKIT_URL = process.env.LIVEKIT_URL;
const COOKIE_KEY = 'random-participant-postfix';
export async function GET(request: NextRequest) {
try {
// Parse query parameters
const roomName = request.nextUrl.searchParams.get('roomName');
const participantName = request.nextUrl.searchParams.get('participantName');
const metadata = request.nextUrl.searchParams.get('metadata') ?? '';
const region = request.nextUrl.searchParams.get('region');
if (!LIVEKIT_URL) {
throw new Error('LIVEKIT_URL is not defined');
}
const livekitServerUrl = region ? getLiveKitURL(LIVEKIT_URL, region) : LIVEKIT_URL;
let randomParticipantPostfix = request.cookies.get(COOKIE_KEY)?.value;
if (livekitServerUrl === undefined) {
throw new Error('Invalid region');
}
if (typeof roomName !== 'string') {
return new NextResponse('Missing required query parameter: roomName', { status: 400 });
}
if (participantName === null) {
return new NextResponse('Missing required query parameter: participantName', { status: 400 });
}
// Generate participant token
if (!randomParticipantPostfix) {
randomParticipantPostfix = randomString(4);
}
const participantToken = await createParticipantToken(
{
identity: `${participantName}__${randomParticipantPostfix}`,
name: participantName,
metadata,
},
roomName,
);
// Return connection details
const data: ConnectionDetails = {
serverUrl: livekitServerUrl,
roomName: roomName,
participantToken: participantToken,
participantName: participantName,
};
return new NextResponse(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Set-Cookie': `${COOKIE_KEY}=${randomParticipantPostfix}; Path=/; HttpOnly; SameSite=Strict; Secure; Expires=${getCookieExpirationTime()}`,
},
});
} catch (error) {
if (error instanceof Error) {
return new NextResponse(error.message, { status: 500 });
}
}
}
function createParticipantToken(userInfo: AccessTokenOptions, roomName: string) {
const at = new AccessToken(API_KEY, API_SECRET, userInfo);
at.ttl = '5m';
const grant: VideoGrant = {
room: roomName,
roomJoin: true,
canPublish: true,
canPublishData: true,
canSubscribe: true,
};
at.addGrant(grant);
return at.toJwt();
}
function getCookieExpirationTime(): string {
var now = new Date();
var time = now.getTime();
var expireTime = time + 60 * 120 * 1000;
now.setTime(expireTime);
return now.toUTCString();
}

View File

@ -0,0 +1,70 @@
import { EgressClient, EncodedFileOutput, S3Upload } from 'livekit-server-sdk';
import { NextRequest, NextResponse } from 'next/server';
export async function GET(req: NextRequest) {
try {
const roomName = req.nextUrl.searchParams.get('roomName');
/**
* CAUTION:
* for simplicity this implementation does not authenticate users and therefore allows anyone with knowledge of a roomName
* to start/stop recordings for that room.
* DO NOT USE THIS FOR PRODUCTION PURPOSES AS IS
*/
if (roomName === null) {
return new NextResponse('Missing roomName parameter', { status: 403 });
}
const {
LIVEKIT_API_KEY,
LIVEKIT_API_SECRET,
LIVEKIT_URL,
S3_KEY_ID,
S3_KEY_SECRET,
S3_BUCKET,
S3_ENDPOINT,
S3_REGION,
} = process.env;
const hostURL = new URL(LIVEKIT_URL!);
hostURL.protocol = 'https:';
const egressClient = new EgressClient(hostURL.origin, LIVEKIT_API_KEY, LIVEKIT_API_SECRET);
const existingEgresses = await egressClient.listEgress({ roomName });
if (existingEgresses.length > 0 && existingEgresses.some((e) => e.status < 2)) {
return new NextResponse('Meeting is already being recorded', { status: 409 });
}
const fileOutput = new EncodedFileOutput({
filepath: `${new Date(Date.now()).toISOString()}-${roomName}.mp4`,
output: {
case: 's3',
value: new S3Upload({
endpoint: S3_ENDPOINT,
accessKey: S3_KEY_ID,
secret: S3_KEY_SECRET,
region: S3_REGION,
bucket: S3_BUCKET,
}),
},
});
await egressClient.startRoomCompositeEgress(
roomName,
{
file: fileOutput,
},
{
layout: 'speaker',
},
);
return new NextResponse(null, { status: 200 });
} catch (error) {
if (error instanceof Error) {
return new NextResponse(error.message, { status: 500 });
}
}
}

View File

@ -0,0 +1,39 @@
import { EgressClient } from 'livekit-server-sdk';
import { NextRequest, NextResponse } from 'next/server';
export async function GET(req: NextRequest) {
try {
const roomName = req.nextUrl.searchParams.get('roomName');
/**
* CAUTION:
* for simplicity this implementation does not authenticate users and therefore allows anyone with knowledge of a roomName
* to start/stop recordings for that room.
* DO NOT USE THIS FOR PRODUCTION PURPOSES AS IS
*/
if (roomName === null) {
return new NextResponse('Missing roomName parameter', { status: 403 });
}
const { LIVEKIT_API_KEY, LIVEKIT_API_SECRET, LIVEKIT_URL } = process.env;
const hostURL = new URL(LIVEKIT_URL!);
hostURL.protocol = 'https:';
const egressClient = new EgressClient(hostURL.origin, LIVEKIT_API_KEY, LIVEKIT_API_SECRET);
const activeEgresses = (await egressClient.listEgress({ roomName })).filter(
(info) => info.status < 2,
);
if (activeEgresses.length === 0) {
return new NextResponse('No active recording found', { status: 404 });
}
await Promise.all(activeEgresses.map((info) => egressClient.stopEgress(info.egressId)));
return new NextResponse(null, { status: 200 });
} catch (error) {
if (error instanceof Error) {
return new NextResponse(error.message, { status: 500 });
}
}
}

View File

@ -0,0 +1,98 @@
'use client';
import { formatChatMessageLinks, RoomContext, VideoConference } from '@livekit/components-react';
import {
ExternalE2EEKeyProvider,
LogLevel,
Room,
RoomConnectOptions,
RoomOptions,
VideoPresets,
type VideoCodec,
} from 'livekit-client';
import { DebugMode } from '@/lib/Debug';
import { useEffect, useMemo, useState } from 'react';
import { KeyboardShortcuts } from '@/lib/KeyboardShortcuts';
import { SettingsMenu } from '@/lib/SettingsMenu';
import { useSetupE2EE } from '@/lib/useSetupE2EE';
import { useLowCPUOptimizer } from '@/lib/usePerfomanceOptimiser';
export function VideoConferenceClientImpl(props: {
liveKitUrl: string;
token: string;
codec: VideoCodec | undefined;
singlePeerConnection: boolean | undefined;
}) {
const keyProvider = new ExternalE2EEKeyProvider();
const { worker, e2eePassphrase } = useSetupE2EE();
const e2eeEnabled = !!(e2eePassphrase && worker);
const [e2eeSetupComplete, setE2eeSetupComplete] = useState(false);
const roomOptions = useMemo((): RoomOptions => {
return {
publishDefaults: {
videoSimulcastLayers: [VideoPresets.h540, VideoPresets.h216],
red: !e2eeEnabled,
videoCodec: props.codec,
},
adaptiveStream: { pixelDensity: 'screen' },
dynacast: true,
e2ee: e2eeEnabled
? {
keyProvider,
worker,
}
: undefined,
singlePeerConnection: props.singlePeerConnection,
};
}, [e2eeEnabled, props.codec, keyProvider, worker]);
const room = useMemo(() => new Room(roomOptions), [roomOptions]);
const connectOptions = useMemo((): RoomConnectOptions => {
return {
autoSubscribe: true,
};
}, []);
useEffect(() => {
if (e2eeEnabled) {
keyProvider.setKey(e2eePassphrase).then(() => {
room.setE2EEEnabled(true).then(() => {
setE2eeSetupComplete(true);
});
});
} else {
setE2eeSetupComplete(true);
}
}, [e2eeEnabled, e2eePassphrase, keyProvider, room, setE2eeSetupComplete]);
useEffect(() => {
if (e2eeSetupComplete) {
room.connect(props.liveKitUrl, props.token, connectOptions).catch((error) => {
console.error(error);
});
room.localParticipant.enableCameraAndMicrophone().catch((error) => {
console.error(error);
});
}
}, [room, props.liveKitUrl, props.token, connectOptions, e2eeSetupComplete]);
useLowCPUOptimizer(room);
return (
<div className="lk-room-container">
<RoomContext.Provider value={room}>
<KeyboardShortcuts />
<VideoConference
chatMessageFormatter={formatChatMessageLinks}
SettingsComponent={
process.env.NEXT_PUBLIC_SHOW_SETTINGS_MENU === 'true' ? SettingsMenu : undefined
}
/>
<DebugMode logLevel={LogLevel.debug} />
</RoomContext.Provider>
</div>
);
}

34
app/custom/page.tsx Normal file
View File

@ -0,0 +1,34 @@
import { videoCodecs } from 'livekit-client';
import { VideoConferenceClientImpl } from './VideoConferenceClientImpl';
import { isVideoCodec } from '@/lib/types';
export default async function CustomRoomConnection(props: {
searchParams: Promise<{
liveKitUrl?: string;
token?: string;
codec?: string;
singlePC?: string;
}>;
}) {
const { liveKitUrl, token, codec, singlePC } = await props.searchParams;
if (typeof liveKitUrl !== 'string') {
return <h2>Missing LiveKit URL</h2>;
}
if (typeof token !== 'string') {
return <h2>Missing LiveKit token</h2>;
}
if (codec !== undefined && !isVideoCodec(codec)) {
return <h2>Invalid codec, if defined it has to be [{videoCodecs.join(', ')}].</h2>;
}
return (
<main data-lk-theme="default" style={{ height: '100%' }}>
<VideoConferenceClientImpl
liveKitUrl={liveKitUrl}
token={token}
codec={codec}
singlePeerConnection={singlePC === 'true'}
/>
</main>
);
}

60
app/layout.tsx Normal file
View File

@ -0,0 +1,60 @@
import '../styles/globals.css';
import '@livekit/components-styles';
import '@livekit/components-styles/prefabs';
import type { Metadata, Viewport } from 'next';
import { Toaster } from 'react-hot-toast';
export const metadata: Metadata = {
title: {
default: 'LiveKit Meet | Conference app build with LiveKit open source',
template: '%s',
},
description:
'LiveKit is an open source WebRTC project that gives you everything needed to build scalable and real-time audio and/or video experiences in your applications.',
twitter: {
creator: '@livekitted',
site: '@livekitted',
card: 'summary_large_image',
},
openGraph: {
url: 'https://meet.livekit.io',
images: [
{
url: 'https://meet.livekit.io/images/livekit-meet-open-graph.png',
width: 2000,
height: 1000,
type: 'image/png',
},
],
siteName: 'LiveKit Meet',
},
icons: {
icon: {
rel: 'icon',
url: '/favicon.ico',
},
apple: [
{
rel: 'apple-touch-icon',
url: '/images/livekit-apple-touch.png',
sizes: '180x180',
},
{ rel: 'mask-icon', url: '/images/livekit-safari-pinned-tab.svg', color: '#070707' },
],
},
};
export const viewport: Viewport = {
themeColor: '#070707',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body data-lk-theme="default">
<Toaster />
{children}
</body>
</html>
);
}

View File

@ -1,19 +1,18 @@
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next';
import { useRouter } from 'next/router';
import React, { ReactElement, useState } from 'react';
import { encodePassphrase, generateRoomId, randomString } from '../lib/client-utils';
'use client';
import { useRouter, useSearchParams } from 'next/navigation';
import React, { Suspense, useState } from 'react';
import { encodePassphrase, generateRoomId, randomString } from '@/lib/client-utils';
import styles from '../styles/Home.module.css';
interface TabsProps {
children: ReactElement[];
selectedIndex?: number;
onTabSelected?: (index: number) => void;
}
function Tabs(props: React.PropsWithChildren<{}>) {
const searchParams = useSearchParams();
const tabIndex = searchParams?.get('tab') === 'custom' ? 1 : 0;
function Tabs(props: TabsProps) {
const activeIndex = props.selectedIndex ?? 0;
if (!props.children) {
return <></>;
const router = useRouter();
function onTabSelected(index: number) {
const tab = index === 1 ? 'custom' : 'demo';
router.push(`/?tab=${tab}`);
}
let tabs = React.Children.map(props.children, (child, index) => {
@ -21,23 +20,28 @@ function Tabs(props: TabsProps) {
<button
className="lk-button"
onClick={() => {
if (props.onTabSelected) props.onTabSelected(index);
if (onTabSelected) {
onTabSelected(index);
}
}}
aria-pressed={activeIndex === index}
aria-pressed={tabIndex === index}
>
{/* @ts-ignore */}
{child?.props.label}
</button>
);
});
return (
<div className={styles.tabContainer}>
<div className={styles.tabSelect}>{tabs}</div>
{props.children[activeIndex]}
{/* @ts-ignore */}
{props.children[tabIndex]}
</div>
);
}
function DemoMeetingTab({ label }: { label: string }) {
function DemoMeetingTab(props: { label: string }) {
const router = useRouter();
const [e2ee, setE2ee] = useState(false);
const [sharedPassphrase, setSharedPassphrase] = useState(randomString(64));
@ -80,7 +84,7 @@ function DemoMeetingTab({ label }: { label: string }) {
);
}
function CustomConnectionTab({ label }: { label: string }) {
function CustomConnectionTab(props: { label: string }) {
const router = useRouter();
const [e2ee, setE2ee] = useState(false);
@ -156,21 +160,7 @@ function CustomConnectionTab({ label }: { label: string }) {
);
}
export const getServerSideProps: GetServerSideProps<{ tabIndex: number }> = async ({
query,
res,
}) => {
res.setHeader('Cache-Control', 'public, max-age=7200');
const tabIndex = query.tab === 'custom' ? 1 : 0;
return { props: { tabIndex } };
};
const Home = ({ tabIndex }: InferGetServerSidePropsType<typeof getServerSideProps>) => {
const router = useRouter();
function onTabSelected(index: number) {
const tab = index === 1 ? 'custom' : 'demo';
router.push({ query: { tab } });
}
export default function Page() {
return (
<>
<main className={styles.main} data-lk-theme="default">
@ -188,10 +178,12 @@ const Home = ({ tabIndex }: InferGetServerSidePropsType<typeof getServerSideProp
and Next.js.
</h2>
</div>
<Tabs selectedIndex={tabIndex} onTabSelected={onTabSelected}>
<DemoMeetingTab label="Demo" />
<CustomConnectionTab label="Custom" />
</Tabs>
<Suspense fallback="Loading">
<Tabs>
<DemoMeetingTab label="Demo" />
<CustomConnectionTab label="Custom" />
</Tabs>
</Suspense>
</main>
<footer data-lk-theme="default">
Hosted on{' '}
@ -206,6 +198,4 @@ const Home = ({ tabIndex }: InferGetServerSidePropsType<typeof getServerSideProp
</footer>
</>
);
};
export default Home;
}

View File

@ -0,0 +1,233 @@
'use client';
import React from 'react';
import { decodePassphrase } from '@/lib/client-utils';
import { DebugMode } from '@/lib/Debug';
import { KeyboardShortcuts } from '@/lib/KeyboardShortcuts';
import { RecordingIndicator } from '@/lib/RecordingIndicator';
import { SettingsMenu } from '@/lib/SettingsMenu';
import { ConnectionDetails } from '@/lib/types';
import {
formatChatMessageLinks,
LocalUserChoices,
PreJoin,
RoomContext,
VideoConference,
} from '@livekit/components-react';
import {
ExternalE2EEKeyProvider,
RoomOptions,
VideoCodec,
VideoPresets,
Room,
DeviceUnsupportedError,
RoomConnectOptions,
RoomEvent,
TrackPublishDefaults,
VideoCaptureOptions,
} from 'livekit-client';
import { useRouter } from 'next/navigation';
import { useSetupE2EE } from '@/lib/useSetupE2EE';
import { useLowCPUOptimizer } from '@/lib/usePerfomanceOptimiser';
const CONN_DETAILS_ENDPOINT =
process.env.NEXT_PUBLIC_CONN_DETAILS_ENDPOINT ?? '/api/connection-details';
const SHOW_SETTINGS_MENU = process.env.NEXT_PUBLIC_SHOW_SETTINGS_MENU == 'true';
export function PageClientImpl(props: {
roomName: string;
region?: string;
hq: boolean;
codec: VideoCodec;
}) {
const [preJoinChoices, setPreJoinChoices] = React.useState<LocalUserChoices | undefined>(
undefined,
);
const preJoinDefaults = React.useMemo(() => {
return {
username: '',
videoEnabled: true,
audioEnabled: true,
};
}, []);
const [connectionDetails, setConnectionDetails] = React.useState<ConnectionDetails | undefined>(
undefined,
);
const handlePreJoinSubmit = React.useCallback(async (values: LocalUserChoices) => {
setPreJoinChoices(values);
const url = new URL(CONN_DETAILS_ENDPOINT, window.location.origin);
url.searchParams.append('roomName', props.roomName);
url.searchParams.append('participantName', values.username);
if (props.region) {
url.searchParams.append('region', props.region);
}
const connectionDetailsResp = await fetch(url.toString());
const connectionDetailsData = await connectionDetailsResp.json();
setConnectionDetails(connectionDetailsData);
}, []);
const handlePreJoinError = React.useCallback((e: any) => console.error(e), []);
return (
<main data-lk-theme="default" style={{ height: '100%' }}>
{connectionDetails === undefined || preJoinChoices === undefined ? (
<div style={{ display: 'grid', placeItems: 'center', height: '100%' }}>
<PreJoin
defaults={preJoinDefaults}
onSubmit={handlePreJoinSubmit}
onError={handlePreJoinError}
/>
</div>
) : (
<VideoConferenceComponent
connectionDetails={connectionDetails}
userChoices={preJoinChoices}
options={{ codec: props.codec, hq: props.hq }}
/>
)}
</main>
);
}
function VideoConferenceComponent(props: {
userChoices: LocalUserChoices;
connectionDetails: ConnectionDetails;
options: {
hq: boolean;
codec: VideoCodec;
};
}) {
const keyProvider = new ExternalE2EEKeyProvider();
const { worker, e2eePassphrase } = useSetupE2EE();
const e2eeEnabled = !!(e2eePassphrase && worker);
const [e2eeSetupComplete, setE2eeSetupComplete] = React.useState(false);
const roomOptions = React.useMemo((): RoomOptions => {
let videoCodec: VideoCodec | undefined = props.options.codec ? props.options.codec : 'vp9';
if (e2eeEnabled && (videoCodec === 'av1' || videoCodec === 'vp9')) {
videoCodec = undefined;
}
const videoCaptureDefaults: VideoCaptureOptions = {
deviceId: props.userChoices.videoDeviceId ?? undefined,
resolution: props.options.hq ? VideoPresets.h2160 : VideoPresets.h720,
};
const publishDefaults: TrackPublishDefaults = {
dtx: false,
videoSimulcastLayers: props.options.hq
? [VideoPresets.h1080, VideoPresets.h720]
: [VideoPresets.h540, VideoPresets.h216],
red: !e2eeEnabled,
videoCodec,
};
return {
videoCaptureDefaults: videoCaptureDefaults,
publishDefaults: publishDefaults,
audioCaptureDefaults: {
deviceId: props.userChoices.audioDeviceId ?? undefined,
},
adaptiveStream: true,
dynacast: true,
e2ee: keyProvider && worker && e2eeEnabled ? { keyProvider, worker } : undefined,
singlePeerConnection: true,
};
}, [props.userChoices, props.options.hq, props.options.codec]);
const room = React.useMemo(() => new Room(roomOptions), []);
React.useEffect(() => {
if (e2eeEnabled) {
keyProvider
.setKey(decodePassphrase(e2eePassphrase))
.then(() => {
room.setE2EEEnabled(true).catch((e) => {
if (e instanceof DeviceUnsupportedError) {
alert(
`You're trying to join an encrypted meeting, but your browser does not support it. Please update it to the latest version and try again.`,
);
console.error(e);
} else {
throw e;
}
});
})
.then(() => setE2eeSetupComplete(true));
} else {
setE2eeSetupComplete(true);
}
}, [e2eeEnabled, room, e2eePassphrase]);
const connectOptions = React.useMemo((): RoomConnectOptions => {
return {
autoSubscribe: true,
};
}, []);
React.useEffect(() => {
room.on(RoomEvent.Disconnected, handleOnLeave);
room.on(RoomEvent.EncryptionError, handleEncryptionError);
room.on(RoomEvent.MediaDevicesError, handleError);
if (e2eeSetupComplete) {
room
.connect(
props.connectionDetails.serverUrl,
props.connectionDetails.participantToken,
connectOptions,
)
.catch((error) => {
handleError(error);
});
if (props.userChoices.videoEnabled) {
room.localParticipant.setCameraEnabled(true).catch((error) => {
handleError(error);
});
}
if (props.userChoices.audioEnabled) {
room.localParticipant.setMicrophoneEnabled(true).catch((error) => {
handleError(error);
});
}
}
return () => {
room.off(RoomEvent.Disconnected, handleOnLeave);
room.off(RoomEvent.EncryptionError, handleEncryptionError);
room.off(RoomEvent.MediaDevicesError, handleError);
};
}, [e2eeSetupComplete, room, props.connectionDetails, props.userChoices]);
const lowPowerMode = useLowCPUOptimizer(room);
const router = useRouter();
const handleOnLeave = React.useCallback(() => router.push('/'), [router]);
const handleError = React.useCallback((error: Error) => {
console.error(error);
alert(`Encountered an unexpected error, check the console logs for details: ${error.message}`);
}, []);
const handleEncryptionError = React.useCallback((error: Error) => {
console.error(error);
alert(
`Encountered an unexpected encryption error, check the console logs for details: ${error.message}`,
);
}, []);
React.useEffect(() => {
if (lowPowerMode) {
console.warn('Low power mode enabled');
}
}, [lowPowerMode]);
return (
<div className="lk-room-container">
<RoomContext.Provider value={room}>
<KeyboardShortcuts />
<VideoConference
chatMessageFormatter={formatChatMessageLinks}
SettingsComponent={SHOW_SETTINGS_MENU ? SettingsMenu : undefined}
/>
<DebugMode />
<RecordingIndicator />
</RoomContext.Provider>
</div>
);
}

View File

@ -0,0 +1,33 @@
import * as React from 'react';
import { PageClientImpl } from './PageClientImpl';
import { isVideoCodec } from '@/lib/types';
export default async function Page({
params,
searchParams,
}: {
params: Promise<{ roomName: string }>;
searchParams: Promise<{
// FIXME: We should not allow values for regions if in playground mode.
region?: string;
hq?: string;
codec?: string;
}>;
}) {
const _params = await params;
const _searchParams = await searchParams;
const codec =
typeof _searchParams.codec === 'string' && isVideoCodec(_searchParams.codec)
? _searchParams.codec
: 'vp9';
const hq = _searchParams.hq === 'true' ? true : false;
return (
<PageClientImpl
roomName={_params.roomName}
region={_searchParams.region}
hq={hq}
codec={codec}
/>
);
}

175
lib/CameraSettings.tsx Normal file
View File

@ -0,0 +1,175 @@
import React from 'react';
import {
MediaDeviceMenu,
TrackReference,
TrackToggle,
useLocalParticipant,
VideoTrack,
} from '@livekit/components-react';
import { BackgroundBlur, VirtualBackground } from '@livekit/track-processors';
import { isLocalTrack, LocalTrackPublication, Track } from 'livekit-client';
import Desk from '../public/background-images/samantha-gades-BlIhVfXbi9s-unsplash.jpg';
import Nature from '../public/background-images/ali-kazal-tbw_KQE3Cbg-unsplash.jpg';
// Background image paths
const BACKGROUND_IMAGES = [
{ name: 'Desk', path: Desk },
{ name: 'Nature', path: Nature },
];
// Background options
type BackgroundType = 'none' | 'blur' | 'image';
export function CameraSettings() {
const { cameraTrack, localParticipant } = useLocalParticipant();
const [backgroundType, setBackgroundType] = React.useState<BackgroundType>(
(cameraTrack as LocalTrackPublication)?.track?.getProcessor()?.name === 'background-blur'
? 'blur'
: (cameraTrack as LocalTrackPublication)?.track?.getProcessor()?.name === 'virtual-background'
? 'image'
: 'none',
);
const [virtualBackgroundImagePath, setVirtualBackgroundImagePath] = React.useState<string | null>(
null,
);
const camTrackRef: TrackReference | undefined = React.useMemo(() => {
return cameraTrack
? { participant: localParticipant, publication: cameraTrack, source: Track.Source.Camera }
: undefined;
}, [localParticipant, cameraTrack]);
const selectBackground = (type: BackgroundType, imagePath?: string) => {
setBackgroundType(type);
if (type === 'image' && imagePath) {
setVirtualBackgroundImagePath(imagePath);
} else if (type !== 'image') {
setVirtualBackgroundImagePath(null);
}
};
React.useEffect(() => {
if (isLocalTrack(cameraTrack?.track)) {
if (backgroundType === 'blur') {
cameraTrack.track?.setProcessor(BackgroundBlur());
} else if (backgroundType === 'image' && virtualBackgroundImagePath) {
cameraTrack.track?.setProcessor(VirtualBackground(virtualBackgroundImagePath));
} else {
cameraTrack.track?.stopProcessor();
}
}
}, [cameraTrack, backgroundType, virtualBackgroundImagePath]);
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
{camTrackRef && (
<VideoTrack
style={{
maxHeight: '280px',
objectFit: 'contain',
objectPosition: 'right',
transform: 'scaleX(-1)',
}}
trackRef={camTrackRef}
/>
)}
<section className="lk-button-group">
<TrackToggle source={Track.Source.Camera}>Camera</TrackToggle>
<div className="lk-button-group-menu">
<MediaDeviceMenu kind="videoinput" />
</div>
</section>
<div style={{ marginTop: '10px' }}>
<div style={{ marginBottom: '8px' }}>Background Effects</div>
<div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
<button
onClick={() => selectBackground('none')}
className="lk-button"
aria-pressed={backgroundType === 'none'}
style={{
border: backgroundType === 'none' ? '2px solid #0090ff' : '1px solid #d1d1d1',
minWidth: '80px',
}}
>
None
</button>
<button
onClick={() => selectBackground('blur')}
className="lk-button"
aria-pressed={backgroundType === 'blur'}
style={{
border: backgroundType === 'blur' ? '2px solid #0090ff' : '1px solid #d1d1d1',
minWidth: '80px',
backgroundColor: '#f0f0f0',
position: 'relative',
overflow: 'hidden',
height: '60px',
}}
>
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: '#e0e0e0',
filter: 'blur(8px)',
zIndex: 0,
}}
/>
<span
style={{
position: 'relative',
zIndex: 1,
backgroundColor: 'rgba(0,0,0,0.6)',
padding: '2px 5px',
borderRadius: '4px',
fontSize: '12px',
}}
>
Blur
</span>
</button>
{BACKGROUND_IMAGES.map((image) => (
<button
key={image.path.src}
onClick={() => selectBackground('image', image.path.src)}
className="lk-button"
aria-pressed={
backgroundType === 'image' && virtualBackgroundImagePath === image.path.src
}
style={{
backgroundImage: `url(${image.path.src})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
width: '80px',
height: '60px',
border:
backgroundType === 'image' && virtualBackgroundImagePath === image.path.src
? '2px solid #0090ff'
: '1px solid #d1d1d1',
}}
>
<span
style={{
backgroundColor: 'rgba(0,0,0,0.6)',
padding: '2px 5px',
borderRadius: '4px',
fontSize: '12px',
}}
>
{image.name}
</span>
</button>
))}
</div>
</div>
</div>
);
}

View File

@ -1,7 +1,10 @@
import * as React from 'react';
import { useRoomContext } from '@livekit/components-react';
import { setLogLevel, LogLevel, RemoteTrackPublication } from 'livekit-client';
import { setLogLevel, LogLevel, RemoteTrackPublication, setLogExtension } from 'livekit-client';
// @ts-ignore
import { tinykeys } from 'tinykeys';
import { datadogLogs } from '@datadog/browser-logs';
import styles from '../styles/Debug.module.css';
export const useDebugMode = ({ logLevel }: { logLevel?: LogLevel }) => {
@ -9,6 +12,36 @@ export const useDebugMode = ({ logLevel }: { logLevel?: LogLevel }) => {
React.useEffect(() => {
setLogLevel(logLevel ?? 'debug');
if (process.env.NEXT_PUBLIC_DATADOG_CLIENT_TOKEN && process.env.NEXT_PUBLIC_DATADOG_SITE) {
console.log('setting up datadog logs');
datadogLogs.init({
clientToken: process.env.NEXT_PUBLIC_DATADOG_CLIENT_TOKEN,
site: process.env.NEXT_PUBLIC_DATADOG_SITE,
forwardErrorsToLogs: true,
sessionSampleRate: 100,
});
setLogExtension((level, msg, context) => {
switch (level) {
case LogLevel.debug:
datadogLogs.logger.debug(msg, context);
break;
case LogLevel.info:
datadogLogs.logger.info(msg, context);
break;
case LogLevel.warn:
datadogLogs.logger.warn(msg, context);
break;
case LogLevel.error:
datadogLogs.logger.error(msg, context);
break;
default:
break;
}
});
}
// @ts-expect-error
window.__lk_room = room;
@ -23,6 +56,11 @@ export const DebugMode = ({ logLevel }: { logLevel?: LogLevel }) => {
const room = useRoomContext();
const [isOpen, setIsOpen] = React.useState(false);
const [, setRender] = React.useState({});
const [roomSid, setRoomSid] = React.useState('');
React.useEffect(() => {
room.getSid().then(setRoomSid);
}, [room]);
useDebugMode({ logLevel });
@ -78,7 +116,7 @@ export const DebugMode = ({ logLevel }: { logLevel?: LogLevel }) => {
<div className={styles.overlay}>
<section id="room-info">
<h3>
Room Info {room.name}: {room.sid}
Room Info {room.name}: {roomSid}
</h3>
</section>
<details open>
@ -90,7 +128,7 @@ export const DebugMode = ({ logLevel }: { logLevel?: LogLevel }) => {
<b>Published tracks</b>
</summary>
<div>
{Array.from(lp.tracks.values()).map((t) => (
{Array.from(lp.trackPublications.values()).map((t) => (
<>
<div>
<i>
@ -151,7 +189,7 @@ export const DebugMode = ({ logLevel }: { logLevel?: LogLevel }) => {
<summary>
<b>Remote Participants</b>
</summary>
{Array.from(room.participants.values()).map((p) => (
{Array.from(room.remoteParticipants.values()).map((p) => (
<details key={p.sid} className={styles.detailsSection}>
<summary>
<b>
@ -160,7 +198,7 @@ export const DebugMode = ({ logLevel }: { logLevel?: LogLevel }) => {
</b>
</summary>
<div>
{Array.from(p.tracks.values()).map((t) => (
{Array.from(p.trackPublications.values()).map((t) => (
<>
<div>
<i>

31
lib/KeyboardShortcuts.tsx Normal file
View File

@ -0,0 +1,31 @@
'use client';
import React from 'react';
import { Track } from 'livekit-client';
import { useTrackToggle } from '@livekit/components-react';
export function KeyboardShortcuts() {
const { toggle: toggleMic } = useTrackToggle({ source: Track.Source.Microphone });
const { toggle: toggleCamera } = useTrackToggle({ source: Track.Source.Camera });
React.useEffect(() => {
function handleShortcut(event: KeyboardEvent) {
// Toggle microphone: Cmd/Ctrl-Shift-A
if (toggleMic && event.key === 'A' && (event.ctrlKey || event.metaKey)) {
event.preventDefault();
toggleMic();
}
// Toggle camera: Cmd/Ctrl-Shift-V
if (event.key === 'V' && (event.ctrlKey || event.metaKey)) {
event.preventDefault();
toggleCamera();
}
}
window.addEventListener('keydown', handleShortcut);
return () => window.removeEventListener('keydown', handleShortcut);
}, [toggleMic, toggleCamera]);
return null;
}

View File

@ -0,0 +1,55 @@
import React from 'react';
import { useKrispNoiseFilter } from '@livekit/components-react/krisp';
import { TrackToggle } from '@livekit/components-react';
import { MediaDeviceMenu } from '@livekit/components-react';
import { Track } from 'livekit-client';
import { isLowPowerDevice } from './client-utils';
export function MicrophoneSettings() {
const { isNoiseFilterEnabled, setNoiseFilterEnabled, isNoiseFilterPending } = useKrispNoiseFilter(
{
filterOptions: {
bufferOverflowMs: 100,
bufferDropMs: 200,
quality: isLowPowerDevice() ? 'low' : 'medium',
onBufferDrop: () => {
console.warn(
'krisp buffer dropped, noise filter versions >= 0.3.2 will automatically disable the filter',
);
},
},
},
);
React.useEffect(() => {
// enable Krisp by default on non-low power devices
setNoiseFilterEnabled(!isLowPowerDevice());
}, []);
return (
<div
style={{
display: 'flex',
flexDirection: 'row',
gap: '10px',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
<section className="lk-button-group">
<TrackToggle source={Track.Source.Microphone}>Microphone</TrackToggle>
<div className="lk-button-group-menu">
<MediaDeviceMenu kind="audioinput" />
</div>
</section>
<button
className="lk-button"
onClick={() => setNoiseFilterEnabled(!isNoiseFilterEnabled)}
disabled={isNoiseFilterPending}
aria-pressed={isNoiseFilterEnabled}
>
{isNoiseFilterEnabled ? 'Disable' : 'Enable'} Enhanced Noise Cancellation
</button>
</div>
);
}

View File

@ -0,0 +1,40 @@
import { useIsRecording } from '@livekit/components-react';
import * as React from 'react';
import toast from 'react-hot-toast';
export function RecordingIndicator() {
const isRecording = useIsRecording();
const [wasRecording, setWasRecording] = React.useState(false);
React.useEffect(() => {
if (isRecording !== wasRecording) {
setWasRecording(isRecording);
if (isRecording) {
toast('This meeting is being recorded', {
duration: 3000,
icon: '🎥',
position: 'top-center',
className: 'lk-button',
style: {
backgroundColor: 'var(--lk-danger3)',
color: 'var(--lk-fg)',
},
});
}
}
}, [isRecording]);
return (
<div
style={{
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
boxShadow: isRecording ? 'var(--lk-danger3) 0px 0px 0px 3px inset' : 'none',
pointerEvents: 'none',
}}
></div>
);
}

154
lib/SettingsMenu.tsx Normal file
View File

@ -0,0 +1,154 @@
'use client';
import * as React from 'react';
import { Track } from 'livekit-client';
import {
useMaybeLayoutContext,
MediaDeviceMenu,
TrackToggle,
useRoomContext,
useIsRecording,
} from '@livekit/components-react';
import styles from '../styles/SettingsMenu.module.css';
import { CameraSettings } from './CameraSettings';
import { MicrophoneSettings } from './MicrophoneSettings';
/**
* @alpha
*/
export interface SettingsMenuProps extends React.HTMLAttributes<HTMLDivElement> {}
/**
* @alpha
*/
export function SettingsMenu(props: SettingsMenuProps) {
const layoutContext = useMaybeLayoutContext();
const room = useRoomContext();
const recordingEndpoint = process.env.NEXT_PUBLIC_LK_RECORD_ENDPOINT;
const settings = React.useMemo(() => {
return {
media: { camera: true, microphone: true, label: 'Media Devices', speaker: true },
recording: recordingEndpoint ? { label: 'Recording' } : undefined,
};
}, []);
const tabs = React.useMemo(
() => Object.keys(settings).filter((t) => t !== undefined) as Array<keyof typeof settings>,
[settings],
);
const [activeTab, setActiveTab] = React.useState(tabs[0]);
const isRecording = useIsRecording();
const [initialRecStatus, setInitialRecStatus] = React.useState(isRecording);
const [processingRecRequest, setProcessingRecRequest] = React.useState(false);
React.useEffect(() => {
if (initialRecStatus !== isRecording) {
setProcessingRecRequest(false);
}
}, [isRecording, initialRecStatus]);
const toggleRoomRecording = async () => {
if (!recordingEndpoint) {
throw TypeError('No recording endpoint specified');
}
if (room.isE2EEEnabled) {
throw Error('Recording of encrypted meetings is currently not supported');
}
setProcessingRecRequest(true);
setInitialRecStatus(isRecording);
let response: Response;
if (isRecording) {
response = await fetch(recordingEndpoint + `/stop?roomName=${room.name}`);
} else {
response = await fetch(recordingEndpoint + `/start?roomName=${room.name}`);
}
if (response.ok) {
} else {
console.error(
'Error handling recording request, check server logs:',
response.status,
response.statusText,
);
setProcessingRecRequest(false);
}
};
return (
<div className="settings-menu" style={{ width: '100%', position: 'relative' }} {...props}>
<div className={styles.tabs}>
{tabs.map(
(tab) =>
settings[tab] && (
<button
className={`${styles.tab} lk-button`}
key={tab}
onClick={() => setActiveTab(tab)}
aria-pressed={tab === activeTab}
>
{
// @ts-ignore
settings[tab].label
}
</button>
),
)}
</div>
<div className="tab-content">
{activeTab === 'media' && (
<>
{settings.media && settings.media.camera && (
<>
<h3>Camera</h3>
<section>
<CameraSettings />
</section>
</>
)}
{settings.media && settings.media.microphone && (
<>
<h3>Microphone</h3>
<section>
<MicrophoneSettings />
</section>
</>
)}
{settings.media && settings.media.speaker && (
<>
<h3>Speaker & Headphones</h3>
<section className="lk-button-group">
<span className="lk-button">Audio Output</span>
<div className="lk-button-group-menu">
<MediaDeviceMenu kind="audiooutput"></MediaDeviceMenu>
</div>
</section>
</>
)}
</>
)}
{activeTab === 'recording' && (
<>
<h3>Record Meeting</h3>
<section>
<p>
{isRecording
? 'Meeting is currently being recorded'
: 'No active recordings for this meeting'}
</p>
<button disabled={processingRecRequest} onClick={() => toggleRoomRecording()}>
{isRecording ? 'Stop' : 'Start'} Recording
</button>
</section>
</>
)}
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', width: '100%' }}>
<button
className={`lk-button`}
onClick={() => layoutContext?.widget.dispatch?.({ msg: 'toggle_settings' })}
>
Close
</button>
</div>
</div>
);
}

View File

@ -1,25 +1,3 @@
import { useEffect, useState } from 'react';
export function useServerUrl(region?: string) {
const [serverUrl, setServerUrl] = useState<string | undefined>();
useEffect(() => {
let endpoint = `/api/url`;
if (region) {
endpoint += `?region=${region}`;
}
fetch(endpoint).then(async (res) => {
if (res.ok) {
const body = await res.json();
console.log(body);
setServerUrl(body.url);
} else {
throw Error('Error fetching server url, check server logs');
}
});
});
return serverUrl;
}
export function encodePassphrase(passphrase: string) {
return encodeURIComponent(passphrase);
}
@ -41,3 +19,11 @@ export function randomString(length: number): string {
}
return result;
}
export function isLowPowerDevice() {
return navigator.hardwareConcurrency < 6;
}
export function isMeetStaging() {
return new URL(location.origin).host === 'meet.staging.livekit.io';
}

35
lib/getLiveKitURL.test.ts Normal file
View File

@ -0,0 +1,35 @@
import { describe, it, expect } from 'vitest';
import { getLiveKitURL } from './getLiveKitURL';
describe('getLiveKitURL', () => {
it('returns the original URL if no region is provided', () => {
const url = 'https://myproject.livekit.cloud';
expect(getLiveKitURL(url, null)).toBe(url + '/');
});
it('inserts the region into livekit.cloud URLs', () => {
const url = 'https://myproject.livekit.cloud';
const region = 'eu';
expect(getLiveKitURL(url, region)).toBe('https://myproject.eu.production.livekit.cloud/');
});
it('inserts the region into livekit.cloud URLs and preserves the staging environment', () => {
const url = 'https://myproject.staging.livekit.cloud';
const region = 'eu';
expect(getLiveKitURL(url, region)).toBe('https://myproject.eu.staging.livekit.cloud/');
});
it('returns the original URL for non-livekit.cloud hosts, even with region', () => {
const url = 'https://example.com';
const region = 'us';
expect(getLiveKitURL(url, region)).toBe(url + '/');
});
it('handles URLs with paths and query params', () => {
const url = 'https://myproject.livekit.cloud/room?foo=bar';
const region = 'ap';
expect(getLiveKitURL(url, region)).toBe(
'https://myproject.ap.production.livekit.cloud/room?foo=bar',
);
});
});

12
lib/getLiveKitURL.ts Normal file
View File

@ -0,0 +1,12 @@
export function getLiveKitURL(projectUrl: string, region: string | null): string {
const url = new URL(projectUrl);
if (region && url.hostname.includes('livekit.cloud')) {
let [projectId, ...hostParts] = url.hostname.split('.');
if (hostParts[0] !== 'staging') {
hostParts = ['production', ...hostParts];
}
const regionURL = [projectId, region, ...hostParts].join('.');
url.hostname = regionURL;
}
return url.toString();
}

View File

@ -1,27 +0,0 @@
import { RoomServiceClient } from 'livekit-server-sdk';
export function getRoomClient(): RoomServiceClient {
checkKeys();
return new RoomServiceClient(getLiveKitURL());
}
export function getLiveKitURL(region?: string | string[]): string {
let targetKey = 'LIVEKIT_URL';
if (region && !Array.isArray(region)) {
targetKey = `LIVEKIT_URL_${region}`.toUpperCase();
}
const url = process.env[targetKey];
if (!url) {
throw new Error(`${targetKey} is not defined`);
}
return url;
}
function checkKeys() {
if (typeof process.env.LIVEKIT_API_KEY === 'undefined') {
throw new Error('LIVEKIT_API_KEY is not defined');
}
if (typeof process.env.LIVEKIT_API_SECRET === 'undefined') {
throw new Error('LIVEKIT_API_SECRET is not defined');
}
}

View File

@ -1,4 +1,5 @@
import { LocalAudioTrack, LocalVideoTrack } from 'livekit-client';
import { LocalAudioTrack, LocalVideoTrack, videoCodecs } from 'livekit-client';
import { VideoCodec } from 'livekit-client';
export interface SessionProps {
roomName: string;
@ -14,3 +15,14 @@ export interface TokenResult {
identity: string;
accessToken: string;
}
export function isVideoCodec(codec: string): codec is VideoCodec {
return videoCodecs.includes(codec as VideoCodec);
}
export type ConnectionDetails = {
serverUrl: string;
roomName: string;
participantName: string;
participantToken: string;
};

View File

@ -0,0 +1,71 @@
import {
Room,
ParticipantEvent,
RoomEvent,
RemoteTrack,
RemoteTrackPublication,
VideoQuality,
LocalVideoTrack,
isVideoTrack,
} from 'livekit-client';
import * as React from 'react';
export type LowCPUOptimizerOptions = {
reducePublisherVideoQuality: boolean;
reduceSubscriberVideoQuality: boolean;
disableVideoProcessing: boolean;
};
const defaultOptions: LowCPUOptimizerOptions = {
reducePublisherVideoQuality: true,
reduceSubscriberVideoQuality: true,
disableVideoProcessing: false,
} as const;
/**
* This hook ensures that on devices with low CPU, the performance is optimised when needed.
* This is done by primarily reducing the video quality to low when the CPU is constrained.
*/
export function useLowCPUOptimizer(room: Room, options: Partial<LowCPUOptimizerOptions> = {}) {
const [lowPowerMode, setLowPowerMode] = React.useState(false);
const opts = React.useMemo(() => ({ ...defaultOptions, ...options }), [options]);
React.useEffect(() => {
const handleCpuConstrained = async (track: LocalVideoTrack) => {
setLowPowerMode(true);
console.warn('Local track CPU constrained', track);
if (opts.reducePublisherVideoQuality) {
track.prioritizePerformance();
}
if (opts.disableVideoProcessing && isVideoTrack(track)) {
track.stopProcessor();
}
if (opts.reduceSubscriberVideoQuality) {
room.remoteParticipants.forEach((participant) => {
participant.videoTrackPublications.forEach((publication) => {
publication.setVideoQuality(VideoQuality.LOW);
});
});
}
};
room.localParticipant.on(ParticipantEvent.LocalTrackCpuConstrained, handleCpuConstrained);
return () => {
room.localParticipant.off(ParticipantEvent.LocalTrackCpuConstrained, handleCpuConstrained);
};
}, [room, opts.reducePublisherVideoQuality, opts.reduceSubscriberVideoQuality]);
React.useEffect(() => {
const lowerQuality = (_: RemoteTrack, publication: RemoteTrackPublication) => {
publication.setVideoQuality(VideoQuality.LOW);
};
if (lowPowerMode && opts.reduceSubscriberVideoQuality) {
room.on(RoomEvent.TrackSubscribed, lowerQuality);
}
return () => {
room.off(RoomEvent.TrackSubscribed, lowerQuality);
};
}, [lowPowerMode, room, opts.reduceSubscriberVideoQuality]);
return lowPowerMode;
}

15
lib/useSetupE2EE.ts Normal file
View File

@ -0,0 +1,15 @@
import React from 'react';
import { ExternalE2EEKeyProvider } from 'livekit-client';
import { decodePassphrase } from './client-utils';
export function useSetupE2EE() {
const e2eePassphrase =
typeof window !== 'undefined' ? decodePassphrase(location.hash.substring(1)) : undefined;
const worker: Worker | undefined =
typeof window !== 'undefined' && e2eePassphrase
? new Worker(new URL('livekit-client/e2ee-worker', import.meta.url))
: undefined;
return { worker, e2eePassphrase };
}

2
next-env.d.ts vendored
View File

@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View File

@ -1,19 +1,36 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: false,
swcMinify: false,
productionBrowserSourceMaps: true,
images: {
formats: ['image/webp'],
},
webpack: (config, { buildId, dev, isServer, defaultLoaders, nextRuntime, webpack }) => {
// Important: return the modified config
config.module.rules = [
...config.module.rules,
config.module.rules.push({
test: /\.mjs$/,
enforce: 'pre',
use: ['source-map-loader'],
});
return config;
},
headers: async () => {
return [
{
test: /\.mjs$/,
enforce: 'pre',
use: ['source-map-loader'],
source: '/(.*)',
headers: [
{
key: 'Cross-Origin-Opener-Policy',
value: 'same-origin',
},
{
key: 'Cross-Origin-Embedder-Policy',
value: 'credentialless',
},
],
},
];
return config;
},
};

View File

@ -6,29 +6,39 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"lint:fix": "next lint --fix",
"test": "vitest run",
"format:check": "prettier --check \"**/*.{ts,tsx,md,json}\"",
"format:write": "prettier --write \"**/*.{ts,tsx,md,json}\""
},
"dependencies": {
"@livekit/components-react": "1.5.3",
"@livekit/components-styles": "1.0.9",
"livekit-client": "1.15.11",
"livekit-server-sdk": "2.0.0",
"next": "14.1.0",
"next-seo": "^6.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"tinykeys": "^2.1.0"
"@datadog/browser-logs": "^5.23.3",
"@livekit/components-react": "2.9.19",
"@livekit/components-styles": "1.2.0",
"@livekit/krisp-noise-filter": "0.4.1",
"@livekit/track-processors": "^0.7.0",
"livekit-client": "2.17.2",
"livekit-server-sdk": "2.15.0",
"next": "15.2.8",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-hot-toast": "^2.5.2",
"tinykeys": "^3.0.0"
},
"devDependencies": {
"@types/node": "20.11.14",
"@types/react": "18.2.49",
"@types/react-dom": "18.2.18",
"eslint": "8.56.0",
"eslint-config-next": "14.1.0",
"@types/node": "24.10.13",
"@types/react": "18.3.27",
"@types/react-dom": "18.3.7",
"eslint": "9.39.1",
"eslint-config-next": "15.5.6",
"prettier": "3.7.3",
"source-map-loader": "^5.0.0",
"typescript": "5.3.3"
"typescript": "5.9.3",
"vitest": "^3.2.4"
},
"engines": {
"node": ">=18"
}
},
"packageManager": "pnpm@10.18.2"
}

View File

@ -1,60 +0,0 @@
import '../styles/globals.css';
import type { AppProps } from 'next/app';
import '@livekit/components-styles';
import '@livekit/components-styles/prefabs';
import { DefaultSeo } from 'next-seo';
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<DefaultSeo
title="LiveKit Meet | Conference app build with LiveKit Open Source"
titleTemplate="%s"
defaultTitle="LiveKit Meet | Conference app build with LiveKit open source"
description="LiveKit is an open source WebRTC project that gives you everything needed to build scalable and real-time audio and/or video experiences in your applications."
twitter={{
handle: '@livekitted',
site: '@livekitted',
cardType: 'summary_large_image',
}}
openGraph={{
url: 'https://meet.livekit.io',
images: [
{
url: 'https://meet.livekit.io/images/livekit-meet-open-graph.png',
width: 2000,
height: 1000,
type: 'image/png',
},
],
site_name: 'LiveKit Meet',
}}
additionalMetaTags={[
{
property: 'theme-color',
content: '#070707',
},
]}
additionalLinkTags={[
{
rel: 'icon',
href: '/favicon.ico',
},
{
rel: 'apple-touch-icon',
href: '/images/livekit-apple-touch.png',
sizes: '180x180',
},
{
rel: 'mask-icon',
href: '/images/livekit-safari-pinned-tab.svg',
color: '#070707',
},
]}
/>
<Component {...pageProps} />
</>
);
}
export default MyApp;

View File

@ -1,67 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { AccessToken } from 'livekit-server-sdk';
import type { AccessTokenOptions, VideoGrant } from 'livekit-server-sdk';
import { TokenResult } from '../../lib/types';
const apiKey = process.env.LIVEKIT_API_KEY;
const apiSecret = process.env.LIVEKIT_API_SECRET;
const createToken = (userInfo: AccessTokenOptions, grant: VideoGrant) => {
const at = new AccessToken(apiKey, apiSecret, userInfo);
at.ttl = '5m';
at.addGrant(grant);
return at.toJwt();
};
const roomPattern = /\w{4}\-\w{4}/;
export default async function handleToken(req: NextApiRequest, res: NextApiResponse) {
try {
const { roomName, identity, name, metadata } = req.query;
if (typeof identity !== 'string' || typeof roomName !== 'string') {
res.status(403).end();
return;
}
if (Array.isArray(name)) {
throw Error('provide max one name');
}
if (Array.isArray(metadata)) {
throw Error('provide max one metadata string');
}
// enforce room name to be xxxx-xxxx
// this is simple & naive way to prevent user from guessing room names
// please use your own authentication mechanisms in your own app
if (!roomName.match(roomPattern)) {
res.status(400).end();
return;
}
// if (!userSession.isAuthenticated) {
// res.status(403).end();
// return;
// }
const grant: VideoGrant = {
room: roomName,
roomJoin: true,
canPublish: true,
canPublishData: true,
canSubscribe: true,
};
const token = await createToken({ identity, name, metadata }, grant);
const result: TokenResult = {
identity,
accessToken: token,
};
res.status(200).json(result);
} catch (e) {
res.statusMessage = (e as Error).message;
res.status(500).end();
}
}

View File

@ -1,17 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { getLiveKitURL } from '../../lib/server-utils';
export default async function handleServerUrl(req: NextApiRequest, res: NextApiResponse) {
try {
const { region } = req.query;
if (Array.isArray(region)) {
throw Error('provide max one region string');
}
const url = getLiveKitURL(region);
res.status(200).json({ url });
} catch (e) {
res.statusMessage = (e as Error).message;
res.status(500).end();
}
}

View File

@ -1,82 +0,0 @@
import { formatChatMessageLinks, LiveKitRoom, VideoConference } from '@livekit/components-react';
import {
ExternalE2EEKeyProvider,
LogLevel,
Room,
RoomConnectOptions,
RoomOptions,
VideoCodec,
VideoPresets,
} from 'livekit-client';
import { useRouter } from 'next/router';
import { useMemo } from 'react';
import { decodePassphrase } from '../../lib/client-utils';
import { DebugMode } from '../../lib/Debug';
export default function CustomRoomConnection() {
const router = useRouter();
const { liveKitUrl, token, codec } = router.query;
const e2eePassphrase =
typeof window !== 'undefined' && decodePassphrase(window.location.hash.substring(1));
const worker =
typeof window !== 'undefined' &&
new Worker(new URL('livekit-client/e2ee-worker', import.meta.url));
const keyProvider = new ExternalE2EEKeyProvider();
const e2eeEnabled = !!(e2eePassphrase && worker);
const roomOptions = useMemo((): RoomOptions => {
return {
publishDefaults: {
videoSimulcastLayers: [VideoPresets.h540, VideoPresets.h216],
red: !e2eeEnabled,
videoCodec: codec as VideoCodec | undefined,
},
adaptiveStream: { pixelDensity: 'screen' },
dynacast: true,
e2ee: e2eeEnabled
? {
keyProvider,
worker,
}
: undefined,
};
}, []);
const room = useMemo(() => new Room(roomOptions), []);
if (e2eeEnabled) {
keyProvider.setKey(e2eePassphrase);
room.setE2EEEnabled(true);
}
const connectOptions = useMemo((): RoomConnectOptions => {
return {
autoSubscribe: true,
};
}, []);
if (typeof liveKitUrl !== 'string') {
return <h2>Missing LiveKit URL</h2>;
}
if (typeof token !== 'string') {
return <h2>Missing LiveKit token</h2>;
}
return (
<main data-lk-theme="default">
{liveKitUrl && (
<LiveKitRoom
room={room}
token={token}
connectOptions={connectOptions}
serverUrl={liveKitUrl}
audio={true}
video={true}
>
<VideoConference chatMessageFormatter={formatChatMessageLinks} />
<DebugMode logLevel={LogLevel.debug} />
</LiveKitRoom>
)}
</main>
);
}

View File

@ -1,186 +0,0 @@
'use client';
import {
LiveKitRoom,
VideoConference,
formatChatMessageLinks,
useToken,
LocalUserChoices,
} from '@livekit/components-react';
import {
DeviceUnsupportedError,
ExternalE2EEKeyProvider,
LogLevel,
Room,
RoomConnectOptions,
RoomOptions,
VideoCodec,
VideoPresets,
} from 'livekit-client';
import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import Head from 'next/head';
import { useRouter } from 'next/router';
import * as React from 'react';
import { DebugMode } from '../../lib/Debug';
import { decodePassphrase, useServerUrl } from '../../lib/client-utils';
const PreJoinNoSSR = dynamic(
async () => {
return (await import('@livekit/components-react')).PreJoin;
},
{ ssr: false },
);
const Home: NextPage = () => {
const router = useRouter();
const { name: roomName } = router.query;
const [preJoinChoices, setPreJoinChoices] = React.useState<LocalUserChoices | undefined>(
undefined,
);
function handlePreJoinSubmit(values: LocalUserChoices) {
setPreJoinChoices(values);
}
return (
<>
<Head>
<title>LiveKit Meet</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main data-lk-theme="default">
{roomName && !Array.isArray(roomName) && preJoinChoices ? (
<ActiveRoom
roomName={roomName}
userChoices={preJoinChoices}
onLeave={() => {
router.push('/');
}}
></ActiveRoom>
) : (
<div style={{ display: 'grid', placeItems: 'center', height: '100%' }}>
<PreJoinNoSSR
onError={(err) => console.log('error while setting up prejoin', err)}
defaults={{
username: '',
videoEnabled: true,
audioEnabled: true,
}}
onSubmit={handlePreJoinSubmit}
></PreJoinNoSSR>
</div>
)}
</main>
</>
);
};
export default Home;
type ActiveRoomProps = {
userChoices: LocalUserChoices;
roomName: string;
region?: string;
onLeave?: () => void;
};
const ActiveRoom = ({ roomName, userChoices, onLeave }: ActiveRoomProps) => {
const tokenOptions = React.useMemo(() => {
return {
userInfo: {
identity: userChoices.username,
name: userChoices.username,
},
};
}, [userChoices.username]);
const token = useToken(process.env.NEXT_PUBLIC_LK_TOKEN_ENDPOINT, roomName, tokenOptions);
const router = useRouter();
const { region, hq, codec } = router.query;
const e2eePassphrase =
typeof window !== 'undefined' && decodePassphrase(location.hash.substring(1));
const liveKitUrl = useServerUrl(region as string | undefined);
const worker =
typeof window !== 'undefined' &&
e2eePassphrase &&
new Worker(new URL('livekit-client/e2ee-worker', import.meta.url));
const e2eeEnabled = !!(e2eePassphrase && worker);
const keyProvider = new ExternalE2EEKeyProvider();
const roomOptions = React.useMemo((): RoomOptions => {
let videoCodec: VideoCodec | undefined = (
Array.isArray(codec) ? codec[0] : codec ?? 'vp9'
) as VideoCodec;
if (e2eeEnabled && (videoCodec === 'av1' || videoCodec === 'vp9')) {
videoCodec = undefined;
}
return {
videoCaptureDefaults: {
deviceId: userChoices.videoDeviceId ?? undefined,
resolution: hq === 'true' ? VideoPresets.h2160 : VideoPresets.h720,
},
publishDefaults: {
dtx: false,
videoSimulcastLayers:
hq === 'true'
? [VideoPresets.h1080, VideoPresets.h720]
: [VideoPresets.h540, VideoPresets.h216],
red: !e2eeEnabled,
videoCodec,
},
audioCaptureDefaults: {
deviceId: userChoices.audioDeviceId ?? undefined,
},
adaptiveStream: { pixelDensity: 'screen' },
dynacast: true,
e2ee: e2eeEnabled
? {
keyProvider,
worker,
}
: undefined,
};
}, [userChoices, hq, codec]);
const room = React.useMemo(() => new Room(roomOptions), []);
if (e2eeEnabled) {
keyProvider.setKey(decodePassphrase(e2eePassphrase));
room.setE2EEEnabled(true).catch((e) => {
if (e instanceof DeviceUnsupportedError) {
alert(
`You're trying to join an encrypted meeting, but your browser does not support it. Please update it to the latest version and try again.`,
);
console.error(e);
}
});
}
const connectOptions = React.useMemo((): RoomConnectOptions => {
return {
autoSubscribe: true,
};
}, []);
return (
<>
{liveKitUrl && (
<LiveKitRoom
room={room}
token={token}
serverUrl={liveKitUrl}
connectOptions={connectOptions}
video={userChoices.videoEnabled}
audio={userChoices.audioEnabled}
onDisconnected={onLeave}
>
<VideoConference chatMessageFormatter={formatChatMessageLinks} />
<DebugMode logLevel={LogLevel.debug} />
</LiveKitRoom>
)}
</>
);
};

6152
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4a3c9eb8da1ef3ddf2439428b49c11abd9a765e056600bd4f1d89a5dfc82778a
size 52339

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8bc017736e04acb0188f69cec0aafb88bd6891bfc4a6ff1530665e8dc210dbdf
size 1273171

View File

@ -1,17 +1,17 @@
{
"extends": ["config:base"],
"packageRules": [
{
"matchSourceUrlPrefixes": ["https://github.com/livekit/"],
"rangeStrategy": "replace",
"groupName": "LiveKit dependencies (non-major)",
"automerge": true
},
{
"schedule": "before 6am on the first day of the month",
"matchDepTypes": ["devDependencies"],
"matchUpdateTypes": ["patch", "minor"],
"groupName": "devDependencies (non-major)"
},
{
"matchSourceUrlPrefixes": ["https://github.com/livekit/"],
"rangeStrategy": "replace",
"groupName": "LiveKit dependencies (non-major)",
"automerge": true
}
]
}

View File

@ -1,11 +1,12 @@
.main {
position: relative;
display: grid;
gap: 1rem;
justify-content: center;
place-content: center;
justify-items: center;
padding-bottom: 100px;
overflow: auto;
flex-grow: 1;
}
.tabContainer {

View File

@ -0,0 +1,17 @@
.tabs {
position: relative;
display: flex;
align-content: space-between;
}
.tabs > .tab {
padding: 0.5rem;
border-radius: 0;
padding-bottom: 0.5rem;
border-bottom: 3px solid;
border-color: var(--bg5);
}
.tabs > .tab[aria-pressed='true'] {
border-color: var(--lk-accent-bg);
}

View File

@ -10,17 +10,16 @@ html {
html,
body {
overflow: hidden;
}
html,
body,
#__next,
main {
width: 100%;
height: 100%;
margin: 0px;
}
body {
display: flex;
flex-direction: column;
}
.header {
max-width: 500px;
padding-inline: 2rem;
@ -43,8 +42,6 @@ main {
}
footer {
position: absolute;
bottom: 0;
width: 100%;
padding: 1.5rem 2rem;
text-align: center;

View File

@ -1,21 +1,29 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"target": "ES2020",
"lib": ["dom", "dom.iterable", "ES2020"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"module": "ES2020",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"sourceMap": true
"sourceMap": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}