Fix ingest process detection in Restreamer UI, add dynamic camera orientation support, and implement OpenGL overlay rendering for lower thirds/comments in Almeira Live

This commit is contained in:
Carlos (Almeira) 2026-07-04 22:13:32 -05:00
parent d0523e7c02
commit 03d686d443
7 changed files with 424 additions and 52 deletions

@ -1 +1 @@
Subproject commit 97a1992210872fe8eff0b3bce0a0a97db67c6eb4
Subproject commit 07ecfa5268f8b17294a25d08ed82340479f4aaae

Binary file not shown.

361
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "almeira-web",
"version": "0.1.0",
"dependencies": {
"@livekit/components-react": "^2.9.21",
"@tiptap/extension-color": "^3.21.0",
"@tiptap/extension-font-family": "^3.21.0",
"@tiptap/extension-highlight": "^3.21.0",
@ -21,16 +22,18 @@
"@tiptap/starter-kit": "^3.21.0",
"@types/dompurify": "^3.0.5",
"@types/video.js": "^7.3.58",
"appwrite": "^22.4.1",
"appwrite": "^21.0.0",
"axios": "^1.18.0",
"cheerio": "^1.2.0",
"clsx": "^2.1.1",
"dompurify": "^3.3.3",
"dotenv": "^17.4.2",
"isomorphic-dompurify": "^2.20.0",
"livekit-client": "^2.20.0",
"livekit-server-sdk": "^2.16.0",
"lucide-react": "^0.577.0",
"next": "^16.2.6",
"node-appwrite": "^22.1.3",
"node-appwrite": "^21.0.0",
"react": "19.2.3",
"react-dom": "19.2.3",
"string-similarity": "^4.0.4",
@ -385,6 +388,12 @@
"specificity": "bin/cli.js"
}
},
"node_modules/@bufbuild/protobuf": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.10.1.tgz",
"integrity": "sha512-wJ8ReQbHxsAfXhrf9ixl0aYbZorRuOWpBNzm8pL8ftmSxQx/wnJD5Eg861NwJU/czy2VXFIebCeZnZrI9rktIQ==",
"license": "(Apache-2.0 AND BSD-3-Clause)"
},
"node_modules/@csstools/color-helpers": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz",
@ -718,7 +727,6 @@
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz",
"integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"@floating-ui/utils": "^0.2.11"
}
@ -738,8 +746,7 @@
"version": "0.2.11",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
"license": "MIT",
"optional": true
"license": "MIT"
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
@ -1309,6 +1316,77 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@livekit/components-core": {
"version": "0.12.13",
"resolved": "https://registry.npmjs.org/@livekit/components-core/-/components-core-0.12.13.tgz",
"integrity": "sha512-DQmi84afHoHjZ62wm8y+XPNIDHTwFHAltjd3lmyXj8UZHOY7wcza4vFt1xnghJOD5wLRY58L1dkAgAw59MgWvw==",
"license": "Apache-2.0",
"dependencies": {
"@floating-ui/dom": "1.7.4",
"loglevel": "1.9.1",
"rxjs": "7.8.2"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"livekit-client": "^2.17.2",
"tslib": "^2.6.2"
}
},
"node_modules/@livekit/components-core/node_modules/@floating-ui/dom": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.7.3",
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@livekit/components-react": {
"version": "2.9.21",
"resolved": "https://registry.npmjs.org/@livekit/components-react/-/components-react-2.9.21.tgz",
"integrity": "sha512-6hU9VucJJL+gAhilNGe4MBCDCZVk64qyjP9Ck86krvOIdVU76WeWksddg1MYUP10AlUwwrfD7davz41pJTcMJw==",
"license": "Apache-2.0",
"dependencies": {
"@livekit/components-core": "0.12.13",
"clsx": "2.1.1",
"events": "^3.3.0",
"jose": "^6.0.12",
"usehooks-ts": "3.1.1"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@livekit/krisp-noise-filter": "^0.2.12 || ^0.3.0",
"livekit-client": "^2.18.2",
"react": ">=18",
"react-dom": ">=18",
"tslib": "^2.6.2"
},
"peerDependenciesMeta": {
"@livekit/krisp-noise-filter": {
"optional": true
}
}
},
"node_modules/@livekit/mutex": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@livekit/mutex/-/mutex-1.1.1.tgz",
"integrity": "sha512-EsshAucklmpuUAfkABPxJNhzj9v2sG7JuzFDL4ML1oJQSV14sqrpTYnsaOudMAw9yOaW53NU3QQTlUQoRs4czw==",
"license": "Apache-2.0"
},
"node_modules/@livekit/protocol": {
"version": "1.46.6",
"resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.46.6.tgz",
"integrity": "sha512-upzlHP1vi/kZ/QqALZTFskQ0ifqc2f15RKucHYOsIHJsaXvEYanG75mAb7o+Yomfs4XhQ4BaRsdY+TFHXpaqrg==",
"license": "Apache-2.0",
"dependencies": {
"@bufbuild/protobuf": "^1.10.0"
}
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
@ -2343,6 +2421,13 @@
"tslib": "^2.4.0"
}
},
"node_modules/@types/dom-mediacapture-record": {
"version": "1.0.22",
"resolved": "https://registry.npmjs.org/@types/dom-mediacapture-record/-/dom-mediacapture-record-1.0.22.tgz",
"integrity": "sha512-mUMZLK3NvwRLcAAT9qmcK+9p7tpU2FHdDsntR3YI4+GY88XrgG4XiE7u1Q2LAN2/FZOz/tdMDC3GQCR4T8nFuw==",
"license": "MIT",
"peer": true
},
"node_modules/@types/dompurify": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
@ -3140,13 +3225,10 @@
}
},
"node_modules/appwrite": {
"version": "22.4.1",
"resolved": "https://registry.npmjs.org/appwrite/-/appwrite-22.4.1.tgz",
"integrity": "sha512-NlEgUvSo7A1m+TEHZ8zlWUIuCDtNg3VN6Mbxd6r+1618CiWokig630FpeKQ9/WD5Io92QxuTV/dVzBLPlQghqg==",
"license": "BSD-3-Clause",
"dependencies": {
"json-bigint": "1.0.0"
}
"version": "21.5.0",
"resolved": "https://registry.npmjs.org/appwrite/-/appwrite-21.5.0.tgz",
"integrity": "sha512-643bMRZVYXMluXvSXbdaLAi9qqTJLWbVGguKH4vH6IdKHur6gGIirhCOqAEt33pV4TOFJ55VBu8c/+Ft1ke2SA==",
"license": "BSD-3-Clause"
},
"node_modules/argparse": {
"version": "2.0.1",
@ -3485,15 +3567,6 @@
"require-from-string": "^2.0.2"
}
},
"node_modules/bignumber.js": {
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
"integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@ -3617,6 +3690,36 @@
"node": ">=6"
}
},
"node_modules/camelcase": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
"integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
"license": "MIT",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/camelcase-keys": {
"version": "9.1.3",
"resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-9.1.3.tgz",
"integrity": "sha512-Rircqi9ch8AnZscQcsA1C47NFdaO3wukpmIRzYcDOrmvgt78hM/sj5pZhZNec2NM12uk5vTwRHZ4anGcrC4ZTg==",
"license": "MIT",
"dependencies": {
"camelcase": "^8.0.0",
"map-obj": "5.0.0",
"quick-lru": "^6.1.1",
"type-fest": "^4.3.2"
},
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001778",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz",
@ -4828,6 +4931,15 @@
"node": ">=0.10.0"
}
},
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"license": "MIT",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -5951,6 +6063,15 @@
"jiti": "lib/jiti-cli.mjs"
}
},
"node_modules/jose": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz",
"integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -6034,15 +6155,6 @@
"node": ">=6"
}
},
"node_modules/json-bigint": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
"license": "MIT",
"dependencies": {
"bignumber.js": "^9.0.0"
}
},
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@ -6423,6 +6535,72 @@
"integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==",
"license": "MIT"
},
"node_modules/livekit-client": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.20.0.tgz",
"integrity": "sha512-RIJcpvBmOmwz3jTj3rmdY6Dzr55HrhcaJjMgY+HSmoEM+yIRyA40m7r8UKv0hnZWM3z/AYhP1q8C8ciz5UWFKQ==",
"license": "Apache-2.0",
"dependencies": {
"@livekit/mutex": "1.1.1",
"@livekit/protocol": "1.46.6",
"events": "^3.3.0",
"jose": "^6.1.0",
"loglevel": "^1.9.2",
"sdp-transform": "^2.15.0",
"tslib": "2.8.1",
"typed-emitter": "^2.1.0",
"webrtc-adapter": "9.0.5"
},
"peerDependencies": {
"@types/dom-mediacapture-record": "^1"
}
},
"node_modules/livekit-client/node_modules/loglevel": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
"integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
},
"funding": {
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/loglevel"
}
},
"node_modules/livekit-server-sdk": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/livekit-server-sdk/-/livekit-server-sdk-2.16.0.tgz",
"integrity": "sha512-Myr90TWbqUujvPpo0N8JS+gtY6jVKgbIRx688cVSL3mp18IVAwhDWyRzNloQBcimHg24zz1JjERTiwOw0wuFIg==",
"license": "Apache-2.0",
"dependencies": {
"@bufbuild/protobuf": "^1.10.1",
"@livekit/protocol": "^1.48.0",
"camelcase-keys": "^9.0.0",
"jose": "^5.1.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/livekit-server-sdk/node_modules/@livekit/protocol": {
"version": "1.49.0",
"resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.49.0.tgz",
"integrity": "sha512-TCmihwjHBWAUdNrqvmrN/K59r7vDnPIy3OEaE3p0g4FvdcQTiLynmFC3igR25Q2fnifahhAJjnnfSCtWUK7kYg==",
"license": "Apache-2.0",
"dependencies": {
"@bufbuild/protobuf": "^1.10.0"
}
},
"node_modules/livekit-server-sdk/node_modules/jose": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz",
"integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@ -6439,6 +6617,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"license": "MIT"
},
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -6446,6 +6630,19 @@
"dev": true,
"license": "MIT"
},
"node_modules/loglevel": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz",
"integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
},
"funding": {
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/loglevel"
}
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -6499,6 +6696,18 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/map-obj": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-5.0.0.tgz",
"integrity": "sha512-2L3MIgJynYrZ3TYMriLDLWocz15okFakV6J12HXvMXDHui2x/zgChzg1u9mFFGbbGWE+GsLpQByt4POb9Or+uA==",
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/markdown-it": {
"version": "14.3.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz",
@ -6757,12 +6966,11 @@
}
},
"node_modules/node-appwrite": {
"version": "22.1.3",
"resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-22.1.3.tgz",
"integrity": "sha512-pXxvojqgYY3HAiJAj+P6xb/CXVNsMUd+JXVOLrCCDzqZmpKeMF6OEt4mAoDEyKTnRaixUDHlPrdh7y1G/w3sbw==",
"version": "21.1.0",
"resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-21.1.0.tgz",
"integrity": "sha512-HRK5BzN19vgvaH/EeNsigK24t4ngJ1AoiltK5JtahxP6uyMRztzkD8cXP+z9jj/xOjz7ySfQ9YypNyhNr6zVkA==",
"license": "BSD-3-Clause",
"dependencies": {
"json-bigint": "1.0.0",
"node-fetch-native-with-agent": "1.7.2"
}
},
@ -7494,6 +7702,18 @@
],
"license": "MIT"
},
"node_modules/quick-lru": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz",
"integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/react": {
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
@ -7657,6 +7877,15 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/rxjs": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/safe-array-concat": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
@ -7736,6 +7965,21 @@
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"license": "MIT"
},
"node_modules/sdp": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.2.tgz",
"integrity": "sha512-xZocWwfyp4hkbN4hLWxMjmv2Q8aNa9MhmOZ7L9aCZPT+dZsgRr6wZRrSYE3HTdyk/2pZKPSgqI7ns7Een1xMSA==",
"license": "MIT"
},
"node_modules/sdp-transform": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.15.0.tgz",
"integrity": "sha512-KrOH82c/W+GYQ0LHqtr3caRpM3ITglq3ljGUIb8LTki7ByacJZ9z+piSGiwZDsRyhQbYBOBJgr2k6X4BZXi3Kw==",
"license": "MIT",
"bin": {
"sdp-verify": "checker.js"
}
},
"node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@ -8372,6 +8616,18 @@
"node": ">= 0.8.0"
}
},
"node_modules/type-fest": {
"version": "4.41.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
"integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/typed-array-buffer": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
@ -8450,6 +8706,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/typed-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz",
"integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==",
"license": "MIT",
"optionalDependencies": {
"rxjs": "*"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@ -8614,6 +8879,21 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/usehooks-ts": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.1.tgz",
"integrity": "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==",
"license": "MIT",
"dependencies": {
"lodash.debounce": "^4.0.8"
},
"engines": {
"node": ">=16.15.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc"
}
},
"node_modules/video.js": {
"version": "8.23.7",
"resolved": "https://registry.npmjs.org/video.js/-/video.js-8.23.7.tgz",
@ -8692,6 +8972,19 @@
"node": ">=20"
}
},
"node_modules/webrtc-adapter": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-9.0.5.tgz",
"integrity": "sha512-U9vjByy/sK2OMXu5mmfuZFKTMIUQe34c0JXRO+oDrxJTsntdYT2iIFwYMOV7HhMTuktcZLGf2W1N/OcSf9ssWg==",
"license": "BSD-3-Clause",
"dependencies": {
"sdp": "^3.2.0"
},
"engines": {
"node": ">=6.0.0",
"npm": ">=3.10.0"
}
},
"node_modules/whatwg-encoding": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",

