add screen recording using recordRTC

This commit is contained in:
MarconLP 2023-04-18 22:20:30 +02:00
parent d42da7bb8c
commit 48c2702701
No known key found for this signature in database
GPG key ID: A08A9C8B623F5EA5
4 changed files with 693 additions and 60 deletions

454
package-lock.json generated
View file

@ -22,6 +22,7 @@
"@trpc/next": "^10.18.0",
"@trpc/react-query": "^10.18.0",
"@trpc/server": "^10.18.0",
"@types/recordrtc": "^5.6.11",
"@upstash/qstash": "^0.3.6",
"axios": "^1.3.5",
"dayjs": "^1.11.7",
@ -32,8 +33,10 @@
"next-auth": "^4.21.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-media-recorder": "^1.6.6",
"react-player": "^2.12.0",
"react-popper": "^2.3.0",
"recordrtc": "^5.6.2",
"stripe": "^12.1.1",
"superjson": "1.12.2",
"zod": "^3.21.4"
@ -2404,6 +2407,11 @@
"@types/react": "*"
}
},
"node_modules/@types/recordrtc": {
"version": "5.6.11",
"resolved": "https://registry.npmjs.org/@types/recordrtc/-/recordrtc-5.6.11.tgz",
"integrity": "sha512-X4XD5nltz0cjmyzsPNegQReOPF+C5ARTfSPAPhqnKV7SsfRta/M4FBJ5AtSInCaEveL71FLLSVQE9mg8Uuo++w=="
},
"node_modules/@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
@ -2814,6 +2822,18 @@
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/automation-events": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/automation-events/-/automation-events-5.0.3.tgz",
"integrity": "sha512-ZZWTNYJTkGjcJUOBX5P0MHZrArJOkcrQsbyGWwlzJpZs961Y5YvKUw5MsAf8xLlvh7+1B8SO/VTvjMmVXFkD3w==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"tslib": "^2.5.0"
},
"engines": {
"node": ">=14.15.4"
}
},
"node_modules/autoprefixer": {
"version": "10.4.14",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
@ -2929,6 +2949,17 @@
"node": ">=8"
}
},
"node_modules/broker-factory": {
"version": "3.0.75",
"resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.0.75.tgz",
"integrity": "sha512-VMC2GBMaoKXdVPC0yH/Z1NbDieYRuKlGPT6PbrrReDwvHSZH2Cl5dJVO3tPTkA9Q+xXOmnRYgcjNktZD/Oz21w==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"fast-unique-numbers": "^7.0.2",
"tslib": "^2.5.0",
"worker-factory": "^6.0.76"
}
},
"node_modules/browserslist": {
"version": "4.21.5",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
@ -3129,6 +3160,20 @@
"node": ">= 6"
}
},
"node_modules/compilerr": {
"version": "10.0.2",
"resolved": "https://registry.npmjs.org/compilerr/-/compilerr-10.0.2.tgz",
"integrity": "sha512-CFwUXxJ9OuWsSvnLSbefxi+GLsZ0YnuJh40ry5QdmZ1FWK59OG+QB8XSj6t7Kq+/c5DSS7en+cML6GlzHKH58A==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"dashify": "^2.0.0",
"indefinite-article": "0.0.2",
"tslib": "^2.5.0"
},
"engines": {
"node": ">=14.15.4"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -3203,6 +3248,14 @@
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
"dev": true
},
"node_modules/dashify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dashify/-/dashify-2.0.0.tgz",
"integrity": "sha512-hpA5C/YrPjucXypHPPc0oJ1l9Hf6wWbiOL7Ik42cxnsUOhWiCB/fylKbKqqJalW9FgkNQCw16YO8uW9Hs0Iy1A==",
"engines": {
"node": ">=4"
}
},
"node_modules/dayjs": {
"version": "1.11.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
@ -4004,6 +4057,52 @@
"node": ">=0.10.0"
}
},
"node_modules/extendable-media-recorder": {
"version": "6.6.10",
"resolved": "https://registry.npmjs.org/extendable-media-recorder/-/extendable-media-recorder-6.6.10.tgz",
"integrity": "sha512-gnSmLqDFq40ZdbGfuarnMLNqYPLCPpPr0p21V+g67wG4Pv2oCc/ga8sfsZrEM5GywEi7FcpyRm3z99JWZ/0aPw==",
"dependencies": {
"@babel/runtime": "^7.18.9",
"media-encoder-host": "^8.0.76",
"multi-buffer-data-view": "^3.0.20",
"recorder-audio-worklet": "^5.1.26",
"standardized-audio-context": "^25.3.29",
"subscribable-things": "^2.1.6",
"tslib": "^2.4.0"
}
},
"node_modules/extendable-media-recorder-wav-encoder": {
"version": "7.0.85",
"resolved": "https://registry.npmjs.org/extendable-media-recorder-wav-encoder/-/extendable-media-recorder-wav-encoder-7.0.85.tgz",
"integrity": "sha512-M96Y6twJ/OBordRltfyf8Kl2P2C0e176ogVTpGYquHApEyRnGpnm6LMmNqiRkQC8me0WOOhKLVvKCGmEF3lmvQ==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"extendable-media-recorder-wav-encoder-broker": "^7.0.77",
"extendable-media-recorder-wav-encoder-worker": "^8.0.76",
"tslib": "^2.5.0"
}
},
"node_modules/extendable-media-recorder-wav-encoder-broker": {
"version": "7.0.77",
"resolved": "https://registry.npmjs.org/extendable-media-recorder-wav-encoder-broker/-/extendable-media-recorder-wav-encoder-broker-7.0.77.tgz",
"integrity": "sha512-HNUcsUMtKw8hUx1pHM/78+joa3L7FpCh+R3jX9RRuSrHMUw/ldq29wi/evBs4lMoasyMZLHD3yI1Phz0hYH7pg==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"broker-factory": "^3.0.75",
"extendable-media-recorder-wav-encoder-worker": "^8.0.76",
"tslib": "^2.5.0"
}
},
"node_modules/extendable-media-recorder-wav-encoder-worker": {
"version": "8.0.76",
"resolved": "https://registry.npmjs.org/extendable-media-recorder-wav-encoder-worker/-/extendable-media-recorder-wav-encoder-worker-8.0.76.tgz",
"integrity": "sha512-m/pafu/XodKM05r3wkdY6D+gvprD6VCe1rLKnmF/Ooj4VGQ3DglsOhxWmEXRHVtzFWHIxIpjq6Z14VYaj1sXUg==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"tslib": "^2.5.0",
"worker-factory": "^6.0.76"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -4050,6 +4149,18 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
"node_modules/fast-unique-numbers": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-7.0.2.tgz",
"integrity": "sha512-xnqpsnu889bHbq5cbDMwCJ2BPf6kjFPMu+RHfqKvisRxeEbTOVxY5aW/ZNsZ/r8OlwatxmjdFEVQog2xAhLkvg==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"tslib": "^2.5.0"
},
"engines": {
"node": ">=14.15.4"
}
},
"node_modules/fast-xml-parser": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz",
@ -4539,6 +4650,11 @@
"node": ">=0.8.19"
}
},
"node_modules/indefinite-article": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/indefinite-article/-/indefinite-article-0.0.2.tgz",
"integrity": "sha512-Au/2XzRkvxq2J6w5uvSSbBKPZ5kzINx5F2wb0SF8xpRL8BP9Lav81TnRbfPp6p+SYjYxwaaLn4EUwI3/MmYKSw=="
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@ -5094,6 +5210,40 @@
"node": ">=10"
}
},
"node_modules/media-encoder-host": {
"version": "8.0.88",
"resolved": "https://registry.npmjs.org/media-encoder-host/-/media-encoder-host-8.0.88.tgz",
"integrity": "sha512-15pWY8GFLsm3w01JuSQdEIkCh1+KUKqNQg5F6s5SXhAwbj0DuTceFbyuqWFMq+XzV8zZrPt8LtYAAj5ld5IEOw==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"media-encoder-host-broker": "^7.0.78",
"media-encoder-host-worker": "^9.1.0",
"tslib": "^2.5.0"
}
},
"node_modules/media-encoder-host-broker": {
"version": "7.0.78",
"resolved": "https://registry.npmjs.org/media-encoder-host-broker/-/media-encoder-host-broker-7.0.78.tgz",
"integrity": "sha512-5IZEZwN4qyCymkP6FCELS6rRCE2xhNFiE3FWr+8R3ySRn1iwPWd/U/YFB4PdvjqFw3MtQ9rHFErfc/YjUf8j9g==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"broker-factory": "^3.0.75",
"fast-unique-numbers": "^7.0.2",
"media-encoder-host-worker": "^9.1.0",
"tslib": "^2.5.0"
}
},
"node_modules/media-encoder-host-worker": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/media-encoder-host-worker/-/media-encoder-host-worker-9.1.0.tgz",
"integrity": "sha512-dVvb65RH/5Yt5PBxBVGw+BcCFkq8A15/fw3HxEKugtaFJocTcwQsS1IeDUuf1i831Fk/FuCipnXFz8VsGRCZyg==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"extendable-media-recorder-wav-encoder-broker": "^7.0.77",
"tslib": "^2.5.0",
"worker-factory": "^6.0.76"
}
},
"node_modules/memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
@ -5196,6 +5346,18 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/multi-buffer-data-view": {
"version": "3.0.24",
"resolved": "https://registry.npmjs.org/multi-buffer-data-view/-/multi-buffer-data-view-3.0.24.tgz",
"integrity": "sha512-jm7Ycplx37ExXyQmqhwl7zfQmAj81y5LLzVx0XyWea4omP9W/xJhLEHs/5b+WojGyYSRt8BHiXZVcYzu68Ma0Q==",
"dependencies": {
"@babel/runtime": "^7.20.6",
"tslib": "^2.4.1"
},
"engines": {
"node": ">=12.20.1"
}
},
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
@ -6046,6 +6208,15 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-media-recorder": {
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/react-media-recorder/-/react-media-recorder-1.6.6.tgz",
"integrity": "sha512-VdC4bUINMWJyqOAHw1DaZ8HZhdCyVBK85zJ4cHMo9tsrekui3wq5ZxNtBmNe6nbAFQBTNj/pRnLEsiVrCW+TNQ==",
"dependencies": {
"extendable-media-recorder": "^6.6.5",
"extendable-media-recorder-wav-encoder": "^7.0.68"
}
},
"node_modules/react-player": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/react-player/-/react-player-2.12.0.tgz",
@ -6104,6 +6275,35 @@
"node": ">=8.10.0"
}
},
"node_modules/recorder-audio-worklet": {
"version": "5.1.39",
"resolved": "https://registry.npmjs.org/recorder-audio-worklet/-/recorder-audio-worklet-5.1.39.tgz",
"integrity": "sha512-w/RazoBwZnkFnEPRsJYNThOHznLQC98/IzWRrutpJQVvCcL0nbLsVSLDaRrnrqVpRUI11VgiXRh30HaHiSdVhQ==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"broker-factory": "^3.0.75",
"fast-unique-numbers": "^7.0.2",
"recorder-audio-worklet-processor": "^4.2.21",
"standardized-audio-context": "^25.3.41",
"subscribable-things": "^2.1.14",
"tslib": "^2.5.0",
"worker-factory": "^6.0.76"
}
},
"node_modules/recorder-audio-worklet-processor": {
"version": "4.2.21",
"resolved": "https://registry.npmjs.org/recorder-audio-worklet-processor/-/recorder-audio-worklet-processor-4.2.21.tgz",
"integrity": "sha512-oiiS2sp6eMxkvjt13yetSYUJvnAxBZk60mIxz0Vf/2lDWa/4svCyMLHIDzYKbHahkISd0UYyqLS9dI7xDlUOCA==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"tslib": "^2.5.0"
}
},
"node_modules/recordrtc": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/recordrtc/-/recordrtc-5.6.2.tgz",
"integrity": "sha512-1QNKKNtl7+KcwD1lyOgP3ZlbiJ1d0HtXnypUy7yq49xEERxk31PHvE9RCciDrulPCY7WJ+oz0R9hpNxgsIurGQ=="
},
"node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
@ -6200,6 +6400,11 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/rxjs-interop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/rxjs-interop/-/rxjs-interop-2.0.0.tgz",
"integrity": "sha512-ASEq9atUw7lualXB+knvgtvwkCEvGWV2gDD/8qnASzBkzEARZck9JAyxmY8OS6Nc1pCPEgDTKNcx+YqqYfzArw=="
},
"node_modules/safe-regex-test": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
@ -6317,6 +6522,16 @@
"source-map": "^0.6.0"
}
},
"node_modules/standardized-audio-context": {
"version": "25.3.43",
"resolved": "https://registry.npmjs.org/standardized-audio-context/-/standardized-audio-context-25.3.43.tgz",
"integrity": "sha512-Wh/6rZCScDh3ZAsRy0kgeGF+g3mGO4E6UiWj8k79uCHKJjqiBJOtbwEFS1MBFfVXMHN3L3U3kn1aVrkCHtlkvw==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"automation-events": "^5.0.3",
"tslib": "^2.5.0"
}
},
"node_modules/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@ -6481,6 +6696,16 @@
}
}
},
"node_modules/subscribable-things": {
"version": "2.1.14",
"resolved": "https://registry.npmjs.org/subscribable-things/-/subscribable-things-2.1.14.tgz",
"integrity": "sha512-+0e6wGpFVa7snrwzY/xwiiQhlpBglvPAhsYK9EhFAcboP5z0eN1W3CQoROYyeY3psiCbwcleb9vaXasKAcvISw==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"rxjs-interop": "^2.0.0",
"tslib": "^2.5.0"
}
},
"node_modules/sucrase": {
"version": "3.32.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
@ -6995,6 +7220,17 @@
"node": ">=0.10.0"
}
},
"node_modules/worker-factory": {
"version": "6.0.76",
"resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-6.0.76.tgz",
"integrity": "sha512-W1iBNPmE9p0asU4aFmYJYCnMxhkvk4qlKc660GlHxWgmflY64NxxTbmKqipu4K5p9LiKKPjqXfcQme6153BZEQ==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"compilerr": "^10.0.2",
"fast-unique-numbers": "^7.0.2",
"tslib": "^2.5.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@ -8759,6 +8995,11 @@
"@types/react": "*"
}
},
"@types/recordrtc": {
"version": "5.6.11",
"resolved": "https://registry.npmjs.org/@types/recordrtc/-/recordrtc-5.6.11.tgz",
"integrity": "sha512-X4XD5nltz0cjmyzsPNegQReOPF+C5ARTfSPAPhqnKV7SsfRta/M4FBJ5AtSInCaEveL71FLLSVQE9mg8Uuo++w=="
},
"@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
@ -9032,6 +9273,15 @@
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"automation-events": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/automation-events/-/automation-events-5.0.3.tgz",
"integrity": "sha512-ZZWTNYJTkGjcJUOBX5P0MHZrArJOkcrQsbyGWwlzJpZs961Y5YvKUw5MsAf8xLlvh7+1B8SO/VTvjMmVXFkD3w==",
"requires": {
"@babel/runtime": "^7.21.0",
"tslib": "^2.5.0"
}
},
"autoprefixer": {
"version": "10.4.14",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
@ -9113,6 +9363,17 @@
"fill-range": "^7.0.1"
}
},
"broker-factory": {
"version": "3.0.75",
"resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.0.75.tgz",
"integrity": "sha512-VMC2GBMaoKXdVPC0yH/Z1NbDieYRuKlGPT6PbrrReDwvHSZH2Cl5dJVO3tPTkA9Q+xXOmnRYgcjNktZD/Oz21w==",
"requires": {
"@babel/runtime": "^7.21.0",
"fast-unique-numbers": "^7.0.2",
"tslib": "^2.5.0",
"worker-factory": "^6.0.76"
}
},
"browserslist": {
"version": "4.21.5",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
@ -9241,6 +9502,17 @@
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"dev": true
},
"compilerr": {
"version": "10.0.2",
"resolved": "https://registry.npmjs.org/compilerr/-/compilerr-10.0.2.tgz",
"integrity": "sha512-CFwUXxJ9OuWsSvnLSbefxi+GLsZ0YnuJh40ry5QdmZ1FWK59OG+QB8XSj6t7Kq+/c5DSS7en+cML6GlzHKH58A==",
"requires": {
"@babel/runtime": "^7.21.0",
"dashify": "^2.0.0",
"indefinite-article": "0.0.2",
"tslib": "^2.5.0"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -9294,6 +9566,11 @@
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
"dev": true
},
"dashify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dashify/-/dashify-2.0.0.tgz",
"integrity": "sha512-hpA5C/YrPjucXypHPPc0oJ1l9Hf6wWbiOL7Ik42cxnsUOhWiCB/fylKbKqqJalW9FgkNQCw16YO8uW9Hs0Iy1A=="
},
"dayjs": {
"version": "1.11.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
@ -9914,6 +10191,52 @@
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true
},
"extendable-media-recorder": {
"version": "6.6.10",
"resolved": "https://registry.npmjs.org/extendable-media-recorder/-/extendable-media-recorder-6.6.10.tgz",
"integrity": "sha512-gnSmLqDFq40ZdbGfuarnMLNqYPLCPpPr0p21V+g67wG4Pv2oCc/ga8sfsZrEM5GywEi7FcpyRm3z99JWZ/0aPw==",
"requires": {
"@babel/runtime": "^7.18.9",
"media-encoder-host": "^8.0.76",
"multi-buffer-data-view": "^3.0.20",
"recorder-audio-worklet": "^5.1.26",
"standardized-audio-context": "^25.3.29",
"subscribable-things": "^2.1.6",
"tslib": "^2.4.0"
}
},
"extendable-media-recorder-wav-encoder": {
"version": "7.0.85",
"resolved": "https://registry.npmjs.org/extendable-media-recorder-wav-encoder/-/extendable-media-recorder-wav-encoder-7.0.85.tgz",
"integrity": "sha512-M96Y6twJ/OBordRltfyf8Kl2P2C0e176ogVTpGYquHApEyRnGpnm6LMmNqiRkQC8me0WOOhKLVvKCGmEF3lmvQ==",
"requires": {
"@babel/runtime": "^7.21.0",
"extendable-media-recorder-wav-encoder-broker": "^7.0.77",
"extendable-media-recorder-wav-encoder-worker": "^8.0.76",
"tslib": "^2.5.0"
}
},
"extendable-media-recorder-wav-encoder-broker": {
"version": "7.0.77",
"resolved": "https://registry.npmjs.org/extendable-media-recorder-wav-encoder-broker/-/extendable-media-recorder-wav-encoder-broker-7.0.77.tgz",
"integrity": "sha512-HNUcsUMtKw8hUx1pHM/78+joa3L7FpCh+R3jX9RRuSrHMUw/ldq29wi/evBs4lMoasyMZLHD3yI1Phz0hYH7pg==",
"requires": {
"@babel/runtime": "^7.21.0",
"broker-factory": "^3.0.75",
"extendable-media-recorder-wav-encoder-worker": "^8.0.76",
"tslib": "^2.5.0"
}
},
"extendable-media-recorder-wav-encoder-worker": {
"version": "8.0.76",
"resolved": "https://registry.npmjs.org/extendable-media-recorder-wav-encoder-worker/-/extendable-media-recorder-wav-encoder-worker-8.0.76.tgz",
"integrity": "sha512-m/pafu/XodKM05r3wkdY6D+gvprD6VCe1rLKnmF/Ooj4VGQ3DglsOhxWmEXRHVtzFWHIxIpjq6Z14VYaj1sXUg==",
"requires": {
"@babel/runtime": "^7.21.0",
"tslib": "^2.5.0",
"worker-factory": "^6.0.76"
}
},
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -9956,6 +10279,15 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
"fast-unique-numbers": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-7.0.2.tgz",
"integrity": "sha512-xnqpsnu889bHbq5cbDMwCJ2BPf6kjFPMu+RHfqKvisRxeEbTOVxY5aW/ZNsZ/r8OlwatxmjdFEVQog2xAhLkvg==",
"requires": {
"@babel/runtime": "^7.21.0",
"tslib": "^2.5.0"
}
},
"fast-xml-parser": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz",
@ -10293,6 +10625,11 @@
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true
},
"indefinite-article": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/indefinite-article/-/indefinite-article-0.0.2.tgz",
"integrity": "sha512-Au/2XzRkvxq2J6w5uvSSbBKPZ5kzINx5F2wb0SF8xpRL8BP9Lav81TnRbfPp6p+SYjYxwaaLn4EUwI3/MmYKSw=="
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@ -10688,6 +11025,40 @@
"yallist": "^4.0.0"
}
},
"media-encoder-host": {
"version": "8.0.88",
"resolved": "https://registry.npmjs.org/media-encoder-host/-/media-encoder-host-8.0.88.tgz",
"integrity": "sha512-15pWY8GFLsm3w01JuSQdEIkCh1+KUKqNQg5F6s5SXhAwbj0DuTceFbyuqWFMq+XzV8zZrPt8LtYAAj5ld5IEOw==",
"requires": {
"@babel/runtime": "^7.21.0",
"media-encoder-host-broker": "^7.0.78",
"media-encoder-host-worker": "^9.1.0",
"tslib": "^2.5.0"
}
},
"media-encoder-host-broker": {
"version": "7.0.78",
"resolved": "https://registry.npmjs.org/media-encoder-host-broker/-/media-encoder-host-broker-7.0.78.tgz",
"integrity": "sha512-5IZEZwN4qyCymkP6FCELS6rRCE2xhNFiE3FWr+8R3ySRn1iwPWd/U/YFB4PdvjqFw3MtQ9rHFErfc/YjUf8j9g==",
"requires": {
"@babel/runtime": "^7.21.0",
"broker-factory": "^3.0.75",
"fast-unique-numbers": "^7.0.2",
"media-encoder-host-worker": "^9.1.0",
"tslib": "^2.5.0"
}
},
"media-encoder-host-worker": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/media-encoder-host-worker/-/media-encoder-host-worker-9.1.0.tgz",
"integrity": "sha512-dVvb65RH/5Yt5PBxBVGw+BcCFkq8A15/fw3HxEKugtaFJocTcwQsS1IeDUuf1i831Fk/FuCipnXFz8VsGRCZyg==",
"requires": {
"@babel/runtime": "^7.21.0",
"extendable-media-recorder-wav-encoder-broker": "^7.0.77",
"tslib": "^2.5.0",
"worker-factory": "^6.0.76"
}
},
"memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
@ -10765,6 +11136,15 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"multi-buffer-data-view": {
"version": "3.0.24",
"resolved": "https://registry.npmjs.org/multi-buffer-data-view/-/multi-buffer-data-view-3.0.24.tgz",
"integrity": "sha512-jm7Ycplx37ExXyQmqhwl7zfQmAj81y5LLzVx0XyWea4omP9W/xJhLEHs/5b+WojGyYSRt8BHiXZVcYzu68Ma0Q==",
"requires": {
"@babel/runtime": "^7.20.6",
"tslib": "^2.4.1"
}
},
"mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
@ -11269,6 +11649,15 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-media-recorder": {
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/react-media-recorder/-/react-media-recorder-1.6.6.tgz",
"integrity": "sha512-VdC4bUINMWJyqOAHw1DaZ8HZhdCyVBK85zJ4cHMo9tsrekui3wq5ZxNtBmNe6nbAFQBTNj/pRnLEsiVrCW+TNQ==",
"requires": {
"extendable-media-recorder": "^6.6.5",
"extendable-media-recorder-wav-encoder": "^7.0.68"
}
},
"react-player": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/react-player/-/react-player-2.12.0.tgz",
@ -11314,6 +11703,35 @@
"picomatch": "^2.2.1"
}
},
"recorder-audio-worklet": {
"version": "5.1.39",
"resolved": "https://registry.npmjs.org/recorder-audio-worklet/-/recorder-audio-worklet-5.1.39.tgz",
"integrity": "sha512-w/RazoBwZnkFnEPRsJYNThOHznLQC98/IzWRrutpJQVvCcL0nbLsVSLDaRrnrqVpRUI11VgiXRh30HaHiSdVhQ==",
"requires": {
"@babel/runtime": "^7.21.0",
"broker-factory": "^3.0.75",
"fast-unique-numbers": "^7.0.2",
"recorder-audio-worklet-processor": "^4.2.21",
"standardized-audio-context": "^25.3.41",
"subscribable-things": "^2.1.14",
"tslib": "^2.5.0",
"worker-factory": "^6.0.76"
}
},
"recorder-audio-worklet-processor": {
"version": "4.2.21",
"resolved": "https://registry.npmjs.org/recorder-audio-worklet-processor/-/recorder-audio-worklet-processor-4.2.21.tgz",
"integrity": "sha512-oiiS2sp6eMxkvjt13yetSYUJvnAxBZk60mIxz0Vf/2lDWa/4svCyMLHIDzYKbHahkISd0UYyqLS9dI7xDlUOCA==",
"requires": {
"@babel/runtime": "^7.21.0",
"tslib": "^2.5.0"
}
},
"recordrtc": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/recordrtc/-/recordrtc-5.6.2.tgz",
"integrity": "sha512-1QNKKNtl7+KcwD1lyOgP3ZlbiJ1d0HtXnypUy7yq49xEERxk31PHvE9RCciDrulPCY7WJ+oz0R9hpNxgsIurGQ=="
},
"regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
@ -11371,6 +11789,11 @@
"queue-microtask": "^1.2.2"
}
},
"rxjs-interop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/rxjs-interop/-/rxjs-interop-2.0.0.tgz",
"integrity": "sha512-ASEq9atUw7lualXB+knvgtvwkCEvGWV2gDD/8qnASzBkzEARZck9JAyxmY8OS6Nc1pCPEgDTKNcx+YqqYfzArw=="
},
"safe-regex-test": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
@ -11461,6 +11884,16 @@
"source-map": "^0.6.0"
}
},
"standardized-audio-context": {
"version": "25.3.43",
"resolved": "https://registry.npmjs.org/standardized-audio-context/-/standardized-audio-context-25.3.43.tgz",
"integrity": "sha512-Wh/6rZCScDh3ZAsRy0kgeGF+g3mGO4E6UiWj8k79uCHKJjqiBJOtbwEFS1MBFfVXMHN3L3U3kn1aVrkCHtlkvw==",
"requires": {
"@babel/runtime": "^7.21.0",
"automation-events": "^5.0.3",
"tslib": "^2.5.0"
}
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@ -11572,6 +12005,16 @@
"client-only": "0.0.1"
}
},
"subscribable-things": {
"version": "2.1.14",
"resolved": "https://registry.npmjs.org/subscribable-things/-/subscribable-things-2.1.14.tgz",
"integrity": "sha512-+0e6wGpFVa7snrwzY/xwiiQhlpBglvPAhsYK9EhFAcboP5z0eN1W3CQoROYyeY3psiCbwcleb9vaXasKAcvISw==",
"requires": {
"@babel/runtime": "^7.21.0",
"rxjs-interop": "^2.0.0",
"tslib": "^2.5.0"
}
},
"sucrase": {
"version": "3.32.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
@ -11939,6 +12382,17 @@
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
"dev": true
},
"worker-factory": {
"version": "6.0.76",
"resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-6.0.76.tgz",
"integrity": "sha512-W1iBNPmE9p0asU4aFmYJYCnMxhkvk4qlKc660GlHxWgmflY64NxxTbmKqipu4K5p9LiKKPjqXfcQme6153BZEQ==",
"requires": {
"@babel/runtime": "^7.21.0",
"compilerr": "^10.0.2",
"fast-unique-numbers": "^7.0.2",
"tslib": "^2.5.0"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",

View file

@ -28,6 +28,7 @@
"@trpc/next": "^10.18.0",
"@trpc/react-query": "^10.18.0",
"@trpc/server": "^10.18.0",
"@types/recordrtc": "^5.6.11",
"@upstash/qstash": "^0.3.6",
"axios": "^1.3.5",
"dayjs": "^1.11.7",
@ -38,8 +39,10 @@
"next-auth": "^4.21.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-media-recorder": "^1.6.6",
"react-player": "^2.12.0",
"react-popper": "^2.3.0",
"recordrtc": "^5.6.2",
"stripe": "^12.1.1",
"superjson": "1.12.2",
"zod": "^3.21.4"

170
src/components/Recorder.tsx Normal file
View file

@ -0,0 +1,170 @@
import React, { useState, useRef, Fragment } from "react";
import RecordRTC, { invokeSaveAsDialog } from "recordrtc";
import { Listbox, Transition } from "@headlessui/react";
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/20/solid";
const people = [
{ name: "Wade Cooper" },
{ name: "Arlene Mccoy" },
{ name: "Devon Webb" },
{ name: "Tom Cook" },
{ name: "Tanya Fox" },
{ name: "Hellen Schmidt" },
];
export default function Recorder() {
const [steam, setStream] = useState<null | MediaStream>(null);
const [blob, setBlob] = useState<null | Blob>(null);
const refVideo = useRef<null | HTMLVideoElement>(null);
const recorderRef = useRef<null | RecordRTC>(null);
const [pause, setPause] = useState<boolean>(false);
const [selected, setSelected] = useState<{ name: string }>(people[0]);
const handleRecording = async () => {
const mediaStream = await navigator.mediaDevices.getDisplayMedia({
video: {
width: 1920,
height: 1080,
frameRate: 30,
},
audio: {
echoCancellation: true,
noiseSuppression: true,
sampleRate: 44100,
},
});
setStream(mediaStream);
recorderRef.current = new RecordRTC(mediaStream, { type: "video" });
recorderRef.current.startRecording();
};
const handleStop = () => {
if (recorderRef.current === null) return;
recorderRef.current.stopRecording(() => {
if (recorderRef.current) {
setBlob(recorderRef.current.getBlob());
steam?.getTracks().map((track) => track.stop());
}
});
};
const handlePause = () => {
if (recorderRef.current) {
console.log(recorderRef.current?.state);
if (pause) {
recorderRef.current?.resumeRecording();
} else {
recorderRef.current.pauseRecording();
}
setPause(!pause);
}
};
const handleSave = () => {
if (blob) {
invokeSaveAsDialog(blob);
}
};
return (
<div className="App">
<header className="App-header">
<button
type="button"
className="mt-4 inline-flex items-center rounded-md bg-indigo-500 px-4 py-2 text-sm font-semibold leading-6 text-white shadow transition duration-150 ease-in-out hover:bg-indigo-400 disabled:cursor-not-allowed"
onClick={() => void handleRecording()}
>
start
</button>
<button
type="button"
className="mx-4 mt-4 inline-flex items-center rounded-md bg-indigo-500 px-4 py-2 text-sm font-semibold leading-6 text-white shadow transition duration-150 ease-in-out hover:bg-indigo-400 disabled:cursor-not-allowed"
onClick={handleStop}
>
stop
</button>
<button
type="button"
className="mx-4 mt-4 inline-flex items-center rounded-md bg-indigo-500 px-4 py-2 text-sm font-semibold leading-6 text-white shadow transition duration-150 ease-in-out hover:bg-indigo-400 disabled:cursor-not-allowed"
onClick={handlePause}
>
{pause ? "resume" : "pause"}
</button>
<button
type="button"
className="mt-4 inline-flex items-center rounded-md bg-indigo-500 px-4 py-2 text-sm font-semibold leading-6 text-white shadow transition duration-150 ease-in-out hover:bg-indigo-400 disabled:cursor-not-allowed"
onClick={handleSave}
>
save
</button>
<div className="fixed top-16 w-72">
<Listbox value={selected} onChange={setSelected}>
<div className="relative mt-1">
<Listbox.Button className="relative w-full cursor-default rounded-lg bg-white py-2 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm">
<span className="block truncate">{selected.name}</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronUpDownIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{people.map((person, personIdx) => (
<Listbox.Option
key={personIdx}
className={({ active }) =>
`relative cursor-default select-none py-2 pl-10 pr-4 ${
active
? "bg-amber-100 text-amber-900"
: "text-gray-900"
}`
}
value={person}
>
{({ selected }) => (
<>
<span
className={`block truncate ${
selected ? "font-medium" : "font-normal"
}`}
>
{person.name}
</span>
{selected ? (
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600">
<CheckIcon
className="h-5 w-5"
aria-hidden="true"
/>
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</Listbox>
</div>
{blob && (
<video
src={URL.createObjectURL(blob)}
controls
autoPlay
ref={refVideo}
style={{ width: "700px", margin: "1em" }}
/>
)}
</header>
</div>
);
}

View file

@ -3,6 +3,8 @@ import { Dialog, Transition } from "@headlessui/react";
import { api } from "~/utils/api";
import axios from "axios";
import { useRouter } from "next/router";
const Recorder = dynamic(() => import("~/components/Recorder"), { ssr: false });
import dynamic from "next/dynamic";
export default function VideoUploadModal() {
const router = useRouter();
@ -81,76 +83,80 @@ export default function VideoUploadModal() {
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-lg bg-white p-6 text-left align-middle shadow-xl transition-all">
<div className="flex flex-col items-center gap-2">
<label className="flex h-32 w-full min-w-[300px] cursor-pointer appearance-none justify-center rounded-md border-2 border-dashed border-gray-300 px-4 transition hover:border-gray-400 focus:outline-none">
<span className="flex items-center space-x-2 text-[#292D34]">
{file ? (
<span className="font-medium">{file.name}</span>
) : (
{1 + 1 === 2 ? (
<Recorder />
) : (
<div className="flex flex-col items-center gap-2">
<label className="flex h-32 w-full min-w-[300px] cursor-pointer appearance-none justify-center rounded-md border-2 border-dashed border-gray-300 px-4 transition hover:border-gray-400 focus:outline-none">
<span className="flex items-center space-x-2 text-[#292D34]">
{file ? (
<span className="font-medium">{file.name}</span>
) : (
<>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
/>
</svg>
<span className="font-medium">
{"Drop files to Attach, or browse"}
</span>
</>
)}
</span>
<input
accept="video/mp4,video/webm"
onChange={handleFileChange}
type="file"
name="file_upload"
className="hidden"
/>
</label>
<button
type="button"
className="mt-4 inline-flex items-center rounded-md bg-indigo-500 px-4 py-2 text-sm font-semibold leading-6 text-white shadow transition duration-150 ease-in-out hover:bg-indigo-400 disabled:cursor-not-allowed"
disabled={submitting}
onClick={() => void handleSubmit()}
>
{submitting ? (
<>
<svg
className="-ml-1 mr-3 h-5 w-5 animate-spin text-white"
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
/>
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
<span className="font-medium">
{"Drop files to Attach, or browse"}
</span>
Uploading...
</>
) : (
<>Upload</>
)}
</span>
<input
accept="video/mp4,video/webm"
onChange={handleFileChange}
type="file"
name="file_upload"
className="hidden"
/>
</label>
<button
type="button"
className="mt-4 inline-flex items-center rounded-md bg-indigo-500 px-4 py-2 text-sm font-semibold leading-6 text-white shadow transition duration-150 ease-in-out hover:bg-indigo-400 disabled:cursor-not-allowed"
disabled={submitting}
onClick={() => void handleSubmit()}
>
{submitting ? (
<>
<svg
className="-ml-1 mr-3 h-5 w-5 animate-spin text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
Uploading...
</>
) : (
<>Upload</>
)}
</button>
</div>
</button>
</div>
)}
</Dialog.Panel>
</Transition.Child>
</div>