View File

@ -10,6 +10,7 @@
"check-config": "node --env-file=.env.local check_config.js"
},
"dependencies": {
"@livekit/components-react": "^2.9.21",
"@tiptap/extension-color": "^3.21.0",
"@tiptap/extension-font-family": "^3.21.0",
"@tiptap/extension-highlight": "^3.21.0",
@ -23,16 +24,18 @@
"@tiptap/starter-kit": "^3.21.0",
"@types/dompurify": "^3.0.5",
"@types/video.js": "^7.3.58",
"appwrite": "^22.4.1",
"appwrite": "^21.0.0",
"axios": "^1.18.0",
"cheerio": "^1.2.0",
"clsx": "^2.1.1",
"dompurify": "^3.3.3",
"dotenv": "^17.4.2",
"isomorphic-dompurify": "^2.20.0",
"livekit-client": "^2.20.0",
"livekit-server-sdk": "^2.16.0",
"lucide-react": "^0.577.0",
"next": "^16.2.6",
"node-appwrite": "^22.1.3",
"node-appwrite": "^21.0.0",
"react": "19.2.3",
"react-dom": "19.2.3",
"string-similarity": "^4.0.4",

View File

@ -59,30 +59,36 @@ export type RestreamServerInfo = {
viewer_count: number;
};
// ── Información del servidor ───────────────────────────────────────────────────
export async function getRestreamServerInfo(): Promise<{ success: boolean; data?: RestreamServerInfo; error?: string }> {
try {
const data = await restreamFetch('/api/v3/metrics/process/restreamer-ui:ingest:*');
const processes: any[] = await restreamFetch('/api/v3/process');
const ingest = processes.find((p: any) => p.id?.includes('ingest') || p.metadata?.['restreamer-ui']?.meta?.type === 'ingest' || p.metadata?.['restreamer-ui']?.type === 'ingest');
let active = false;
let viewers = 0;
let uptime = 0;
if (ingest) {
const metrics = await restreamFetch(`/api/v3/metrics/process/${ingest.id}`).catch(() => null);
active = ingest.state?.exec === 'running';
viewers = metrics?.viewers ?? 0;
uptime = metrics?.uptime ?? ingest.state?.uptime ?? 0;
}
return {
success: true,
data: {
version: data?.version ?? 'desconocida',
uptime_seconds: data?.uptime ?? 0,
version: '16+',
uptime_seconds: uptime,
is_connected: true,
active_ingest: data?.state === 'running',
viewer_count: data?.viewers ?? 0,
active_ingest: active,
viewer_count: viewers,
}
};
} catch (error: any) {
// Si falla el endpoint de metrics, devolvemos status básico
try {
await restreamFetch('/api/v3/config/active');
return { success: true, data: { version: '-', uptime_seconds: 0, is_connected: true, active_ingest: false, viewer_count: 0 } };
} catch {
return { success: false, error: error.message };
}
}
}
// ── Listar outputs (procesos de egress) ───────────────────────────────────────
export async function listRestreamOutputs(): Promise<{ success: boolean; data: RestreamOutput[]; error?: string }> {
@ -115,6 +121,10 @@ export async function createRestreamOutput(params: {
platform: string;
}): Promise<{ success: boolean; id?: string; error?: string }> {
try {
const processes: any[] = await restreamFetch('/api/v3/process');
const ingest = processes.find((p: any) => p.id?.includes('ingest') || p.metadata?.['restreamer-ui']?.meta?.type === 'ingest' || p.metadata?.['restreamer-ui']?.type === 'ingest');
const reference = ingest?.id || 'restreamer-ui:ingest:*';
const address = `${params.rtmpUrl.replace(/\/$/, '')}/${params.streamKey}`;
const id = `restreamer-ui:egress:rtmp:${Date.now()}`;
@ -122,7 +132,7 @@ export async function createRestreamOutput(params: {
method: 'POST',
body: JSON.stringify({
id,
reference: 'restreamer-ui:ingest:*',
reference: reference,
config: {
id,
reconnect: true,
@ -185,7 +195,7 @@ export async function deleteRestreamOutput(id: string): Promise<{ success: boole
export async function getIngestStats(): Promise<{ success: boolean; data?: { bitrate: number; fps: number; state: string }; error?: string }> {
try {
const processes: any[] = await restreamFetch('/api/v3/process');
const ingest = processes.find((p: any) => p.id?.includes('ingest'));
const ingest = processes.find((p: any) => p.id?.includes('ingest') || p.metadata?.['restreamer-ui']?.meta?.type === 'ingest' || p.metadata?.['restreamer-ui']?.type === 'ingest');
return {
success: true,
data: {

View File

@ -0,0 +1,24 @@
import { AccessToken } from 'livekit-server-sdk';
import { NextRequest, NextResponse } from 'next/server';
export async function GET(req: NextRequest) {
const room = req.nextUrl.searchParams.get('room');
const identity = req.nextUrl.searchParams.get('identity') || `guest-${Math.floor(Math.random() * 1000)}`;
if (!room) {
return NextResponse.json({ error: 'Missing "room" query parameter' }, { status: 400 });
}
// Las variables de entorno de LiveKit
// Si no existen, podemos usar unas por defecto para el desarrollo local, pero en prod deben estar configuradas
const apiKey = process.env.LIVEKIT_API_KEY || 'devkey';
const apiSecret = process.env.LIVEKIT_API_SECRET || 'secret';
const at = new AccessToken(apiKey, apiSecret, {
identity: identity,
});
at.addGrant({ roomJoin: true, room: room });
return NextResponse.json({ token: await at.toJwt() });
}

42
test-pagination.js Normal file
View File

@ -0,0 +1,42 @@
import { Client, Databases, Query } from "node-appwrite";
const client = new Client()
.setEndpoint('https://appwrite.almeira.com.co/v1')
.setProject('69b84df5003bd8957c5c')
.setKey('standard_ee81a4ca6784c0fa6a23abab0a983dbba9cd0c2d8e211a00c759d9801f5d8e27b43a605bfa373e13106da87acfc7673608f0ffb0eb04e10cae3dae93d2404bfda8676f96e3fcfd6bd642f6e12d69367916b796c54e98e2e4c4bc0b7a27aa42ba5048582fcbd45b1365b51c5014cb939c1853923c418048ff0e39c2237065da3c');
const databases = new Databases(client);
async function run() {
try {
const typeRes = await databases.listDocuments('almeira', 'publication_types', [
Query.equal('codigo', 'NOTICIA')
]);
const typeId = typeRes.documents[0].$id;
const queriesAdmin = [
Query.equal('tipo_publicacion_id', typeId),
Query.orderDesc('fecha_publicacion'),
Query.limit(12),
Query.offset(0),
Query.equal('estado', ['Publicado', 'borrador'])
];
const queriesUser = [
Query.equal('tipo_publicacion_id', typeId),
Query.orderDesc('fecha_publicacion'),
Query.limit(12),
Query.offset(0),
Query.equal('estado', 'Publicado')
];
const resAdmin = await databases.listDocuments('almeira', 'posts', queriesAdmin);
console.log('Admin total:', resAdmin.total);
const resUser = await databases.listDocuments('almeira', 'posts', queriesUser);
console.log('User total:', resUser.total);
} catch (e) {
console.error(e);
}
}
run();