Compare commits
485 Commits
3811
...
saghul-pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b8492f505 | ||
|
|
d7ed4bdd06 | ||
|
|
403c4a7ee7 | ||
|
|
21fe3c87a6 | ||
|
|
6ddac3bddf | ||
|
|
663a65ad81 | ||
|
|
5977f09202 | ||
|
|
6be2a8575f | ||
|
|
0d14e2fa5c | ||
|
|
1e0669d33b | ||
|
|
d69c2c84d7 | ||
|
|
0ea7a31b08 | ||
|
|
6284e5fab3 | ||
|
|
647852bd82 | ||
|
|
4e0d6e56bc | ||
|
|
c02763a29e | ||
|
|
0b8ec5a342 | ||
|
|
1a4be30ea1 | ||
|
|
d53d6e5fa8 | ||
|
|
93b05d13c3 | ||
|
|
79f4531bd2 | ||
|
|
5348fa19c8 | ||
|
|
b25319fd2e | ||
|
|
63ca419e76 | ||
|
|
393fb692ca | ||
|
|
735b686b98 | ||
|
|
0598e7369b | ||
|
|
8c30e43b5f | ||
|
|
75c836c70c | ||
|
|
70d8fe91c3 | ||
|
|
c6d5e103f5 | ||
|
|
7f1f92cdf0 | ||
|
|
8c02ba5ee9 | ||
|
|
5591144693 | ||
|
|
fa43539718 | ||
|
|
7e0a36e88e | ||
|
|
532dadb245 | ||
|
|
55051cc203 | ||
|
|
4975f15345 | ||
|
|
2e2d40c1d0 | ||
|
|
c08638da51 | ||
|
|
9525cab60f | ||
|
|
21d5c895fc | ||
|
|
305a63e8f9 | ||
|
|
2fef06772b | ||
|
|
b2895b7095 | ||
|
|
41dc14d30c | ||
|
|
2ddfead4f5 | ||
|
|
d55b49b2c3 | ||
|
|
64d74852e0 | ||
|
|
7c4c8384fd | ||
|
|
262e855260 | ||
|
|
3f679407a9 | ||
|
|
4faeb84d2e | ||
|
|
eed5e31ec9 | ||
|
|
4b1d8ba877 | ||
|
|
3043f50ce3 | ||
|
|
b7f1f3c659 | ||
|
|
07f64d94c0 | ||
|
|
76e1217439 | ||
|
|
591ea0a44a | ||
|
|
24052e9f9a | ||
|
|
38b8772af0 | ||
|
|
5fdb7c176c | ||
|
|
a8da6d4095 | ||
|
|
01fc098d4b | ||
|
|
b50b30e3eb | ||
|
|
1b8e5d0244 | ||
|
|
908712b96f | ||
|
|
a41bda73ff | ||
|
|
75ea0070e2 | ||
|
|
65a8091e53 | ||
|
|
7fea8e2e6c | ||
|
|
d388a7bd3c | ||
|
|
a48aa2b999 | ||
|
|
b3f16926d4 | ||
|
|
f646bc7a7a | ||
|
|
e90b60d661 | ||
|
|
bdd129b9a2 | ||
|
|
4c635a2a63 | ||
|
|
2b526557e4 | ||
|
|
eee1f50ed2 | ||
|
|
e9562adddf | ||
|
|
8accd9e433 | ||
|
|
caabdadf19 | ||
|
|
062bc13d4f | ||
|
|
a56e451536 | ||
|
|
acbf641fb4 | ||
|
|
a18fd1cdb3 | ||
|
|
9be78c60eb | ||
|
|
a45cbf41ef | ||
|
|
5b53232964 | ||
|
|
d4d4490aa9 | ||
|
|
0fd0897531 | ||
|
|
360383440e | ||
|
|
1a40672427 | ||
|
|
1ffd75c0a6 | ||
|
|
77d38731e9 | ||
|
|
13f76c2cce | ||
|
|
9ad87f3706 | ||
|
|
eea8fef044 | ||
|
|
c2cb4ea6b6 | ||
|
|
d6cf0c0afd | ||
|
|
f5faf5bbaa | ||
|
|
363982fad4 | ||
|
|
62f47d5b87 | ||
|
|
223e8560ca | ||
|
|
2085851179 | ||
|
|
47bc6f0470 | ||
|
|
c5686386fa | ||
|
|
e1cededb76 | ||
|
|
268dc33324 | ||
|
|
baaf55a2ab | ||
|
|
a8252103ea | ||
|
|
b0ab51af90 | ||
|
|
69b20cd945 | ||
|
|
8bb0141c1a | ||
|
|
ed766c6fe2 | ||
|
|
d883bae925 | ||
|
|
62d3d1f294 | ||
|
|
dbfed573ac | ||
|
|
640b9bfd52 | ||
|
|
dac6d9d63c | ||
|
|
0662da58b4 | ||
|
|
d7d4ea819f | ||
|
|
2c6cac6526 | ||
|
|
f53251f104 | ||
|
|
8e01f769d9 | ||
|
|
8c4656a3c8 | ||
|
|
a5a36aa1e1 | ||
|
|
6939939a0f | ||
|
|
c60671e008 | ||
|
|
8e4e1f139a | ||
|
|
b608051177 | ||
|
|
2dad7e920b | ||
|
|
be2d20e20e | ||
|
|
fd11c36bc3 | ||
|
|
60148b6439 | ||
|
|
62b4c97423 | ||
|
|
f08fa1e374 | ||
|
|
e5b563ba46 | ||
|
|
c41047344f | ||
|
|
6ae148403d | ||
|
|
b5676c3729 | ||
|
|
3ab6b97b8b | ||
|
|
5ea8e198c7 | ||
|
|
2ad6bfbc20 | ||
|
|
6ce27ef10d | ||
|
|
93ef8495ca | ||
|
|
94a15914d0 | ||
|
|
851976ebdf | ||
|
|
57bb2ead36 | ||
|
|
027c6ddad0 | ||
|
|
8648a5a998 | ||
|
|
c83b30558d | ||
|
|
057dfc8194 | ||
|
|
5eae0b58e9 | ||
|
|
1538107e93 | ||
|
|
cd1862a2d3 | ||
|
|
147a076f5d | ||
|
|
9bdaea4069 | ||
|
|
547d1547bb | ||
|
|
39853e048b | ||
|
|
8b454b5196 | ||
|
|
e2788e0fb2 | ||
|
|
b1d1599a1c | ||
|
|
cef98f457f | ||
|
|
6b0e8aab11 | ||
|
|
086889ed70 | ||
|
|
516e5af118 | ||
|
|
afe1b4b0f9 | ||
|
|
8790ad6013 | ||
|
|
8bbc04d4db | ||
|
|
4fda428be1 | ||
|
|
f972ebfe9e | ||
|
|
3dfcc8d80e | ||
|
|
33ebd241a9 | ||
|
|
cb6fbb0f03 | ||
|
|
0077ee29c5 | ||
|
|
551db30cc7 | ||
|
|
1bd930a3cb | ||
|
|
169906f4cb | ||
|
|
e0563a743f | ||
|
|
a4ab7ea95f | ||
|
|
b50d6e43d0 | ||
|
|
f9fcb46036 | ||
|
|
4824c8714a | ||
|
|
be56521267 | ||
|
|
b7eda8df7a | ||
|
|
d632b6e13e | ||
|
|
dff7d661ca | ||
|
|
edb8ecd542 | ||
|
|
b64112432d | ||
|
|
d8963bc903 | ||
|
|
5c39a2f6a6 | ||
|
|
a72928a9e7 | ||
|
|
e2ea26eb1f | ||
|
|
9962a2ea61 | ||
|
|
07e203ce8d | ||
|
|
638fdf0370 | ||
|
|
6ce1eaba24 | ||
|
|
4780e48be8 | ||
|
|
7776f0a98c | ||
|
|
2eede7e76b | ||
|
|
72a7bd0a68 | ||
|
|
9085cbf363 | ||
|
|
be80f26086 | ||
|
|
a876f78fd7 | ||
|
|
73d948d150 | ||
|
|
c5aa555816 | ||
|
|
7902223f09 | ||
|
|
ac117cd50d | ||
|
|
f2df5906f6 | ||
|
|
0fcecaf18f | ||
|
|
8cc4b73722 | ||
|
|
c08f5c7e18 | ||
|
|
2e0f3ae84f | ||
|
|
41bfb4c7ec | ||
|
|
4a12cdfac7 | ||
|
|
7b34fb89d1 | ||
|
|
4ec438d3f3 | ||
|
|
e79633b0ca | ||
|
|
49a9934c41 | ||
|
|
193d19ce21 | ||
|
|
fe83d87d2d | ||
|
|
b4a2327264 | ||
|
|
d9edf661dd | ||
|
|
e32f367b0c | ||
|
|
2b181673b5 | ||
|
|
87a058eaa4 | ||
|
|
eef0f5ed97 | ||
|
|
1751fc7635 | ||
|
|
4aa58f041f | ||
|
|
39d789a088 | ||
|
|
2654c77f2c | ||
|
|
7af88e5c4f | ||
|
|
ffdd4f2eed | ||
|
|
0b0a19ea5c | ||
|
|
b10aa422ca | ||
|
|
b706972acb | ||
|
|
5574221044 | ||
|
|
0f4369a9a9 | ||
|
|
0c2e13a453 | ||
|
|
2f817b6633 | ||
|
|
678ed605d7 | ||
|
|
84714ba3bd | ||
|
|
b8963629bf | ||
|
|
47cffeb00a | ||
|
|
b70633ef24 | ||
|
|
145596ac6a | ||
|
|
76607bbad8 | ||
|
|
36113fd54f | ||
|
|
f86ace17d8 | ||
|
|
0e16008085 | ||
|
|
f66a919e08 | ||
|
|
8115fb5e78 | ||
|
|
c9f26dc7ac | ||
|
|
7321f706bb | ||
|
|
9b5f135dae | ||
|
|
dbbdcb0b00 | ||
|
|
06ae1861ee | ||
|
|
e0afd8fadb | ||
|
|
953f838a2a | ||
|
|
bf5f1f0168 | ||
|
|
cdf977ff3f | ||
|
|
ee1dc9dd8e | ||
|
|
7bec68e492 | ||
|
|
97fff02c15 | ||
|
|
b372b2ccf2 | ||
|
|
d00ead932b | ||
|
|
bb2b1b58ec | ||
|
|
ceeea7314c | ||
|
|
9d6a93119b | ||
|
|
08be68cda4 | ||
|
|
3a2081ffed | ||
|
|
57d14d9517 | ||
|
|
c5e693f14a | ||
|
|
a9da047d3a | ||
|
|
171b065db1 | ||
|
|
9da0b7fee3 | ||
|
|
716c9eb46f | ||
|
|
a85c72d859 | ||
|
|
0ba567856e | ||
|
|
7f1eb83dbd | ||
|
|
6e4c1f64d8 | ||
|
|
82aa51770a | ||
|
|
990c77bd3d | ||
|
|
5bb23b2d17 | ||
|
|
9cc05ef838 | ||
|
|
a21e3a1946 | ||
|
|
c05ca1d9fc | ||
|
|
1b05d7269c | ||
|
|
c170970992 | ||
|
|
d19a659871 | ||
|
|
955b24be9d | ||
|
|
de6c7e0117 | ||
|
|
feb8fe9e34 | ||
|
|
aff6d4b36d | ||
|
|
45c60717d6 | ||
|
|
6596e27f69 | ||
|
|
8cba7e91d3 | ||
|
|
3a871cbed8 | ||
|
|
a46fd60788 | ||
|
|
86ebfe8dad | ||
|
|
97735ff548 | ||
|
|
9fdc18d1ec | ||
|
|
4a21882345 | ||
|
|
cb0cea4ebd | ||
|
|
b894daa9cf | ||
|
|
06641a7dd6 | ||
|
|
94f7b570d7 | ||
|
|
ca5605620a | ||
|
|
8d0202113a | ||
|
|
f2e59226c0 | ||
|
|
951086e499 | ||
|
|
d3a26f9b4e | ||
|
|
e1a4478a06 | ||
|
|
ed8009883b | ||
|
|
4fd5dc0ee0 | ||
|
|
1bbb937d9d | ||
|
|
c1fb276937 | ||
|
|
3867d5d62e | ||
|
|
08ab513d4e | ||
|
|
a2eca4f029 | ||
|
|
3121494d4b | ||
|
|
92e81c3dbf | ||
|
|
2761a6dbb3 | ||
|
|
faf24ca7ec | ||
|
|
c5ce44f09d | ||
|
|
6969114675 | ||
|
|
1aba57e6bb | ||
|
|
e9785c8b3d | ||
|
|
03215d8906 | ||
|
|
9a5b19babe | ||
|
|
e5d87e66bf | ||
|
|
e00036d44a | ||
|
|
d8059fd0ee | ||
|
|
6fc7456196 | ||
|
|
0bd96b9c0e | ||
|
|
f502e13edc | ||
|
|
08f55ccb94 | ||
|
|
17ca9722b7 | ||
|
|
8cc9b78e21 | ||
|
|
25b4843327 | ||
|
|
d13edd8f63 | ||
|
|
cdc14586de | ||
|
|
a7f8bf2d8f | ||
|
|
8cd881945a | ||
|
|
579d08e27e | ||
|
|
5148c81dd8 | ||
|
|
40bc844caa | ||
|
|
c990a64ba9 | ||
|
|
f5a0a1ef8c | ||
|
|
58018a086c | ||
|
|
b3a7f5f38b | ||
|
|
435d11793d | ||
|
|
9ec3faa6b3 | ||
|
|
959d61468e | ||
|
|
9f28048742 | ||
|
|
19c9a808cf | ||
|
|
7576f850e9 | ||
|
|
7970b38823 | ||
|
|
b40363be31 | ||
|
|
9a0b924f99 | ||
|
|
52def54cd9 | ||
|
|
8d3b59a0d0 | ||
|
|
b0e7471a83 | ||
|
|
4616065b1d | ||
|
|
14855f3255 | ||
|
|
c40a7f736e | ||
|
|
8ffd226159 | ||
|
|
b8dda4c6c2 | ||
|
|
fca4977987 | ||
|
|
7bcece52ea | ||
|
|
155c6de822 | ||
|
|
d0bc3da0f5 | ||
|
|
011b7f9d00 | ||
|
|
9b0f5b0299 | ||
|
|
7e21c277b3 | ||
|
|
e6ade2cf84 | ||
|
|
99b21cdb2a | ||
|
|
677752c78d | ||
|
|
4e81a7abd3 | ||
|
|
5d8d0b9a01 | ||
|
|
2dd5d0a180 | ||
|
|
ccdfff8e9a | ||
|
|
829e3bfe15 | ||
|
|
e90cc0a615 | ||
|
|
b6ccc91038 | ||
|
|
beb4487044 | ||
|
|
add84af7b6 | ||
|
|
cb0d10fbe1 | ||
|
|
9a162c266c | ||
|
|
6654531112 | ||
|
|
b9cc6b6f96 | ||
|
|
6cbc04175c | ||
|
|
fb23aa54e8 | ||
|
|
b73d98f863 | ||
|
|
0289a93e5a | ||
|
|
401003b907 | ||
|
|
025e2b1ecb | ||
|
|
05a8591110 | ||
|
|
d2e6d57be8 | ||
|
|
41fd142977 | ||
|
|
04c0945930 | ||
|
|
5f2acb70de | ||
|
|
4dc10e82f1 | ||
|
|
582d4aff1c | ||
|
|
b13200ac92 | ||
|
|
53f937ba4e | ||
|
|
b9addaed71 | ||
|
|
fd8fb58eaf | ||
|
|
38d1032fec | ||
|
|
073fdc7b0e | ||
|
|
fedaa0ae6e | ||
|
|
f06fe69167 | ||
|
|
16c09c9645 | ||
|
|
24a1a60f04 | ||
|
|
d7ece58c6f | ||
|
|
aa11535db7 | ||
|
|
eea87be801 | ||
|
|
809ac42e4c | ||
|
|
cf27ad0dde | ||
|
|
2064fc8937 | ||
|
|
95eb551156 | ||
|
|
a7ac7790a8 | ||
|
|
94c48ec838 | ||
|
|
da68b9882d | ||
|
|
bd8a7edbd2 | ||
|
|
bde2343951 | ||
|
|
9b141816d6 | ||
|
|
6a7594cd9c | ||
|
|
cdbc5976a0 | ||
|
|
8fd0f56be7 | ||
|
|
5b0c6e088a | ||
|
|
202abf2a9a | ||
|
|
fb8ef366c6 | ||
|
|
7446e6165e | ||
|
|
5feaa421a8 | ||
|
|
a4333d3a80 | ||
|
|
02131f3346 | ||
|
|
8dbd1ba1b7 | ||
|
|
4c065f2de1 | ||
|
|
df895e5a61 | ||
|
|
bbf1927c70 | ||
|
|
86130c1478 | ||
|
|
15fb8a1525 | ||
|
|
64cbfb648f | ||
|
|
3e40bb19cd | ||
|
|
bbca0fc357 | ||
|
|
9bb789472e | ||
|
|
06fa175a6c | ||
|
|
5940f2890a | ||
|
|
7d09088186 | ||
|
|
34be081d87 | ||
|
|
3c36eece5e | ||
|
|
8eb0acada6 | ||
|
|
8307a8be2d | ||
|
|
d62190c644 | ||
|
|
58c4248da0 | ||
|
|
74af70a1d5 | ||
|
|
7b25b847ba | ||
|
|
18536cb14c | ||
|
|
01e36e1c56 | ||
|
|
5d96a226ed | ||
|
|
9855fac805 | ||
|
|
ed5351d250 | ||
|
|
b64260e554 | ||
|
|
7d67cb583e | ||
|
|
8e3c301d03 | ||
|
|
f9071b8b6b | ||
|
|
01abc4e8a8 | ||
|
|
c5bddda781 | ||
|
|
c2bc92ae05 | ||
|
|
a425e9c92e | ||
|
|
3bfa4744c8 | ||
|
|
bd5901d59c | ||
|
|
306c8ba8c2 | ||
|
|
5a6335207f | ||
|
|
97e8a6c3f3 | ||
|
|
523926d598 | ||
|
|
ab5627212d | ||
|
|
1b6c5a7141 | ||
|
|
a9767eda72 | ||
|
|
6c3a5793b4 |
50
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
@@ -4,25 +4,45 @@ about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
*This Issue tracker is only for reporting bugs and tracking code related issues.*
|
||||
<!--
|
||||
|
||||
Before posting, please make sure you check community.jitsi.org to see if the same or similar bugs have already been discussed.
|
||||
General questions, installation help, and feature requests can also be posted to community.jitsi.org.
|
||||
This issue tracker is only for reporting bugs and tracking issues related to the source code.
|
||||
|
||||
## Description
|
||||
---
|
||||
Before posting, please make sure to check if the same or similar bugs have already been discussed: https://github.com/jitsi/jitsi-meet/issues
|
||||
|
||||
## Current behavior
|
||||
---
|
||||
General questions regarding usage, installation, etc. should be posted at https://community.jitsi.org. They will be closed if posted here.
|
||||
|
||||
## Expected Behavior
|
||||
---
|
||||
-->
|
||||
|
||||
## Possible Solution
|
||||
---
|
||||
### Description:
|
||||
|
||||
## Steps to reproduce
|
||||
---
|
||||
<!-- Please describe the bug clearly and concisely. -->
|
||||
|
||||
# Environment details
|
||||
---
|
||||
### Steps to reproduce:
|
||||
|
||||
1. <!-- Open '...' -->
|
||||
2. <!-- Click on '...' -->
|
||||
3. <!-- and so on... -->
|
||||
|
||||
### Expected behavior:
|
||||
|
||||
<!-- Please describe what should happen. -->
|
||||
|
||||
### Actual behavior:
|
||||
|
||||
<!-- Please describe what actually happens. -->
|
||||
<!-- Please attach screenshot if possible. -->
|
||||
|
||||
### Server information:
|
||||
|
||||
- Jitsi Meet version:
|
||||
- Operating System:
|
||||
|
||||
### Client information:
|
||||
|
||||
- Browser / app version:
|
||||
- Operating System:
|
||||
|
||||
### Additional information:
|
||||
|
||||
<!-- Please provide additional information about the bug, if any. -->
|
||||
|
||||
@@ -9,7 +9,7 @@ Thank you for suggesting an idea to make Jitsi Meet better.
|
||||
|
||||
Please fill in as much of the template below as you're able.
|
||||
|
||||
Note that the ultimate decission for implementing features lies on the Jitsi team, not all feature requests shall be accepted.
|
||||
Note that the ultimate decision for implementing features lies on the Jitsi team, not all feature requests shall be accepted.
|
||||
-->
|
||||
|
||||
**Is your feature request related to a problem you are facing?**
|
||||
10
.github/ISSUE_TEMPLATE/2-help.md
vendored
@@ -1,10 +0,0 @@
|
||||
---
|
||||
name: Need help with Jitsi Meet?
|
||||
about: Please ask it in our community at https://community.jitsi.org
|
||||
|
||||
---
|
||||
|
||||
If you have a question about Jitsi Meet that is not a bug report or feature
|
||||
request, please post it in https://community.jitsi.org
|
||||
|
||||
Questions posted to this repository will be closed.
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Need help with Jitsi Meet?
|
||||
url: https://community.jitsi.org
|
||||
about: Please ask it in our community.
|
||||
16
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: Simple CI
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
run-ci:
|
||||
name: Build Frontend
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12.x'
|
||||
- run: npm install
|
||||
- run: npm run lint
|
||||
- run: make
|
||||
@@ -27,3 +27,99 @@ in the agreement, unfortunately, we cannot accept your contribution.
|
||||
- Maintain a clean list of commits, squash them if necessary.
|
||||
- Rebase your topic branch on top of the master branch before creating the pull
|
||||
request.
|
||||
|
||||
## Coding style
|
||||
|
||||
### Comments
|
||||
|
||||
* Comments documenting the source code are required.
|
||||
|
||||
* Comments from which documentation is automatically generated are **not**
|
||||
subject to case-by-case decisions. Such comments are used, for example, on
|
||||
types and their members. Examples of tools which automatically generate
|
||||
documentation from such comments include JSDoc, Javadoc, Doxygen.
|
||||
|
||||
* Comments which are not automatically processed are strongly encouraged. They
|
||||
are subject to case-by-case decisions. Such comments are often observed in
|
||||
function bodies.
|
||||
|
||||
* Comments should be formatted as proper English sentences. Such formatting pays
|
||||
attention to, for example, capitalization and punctuation.
|
||||
|
||||
### Duplication
|
||||
|
||||
* Don't copy-paste source code. Reuse it.
|
||||
|
||||
### Formatting
|
||||
|
||||
* Line length is limited to 120 characters.
|
||||
|
||||
* Sort by alphabetical order in order to make the addition of new entities as
|
||||
easy as looking a word up in a dictionary. Otherwise, one risks duplicate
|
||||
entries (with conflicting values in the cases of key-value pairs). For
|
||||
example:
|
||||
|
||||
* Within an `import` of multiple names from a module, sort the names in
|
||||
alphabetical order. (Of course, the default name stays first as required by
|
||||
the `import` syntax.)
|
||||
|
||||
````javascript
|
||||
import {
|
||||
DOMINANT_SPEAKER_CHANGED,
|
||||
JITSI_CLIENT_CONNECTED,
|
||||
JITSI_CLIENT_CREATED,
|
||||
JITSI_CLIENT_DISCONNECTED,
|
||||
JITSI_CLIENT_ERROR,
|
||||
JITSI_CONFERENCE_JOINED,
|
||||
MODERATOR_CHANGED,
|
||||
PEER_JOINED,
|
||||
PEER_LEFT,
|
||||
RTC_ERROR
|
||||
} from './actionTypes';
|
||||
````
|
||||
|
||||
* Within a group of imports (e.g. groups of imports delimited by an empty line
|
||||
may be: third-party modules, then project modules, and eventually the
|
||||
private files of a module), sort the module names in alphabetical order.
|
||||
|
||||
````javascript
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
````
|
||||
|
||||
### Indentation
|
||||
|
||||
* Align `switch` and `case`/`default`. Don't indent the `case`/`default` more
|
||||
than its `switch`.
|
||||
|
||||
````javascript
|
||||
switch (i) {
|
||||
case 0:
|
||||
...
|
||||
break;
|
||||
default:
|
||||
...
|
||||
}
|
||||
````
|
||||
|
||||
### Naming
|
||||
|
||||
* An abstraction should have one name within the project and across multiple
|
||||
projects. For example:
|
||||
|
||||
* The instance of lib-jitsi-meet's `JitsiConnection` type should be named
|
||||
`connection` or `jitsiConnection` in jitsi-meet, not `client`.
|
||||
|
||||
* The class `ReducerRegistry` should be defined in ReducerRegistry.js and its
|
||||
imports in other files should use the same name. Don't define the class
|
||||
`Registry` in ReducerRegistry.js and then import it as `Reducers` in other
|
||||
files.
|
||||
|
||||
* The names of global constants (including ES6 module-global constants) should
|
||||
be written in uppercase with underscores to separate words. For example,
|
||||
`BACKGROUND_COLOR`.
|
||||
|
||||
* The underscore character at the beginning of a name signals that the
|
||||
respective variable, function, property is non-public i.e. private, protected,
|
||||
or internal. In contrast, the lack of an underscore at the beginning of a name
|
||||
signals public API.
|
||||
|
||||
4
Makefile
@@ -21,6 +21,7 @@ compile:
|
||||
clean:
|
||||
rm -fr $(BUILD_DIR)
|
||||
|
||||
.NOTPARALLEL:
|
||||
deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-lib-jitsi-meet deploy-libflac deploy-css deploy-local
|
||||
|
||||
deploy-init:
|
||||
@@ -79,12 +80,13 @@ deploy-css:
|
||||
deploy-local:
|
||||
([ ! -x deploy-local.sh ] || ./deploy-local.sh)
|
||||
|
||||
.NOTPARALLEL:
|
||||
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-lib-jitsi-meet deploy-libflac
|
||||
$(WEBPACK_DEV_SERVER)
|
||||
|
||||
source-package:
|
||||
mkdir -p source_package/jitsi-meet/css && \
|
||||
cp -r *.js *.html connection_optimization favicon.ico fonts images libs static sounds LICENSE lang source_package/jitsi-meet && \
|
||||
cp -r *.js *.html resources/*.txt connection_optimization favicon.ico fonts images libs static sounds LICENSE lang source_package/jitsi-meet && \
|
||||
cp css/all.css source_package/jitsi-meet/css && \
|
||||
(cd source_package ; tar cjf ../jitsi-meet.tar.bz2 jitsi-meet) && \
|
||||
rm -rf source_package
|
||||
|
||||
34
README.md
@@ -1,8 +1,8 @@
|
||||
# Jitsi Meet - Secure, Simple and Scalable Video Conferences
|
||||
|
||||
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, [secure](#security) and scalable video conferences. Jitsi Meet in action can be seen at [here at the session #482 of the VoIP Users Conference](http://youtu.be/7vFUVClsNh0).
|
||||
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, [secure](https://jitsi.org/security) and scalable video conferences. Jitsi Meet in action can be seen at [here at the session #482 of the VoIP Users Conference](http://youtu.be/7vFUVClsNh0).
|
||||
|
||||
The Jitsi Meet client runs in your browser, without installing anything on your computer. You can try it out at https://meet.jit.si .
|
||||
The Jitsi Meet client runs in your browser, without installing anything else on your computer. You can try it out at https://meet.jit.si.
|
||||
|
||||
Jitsi Meet allows very efficient collaboration. Users can stream their desktop or only some windows. It also supports shared document editing with Etherpad.
|
||||
|
||||
@@ -10,9 +10,11 @@ Jitsi Meet allows very efficient collaboration. Users can stream their desktop o
|
||||
|
||||
On the client side, no installation is necessary. You just point your browser to the URL of your deployment. This section is about installing a Jitsi Meet suite on your server and hosting your own conferencing service.
|
||||
|
||||
Installing Jitsi Meet is a simple experience. For Debian-based system, following the [quick-install](https://github.com/jitsi/jitsi-meet/blob/master/doc/quick-install.md) document, which uses the package system. You can also see a demonstration of the process in [this tutorial video](https://jitsi.org/tutorial).
|
||||
Installing Jitsi Meet is a simple experience. For Debian-based system, following the [quick install](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart) document, which uses the package system. You can also see a demonstration of the process in [this tutorial video](https://jitsi.org/tutorial).
|
||||
|
||||
For other systems, or if you wish to install all components manually, see the [detailed manual installation instructions](https://github.com/jitsi/jitsi-meet/blob/master/doc/manual-install.md).
|
||||
For other systems, or if you wish to install all components manually, see the [detailed manual installation instructions](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-manual).
|
||||
|
||||
Installation with Docker is also available. Please see the [instruction](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker).
|
||||
|
||||
## Download
|
||||
|
||||
@@ -46,9 +48,13 @@ You can also sign up for our open beta testing here:
|
||||
* [Android](https://play.google.com/apps/testing/org.jitsi.meet)
|
||||
* [iOS](https://testflight.apple.com/join/isy6ja7S)
|
||||
|
||||
## Release notes
|
||||
|
||||
Release notes for Jitsi Meet are maintained on [this repository](https://github.com/jitsi/jitsi-meet-release-notes).
|
||||
|
||||
## Development
|
||||
|
||||
For web development see [here](doc/development.md), and for mobile see [here](doc/mobile.md).
|
||||
For web development see [here](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-web), and for mobile see [here](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-mobile).
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -61,25 +67,13 @@ Jitsi Meet provides a very flexible way of embedding in external applications by
|
||||
|
||||
## Security
|
||||
|
||||
WebRTC does not (yet) provide a way of conducting multi-party conversations with end-to-end encryption.
|
||||
Unless you consistently compare DTLS fingerprints with your peers vocally, the same goes for one-to-one calls.
|
||||
As a result, your stream is encrypted on the network but decrypted on the machine that hosts the bridge when using Jitsi Meet.
|
||||
The security section here was starting to feel a bit too succinct for the complexity of the topic, so we created a post that covers the topic much more broadly here: https://jitsi.org/security
|
||||
|
||||
The Jitsi Meet architecture allows you to deploy your own version, including
|
||||
all server components. In that case, your security guarantees will be roughly
|
||||
equivalent to a direct one-to-one WebRTC call. This is the uniqueness of
|
||||
Jitsi Meet in terms of security.
|
||||
|
||||
The [meet.jit.si](https://meet.jit.si) service is maintained by the Jitsi team
|
||||
at [8x8](https://8x8.com).
|
||||
The section on end-to-end encryption in that document is likely going to be one of the key points of interest: https://jitsi.org/security/#e2ee
|
||||
|
||||
## Security issues
|
||||
|
||||
We take security very seriously and develop all Jitsi projects to be secure and safe.
|
||||
|
||||
If you find (or simply suspect) a security issue in any of the Jitsi projects, please send us an email to security@jitsi.org.
|
||||
|
||||
**We encourage responsible disclosure for the sake of our users, so please reach out before posting in a public space.**
|
||||
For information on reporting security vulnerabilities in Jitsi Meet, see [SECURITY.md](./SECURITY.md).
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
---
|
||||
name: Security issues
|
||||
about: Please email security@jitsi.org
|
||||
# Security
|
||||
|
||||
---
|
||||
## Reporting security issuess
|
||||
|
||||
We take security very seriously and develop all Jitsi projects to be secure and safe.
|
||||
|
||||
If you find (or simply suspect) a security issue in any of the Jitsi projects, please send us an email to security@jitsi.org.
|
||||
|
||||
We encourage responsible disclosure for the sake of our users, so please reach out before posting in a public space.
|
||||
**We encourage responsible disclosure for the sake of our users, so please reach out before posting in a public space.**
|
||||
@@ -1,380 +1,3 @@
|
||||
# Jitsi Meet SDK for Android
|
||||
|
||||
## Sample applications using the SDK
|
||||
|
||||
If you want to see how easy integrating the Jitsi Meet SDK into a native application is, take a look at the
|
||||
[sample applications repository](https://github.com/jitsi/jitsi-meet-sdk-samples).
|
||||
|
||||
## Build your own, or use a pre-build SDK artifacts/binaries
|
||||
|
||||
Jitsi conveniently provides a pre-build SDK artifacts/binaries in its Maven repository. When you do not require any
|
||||
modification to the SDK itself or any of its dependencies, it's suggested to use the pre-build SDK. This avoids the
|
||||
complexity of building and installing your own SDK artifacts/binaries.
|
||||
|
||||
### Use pre-build SDK artifacts/binaries
|
||||
|
||||
In your project, add the Maven repository
|
||||
`https://github.com/jitsi/jitsi-maven-repository/raw/master/releases` and the
|
||||
dependency `org.jitsi.react:jitsi-meet-sdk` into your `build.gradle` files.
|
||||
|
||||
The repository typically goes into the `build.gradle` file in the root of your project:
|
||||
|
||||
```gradle
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Dependency definitions belong in the individual module `build.gradle` files:
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
// (other dependencies)
|
||||
implementation ('org.jitsi.react:jitsi-meet-sdk:2.+') { transitive = true }
|
||||
}
|
||||
```
|
||||
|
||||
### Build and use your own SDK artifacts/binaries
|
||||
|
||||
<details>
|
||||
<summary>Show building instructions</summary>
|
||||
|
||||
Start by making sure that your development environment [is set up correctly](https://github.com/jitsi/jitsi-meet/blob/master/doc/mobile.md).
|
||||
|
||||
A note on dependencies: Apart from the SDK, Jitsi also publishes a binary Maven artifact for some of the SDK dependencies (that are not otherwise publicly available) to the Jitsi Maven repository. When you're planning to use a SDK that is built from source, you'll likely use a version of the source code that is newer (or at least _different_) than the version of the source that was used to create the binary SDK artifact. As a consequence, the dependencies that your project will need, might also be different from those that are published in the Jitsi Maven repository. This might lead to build problems, caused by dependencies that are unavailable.
|
||||
|
||||
If you want to use a SDK that is built from source, you will likely benefit from composing a local Maven repository that contains these dependencies. The text below describes how you create a repository that includes both the SDK as well as these dependencies. For illustration purposes, we'll define the location of this local Maven repository as `/tmp/repo`
|
||||
|
||||
In source code form, the Android SDK dependencies are locked/pinned by package.json and package-lock.json of the Jitsi Meet project. To obtain the data, execute NPM in the jitsi-meet project directory:
|
||||
|
||||
npm install
|
||||
|
||||
This will pull in the dependencies in either binary format, or in source code format, somewhere under /node_modules/
|
||||
|
||||
Third-party React Native _modules_, which Jitsi Meet SDK for Android depends on, are download by NPM in source code
|
||||
or binary form. These need to be assembled into Maven artifacts, and then published to your local Maven repository.
|
||||
A script is provided to facilitate this. From the root of the jitsi-meet project repository, run:
|
||||
|
||||
./android/scripts/release-sdk.sh /tmp/repo
|
||||
|
||||
This will build and publish the SDK, and all of its dependencies to the specified Maven repository (`/tmp/repo`) in
|
||||
this example.
|
||||
|
||||
You're now ready to use the artifacts. In _your_ project, add the Maven repository that you used above (`/tmp/repo`) into your top-level `build.gradle` file:
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
maven { url "file:/tmp/repo" }
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
You can use your local repository to replace the Jitsi repository (`maven { url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases" }`) when you published _all_ subprojects. If you didn't do that, you'll have to add both repositories. Make sure your local repository is listed first!
|
||||
|
||||
Then, define the dependency `org.jitsi.react:jitsi-meet-sdk` into the `build.gradle` file of your module:
|
||||
|
||||
implementation ('org.jitsi.react:jitsi-meet-sdk:+') { transitive = true }
|
||||
|
||||
Note that there should not be a need to explicitly add the other dependencies, as they will be pulled in as transitive
|
||||
dependencies of `jitsi-meet-sdk`.
|
||||
|
||||
</details>
|
||||
|
||||
## Using the API
|
||||
|
||||
Jitsi Meet SDK is an Android library which embodies the whole Jitsi Meet
|
||||
experience and makes it reusable by third-party apps.
|
||||
|
||||
First, add Java 1.8 compatibility support to your project by adding the
|
||||
following lines into your `build.gradle` file:
|
||||
|
||||
```
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
```
|
||||
|
||||
To get started, extends your `android.app.Activity` from
|
||||
`org.jitsi.meet.sdk.JitsiMeetActivity`:
|
||||
|
||||
```java
|
||||
package org.jitsi.example;
|
||||
|
||||
import org.jitsi.meet.sdk.JitsiMeetActivity;
|
||||
|
||||
public class MainActivity extends JitsiMeetActivity {
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, you can use the `org.jitsi.meet.sdk.JitsiMeetView` class which
|
||||
extends `android.view.View`.
|
||||
|
||||
Note that this should only be needed when `JitsiMeetActivity` cannot be used for
|
||||
some reason. Extending `JitsiMeetView` requires manual wiring of the view to
|
||||
the activity, using a lot of boilerplate code. Using the Activity instead of the
|
||||
View is strongly recommended.
|
||||
|
||||
<details>
|
||||
<summary>Show example</summary>
|
||||
|
||||
```java
|
||||
package org.jitsi.example;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import org.jitsi.meet.sdk.JitsiMeetView;
|
||||
import org.jitsi.meet.sdk.ReactActivityLifecycleCallbacks;
|
||||
|
||||
// Example
|
||||
//
|
||||
public class MainActivity extends FragmentActivity implements JitsiMeetActivityInterface {
|
||||
private JitsiMeetView view;
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(
|
||||
int requestCode,
|
||||
int resultCode,
|
||||
Intent data) {
|
||||
JitsiMeetActivityDelegate.onActivityResult(
|
||||
this, requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
JitsiMeetActivityDelegate.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
view = new JitsiMeetView(this);
|
||||
JitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions.Builder()
|
||||
.setRoom("https://meet.jit.si/test123")
|
||||
.build();
|
||||
view.join(options);
|
||||
|
||||
setContentView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
view.dispose();
|
||||
view = null;
|
||||
|
||||
JitsiMeetActivityDelegate.onHostDestroy(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
JitsiMeetActivityDelegate.onNewIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
final int requestCode,
|
||||
final String[] permissions,
|
||||
final int[] grantResults) {
|
||||
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
JitsiMeetActivityDelegate.onHostResume(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
||||
JitsiMeetActivityDelegate.onHostPause(this);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### JitsiMeetActivity
|
||||
|
||||
This class encapsulates a high level API in the form of an Android `FragmentActivity`
|
||||
which displays a single `JitsiMeetView`. You can pass a URL as a `ACTION_VIEW`
|
||||
on the Intent when starting it and it will join the conference, and will be
|
||||
automatically terminated (finish() will be called on the activity) when the
|
||||
conference ends or fails.
|
||||
|
||||
### JitsiMeetView
|
||||
|
||||
The `JitsiMeetView` class is the core of Jitsi Meet SDK. It's designed to
|
||||
display a Jitsi Meet conference (or a welcome page).
|
||||
|
||||
#### join(options)
|
||||
|
||||
Joins the conference specified by the given `JitsiMeetConferenceOptions`.
|
||||
|
||||
#### leave()
|
||||
|
||||
Leaves the currently active conference. If the welcome page is enabled it will
|
||||
go back to it, otherwise a black window will be shown.
|
||||
|
||||
#### dispose()
|
||||
|
||||
Releases all resources associated with this view. This method MUST be called
|
||||
when the Activity holding this view is going to be destroyed, usually in the
|
||||
`onDestroy()` method.
|
||||
|
||||
#### getListener()
|
||||
|
||||
Returns the `JitsiMeetViewListener` instance attached to the view.
|
||||
|
||||
#### setListener(listener)
|
||||
|
||||
Sets the given listener (class implementing the `JitsiMeetViewListener`
|
||||
interface) on the view.
|
||||
|
||||
### JitsiMeetConferenceOptions
|
||||
|
||||
This object encapsulates all the options that can be tweaked when joining
|
||||
a conference.
|
||||
|
||||
Example:
|
||||
|
||||
```java
|
||||
JitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions.Builder()
|
||||
.setServerURL(new URL("https://meet.jit.si"))
|
||||
.setRoom("test123")
|
||||
.setAudioMuted(false)
|
||||
.setVideoMuted(false)
|
||||
.setAudioOnly(false)
|
||||
.setWelcomePageEnabled(false)
|
||||
.build();
|
||||
```
|
||||
|
||||
See the `JitsiMeetConferenceOptions` implementation for all available options.
|
||||
|
||||
### JitsiMeetActivityDelegate
|
||||
|
||||
This class handles the interaction between `JitsiMeetView` and its enclosing
|
||||
`Activity`. Generally this shouldn't be consumed by users, because they'd be
|
||||
using `JitsiMeetActivity` instead, which is already completely integrated.
|
||||
|
||||
All its methods are static.
|
||||
|
||||
#### onActivityResult(...)
|
||||
|
||||
Helper method to handle results of auxiliary activities launched by the SDK.
|
||||
Should be called from the activity method of the same name.
|
||||
|
||||
#### onBackPressed()
|
||||
|
||||
Helper method which should be called from the activity's `onBackPressed` method.
|
||||
If this function returns `true`, it means the action was handled and thus no
|
||||
extra processing is required; otherwise the app should call the parent's
|
||||
`onBackPressed` method.
|
||||
|
||||
#### onHostDestroy(...)
|
||||
|
||||
Helper method which should be called from the activity's `onDestroy` method.
|
||||
|
||||
#### onHostResume(...)
|
||||
|
||||
Helper method which should be called from the activity's `onResume` or `onStop`
|
||||
method.
|
||||
|
||||
#### onHostStop(...)
|
||||
|
||||
Helper method which should be called from the activity's `onSstop` method.
|
||||
|
||||
#### onNewIntent(...)
|
||||
|
||||
Helper method for integrating the *deep linking* functionality. If your app's
|
||||
activity is launched in "singleTask" mode this method should be called from the
|
||||
activity's `onNewIntent` method.
|
||||
|
||||
#### onRequestPermissionsResult(...)
|
||||
|
||||
Helper method to handle permission requests inside the SDK. It should be called
|
||||
from the activity method of the same name.
|
||||
|
||||
#### onUserLeaveHint()
|
||||
|
||||
Helper method for integrating automatic Picture-in-Picture. It should be called
|
||||
from the activity's `onUserLeaveHint` method.
|
||||
|
||||
This is a static method.
|
||||
|
||||
#### JitsiMeetViewListener
|
||||
|
||||
`JitsiMeetViewListener` provides an interface apps can implement to listen to
|
||||
the state of the Jitsi Meet conference displayed in a `JitsiMeetView`.
|
||||
|
||||
#### onConferenceJoined
|
||||
|
||||
Called when a conference was joined.
|
||||
|
||||
The `data` `Map` contains a "url" key with the conference URL.
|
||||
|
||||
#### onConferenceTerminated
|
||||
|
||||
Called when a conference was terminated either by user choice or due to a
|
||||
failure.
|
||||
|
||||
The `data` `Map` contains an "error" key with the error and a "url" key
|
||||
with the conference URL. If the conference finished gracefully no `error`
|
||||
key will be present.
|
||||
|
||||
#### onConferenceWillJoin
|
||||
|
||||
Called before a conference is joined.
|
||||
|
||||
The `data` `Map` contains a "url" key with the conference URL.
|
||||
|
||||
## ProGuard rules
|
||||
|
||||
When using the SDK on a project some proguard rules have to be added in order
|
||||
to avoid necessary code being stripped. Add the following to your project's
|
||||
rules file: https://github.com/jitsi/jitsi-meet/blob/master/android/app/proguard-rules.pro
|
||||
|
||||
## Picture-in-Picture
|
||||
|
||||
`JitsiMeetView` will automatically adjust its UI when presented in a
|
||||
Picture-in-Picture style scenario, in a rectangle too small to accommodate its
|
||||
"full" UI.
|
||||
|
||||
## Dropbox integration
|
||||
|
||||
To setup the Dropbox integration, follow these steps:
|
||||
|
||||
1. Add the following to the app's AndroidManifest.xml and change `<APP_KEY>` to
|
||||
your Dropbox app key:
|
||||
```
|
||||
<activity
|
||||
android:configChanges="keyboard|orientation"
|
||||
android:launchMode="singleTask"
|
||||
android:name="com.dropbox.core.android.AuthActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="db-<APP_KEY>" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
```
|
||||
|
||||
2. Add the following to the app's strings.xml and change `<APP_KEY>` to your
|
||||
Dropbox app key:
|
||||
```
|
||||
<string name="dropbox_app_key"><APP_KEY></string>
|
||||
```
|
||||
This document has been moved to [The Handbook](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-android-sdk).
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
boolean googleServicesEnabled \
|
||||
= project.file('google-services.json').exists() && !rootProject.ext.libreBuild
|
||||
|
||||
// Crashlytics integration is done as part of Firebase now, so it gets
|
||||
// automagically activated with google-services.json
|
||||
if (googleServicesEnabled) {
|
||||
@@ -13,7 +10,7 @@ if (googleServicesEnabled) {
|
||||
// This lets us upload a new build at most every 10 seconds for the
|
||||
// next ~680 years.
|
||||
// https://stackoverflow.com/a/38643838
|
||||
def vcode = (int)(((new Date().getTime()/1000) - 1546297200) / 10)
|
||||
def vcode = (int) (((new Date().getTime() / 1000) - 1546297200) / 10)
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
@@ -143,8 +140,8 @@ gradle.projectsEvaluated {
|
||||
def targetName = variant.name.capitalize()
|
||||
|
||||
def currentRunPackagerTask = tasks.create(
|
||||
name: "run${targetName}ReactPackager",
|
||||
type: Exec) {
|
||||
name: "run${targetName}ReactPackager",
|
||||
type: Exec) {
|
||||
group = "react"
|
||||
description = "Run the React packager."
|
||||
|
||||
@@ -175,5 +172,5 @@ gradle.projectsEvaluated {
|
||||
}
|
||||
|
||||
if (googleServicesEnabled) {
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.crashlytics.android.Crashlytics;
|
||||
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
|
||||
import io.fabric.sdk.android.Fabric;
|
||||
|
||||
import org.jitsi.meet.sdk.JitsiMeet;
|
||||
import org.jitsi.meet.sdk.JitsiMeetActivity;
|
||||
|
||||
/**
|
||||
@@ -21,7 +22,9 @@ final class GoogleServicesHelper {
|
||||
if (BuildConfig.GOOGLE_SERVICES_ENABLED) {
|
||||
Log.d(activity.getClass().getSimpleName(), "Initializing Google Services");
|
||||
|
||||
Fabric.with(activity, new Crashlytics());
|
||||
if (!JitsiMeet.isCrashReportingDisabled(activity)) {
|
||||
Fabric.with(activity, new Crashlytics());
|
||||
}
|
||||
|
||||
FirebaseDynamicLinks.getInstance().getDynamicLink(activity.getIntent())
|
||||
.addOnSuccessListener(activity, pendingDynamicLinkData -> {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 2017-2018 Atlassian Pty Ltd
|
||||
* Copyright @ 2017-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -91,6 +90,7 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
= new JitsiMeetConferenceOptions.Builder()
|
||||
.setWelcomePageEnabled(true)
|
||||
.setServerURL(buildURL("https://meet.jit.si"))
|
||||
.setFeatureFlag("call-integration.enabled", false)
|
||||
.build();
|
||||
JitsiMeet.setDefaultConferenceOptions(defaultOptions);
|
||||
|
||||
|
||||
@@ -161,6 +161,8 @@ ext {
|
||||
|
||||
// Libre build
|
||||
libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
|
||||
|
||||
googleServicesEnabled = project.file('app/google-services.json').exists() && !libreBuild
|
||||
}
|
||||
|
||||
// Force the version of the Android build tools we have chosen on all
|
||||
|
||||
|
Before Width: | Height: | Size: 512 KiB After Width: | Height: | Size: 342 KiB |
|
Before Width: | Height: | Size: 396 KiB After Width: | Height: | Size: 264 KiB |
|
Before Width: | Height: | Size: 265 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 4.0 MiB After Width: | Height: | Size: 3.1 MiB |
|
Before Width: | Height: | Size: 6.0 MiB After Width: | Height: | Size: 4.9 MiB |
|
Before Width: | Height: | Size: 948 KiB After Width: | Height: | Size: 694 KiB |
|
Before Width: | Height: | Size: 983 KiB After Width: | Height: | Size: 716 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 950 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 2.7 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1000 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.1 MiB |
@@ -20,5 +20,5 @@
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
appVersion=20.0.0
|
||||
sdkVersion=2.6.0
|
||||
appVersion=20.3.0
|
||||
sdkVersion=2.8.2
|
||||
|
||||
@@ -9,8 +9,8 @@ THE_MVN_REPO=${MVN_REPO:-${1:-$DEFAULT_MVN_REPO}}
|
||||
MVN_HTTP=0
|
||||
DEFAULT_SDK_VERSION=$(grep sdkVersion ${THIS_DIR}/../gradle.properties | cut -d"=" -f2)
|
||||
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
|
||||
RN_VERSION=$(jq -r '.dependencies."react-native"' ${THIS_DIR}/../../package.json)
|
||||
JSC_VERSION="r"$(jq -r '.dependencies."jsc-android"' ${THIS_DIR}/../../node_modules/react-native/package.json | cut -d . -f 1 | cut -c 2-)
|
||||
RN_VERSION=$(jq -r '.version' ${THIS_DIR}/../../node_modules/react-native/package.json)
|
||||
HERMES_VERSION=$(jq -r '.dependencies."hermes-engine"' ${THIS_DIR}/../../node_modules/react-native/package.json | cut -c 2-)
|
||||
DO_GIT_TAG=${GIT_TAG:-0}
|
||||
|
||||
if [[ $THE_MVN_REPO == http* ]]; then
|
||||
@@ -38,17 +38,19 @@ if [[ $MVN_HTTP == 1 ]]; then
|
||||
-DgeneratePom=false \
|
||||
-DpomFile=react-native-${RN_VERSION}.pom || true
|
||||
popd
|
||||
# Push JSC
|
||||
echo "Pushing JSC ${JSC_VERSION} to the Maven repo"
|
||||
pushd ${THIS_DIR}/../../node_modules/jsc-android/dist/org/webkit/android-jsc/${JSC_VERSION}
|
||||
# Push Hermes
|
||||
echo "Pushing Hermes ${HERMES_VERSION} to the Maven repo"
|
||||
pushd ${THIS_DIR}/../../node_modules/hermes-engine/android/
|
||||
mvn \
|
||||
deploy:deploy-file \
|
||||
-Durl=${MVN_REPO} \
|
||||
-DrepositoryId=${MVN_REPO_ID} \
|
||||
-Dfile=android-jsc-${JSC_VERSION}.aar \
|
||||
-Dfile=hermes-release.aar \
|
||||
-Dpackaging=aar \
|
||||
-DgeneratePom=false \
|
||||
-DpomFile=android-jsc-${JSC_VERSION}.pom || true
|
||||
-DgroupId=com.facebook \
|
||||
-DartifactId=hermes \
|
||||
-Dversion=${HERMES_VERSION} \
|
||||
-DgeneratePom=true || true
|
||||
popd
|
||||
else
|
||||
# Push React Native, if necessary
|
||||
@@ -65,17 +67,19 @@ else
|
||||
popd
|
||||
fi
|
||||
|
||||
# Push JSC, if necessary
|
||||
if [[ ! -d ${MVN_REPO}/org/webkit/android-jsc/${JSC_VERSION} ]]; then
|
||||
echo "Pushing JSC ${JSC_VERSION} to the Maven repo"
|
||||
pushd ${THIS_DIR}/../../node_modules/jsc-android/dist/org/webkit/android-jsc/${JSC_VERSION}
|
||||
# Push Hermes, if necessary
|
||||
if [[ ! -d ${MVN_REPO}/com/facebook/hermes/${HERMES_VERSION} ]]; then
|
||||
echo "Pushing Hermes ${HERMES_VERSION} to the Maven repo"
|
||||
pushd ${THIS_DIR}/../../node_modules/hermes-engine/android/
|
||||
mvn \
|
||||
deploy:deploy-file \
|
||||
-Durl=${MVN_REPO} \
|
||||
-Dfile=android-jsc-${JSC_VERSION}.aar \
|
||||
-Dfile=hermes-release.aar \
|
||||
-Dpackaging=aar \
|
||||
-DgeneratePom=false \
|
||||
-DpomFile=android-jsc-${JSC_VERSION}.pom
|
||||
-DgroupId=com.facebook \
|
||||
-DartifactId=hermes \
|
||||
-Dversion=${HERMES_VERSION} \
|
||||
-DgeneratePom=true
|
||||
popd
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
@@ -12,11 +14,13 @@ android {
|
||||
buildTypes {
|
||||
debug {
|
||||
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
|
||||
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${rootProject.ext.googleServicesEnabled}"
|
||||
}
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
|
||||
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${rootProject.ext.googleServicesEnabled}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +35,10 @@ android {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
pickFirst '**/libc++_shared.so'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -38,7 +46,7 @@ dependencies {
|
||||
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.fragment:fragment:1.1.0'
|
||||
implementation 'androidx.fragment:fragment:1.2.0'
|
||||
|
||||
//noinspection GradleDynamicVersion
|
||||
api 'com.facebook.react:react-native:+'
|
||||
@@ -64,6 +72,7 @@ dependencies {
|
||||
implementation project(':react-native-calendar-events')
|
||||
implementation project(':react-native-community-async-storage')
|
||||
implementation project(':react-native-community_netinfo')
|
||||
implementation project(':react-native-default-preference')
|
||||
implementation project(':react-native-immersive')
|
||||
implementation project(':react-native-keep-awake')
|
||||
implementation project(':react-native-linear-gradient')
|
||||
@@ -218,6 +227,14 @@ publishing {
|
||||
dependency.appendNode('artifactId', artifactId)
|
||||
dependency.appendNode('version', it.moduleVersion)
|
||||
}
|
||||
|
||||
// Add Hermes dependency.
|
||||
def hermesPkg = new File("$rootDir/../node_modules/hermes-engine/package.json")
|
||||
def hermesVersion = new JsonSlurper().parseText(hermesPkg.text).version
|
||||
def hermesDependency = dependencies.appendNode('dependency')
|
||||
hermesDependency.appendNode('groupId', "com.facebook")
|
||||
hermesDependency.appendNode('artifactId', "hermes")
|
||||
hermesDependency.appendNode('version', hermesVersion)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
@@ -40,6 +42,8 @@ class AmplitudeModule
|
||||
extends ReactContextBaseJavaModule {
|
||||
|
||||
public static final String NAME = "Amplitude";
|
||||
public static final String JITSI_PREFERENCES = "jitsi-preferences";
|
||||
public static final String AMPLITUDE_DEVICE_ID_KEY = "amplitudeDeviceId";
|
||||
|
||||
public AmplitudeModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
@@ -58,10 +62,14 @@ class AmplitudeModule
|
||||
Amplitude.getInstance(instanceName).initialize(getCurrentActivity(), apiKey);
|
||||
|
||||
// Set the device ID to something consistent.
|
||||
String android_id
|
||||
= Settings.Secure.getString(getReactApplicationContext().getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||
SharedPreferences sharedPreferences = getReactApplicationContext().getSharedPreferences(JITSI_PREFERENCES, Context.MODE_PRIVATE);
|
||||
String android_id = sharedPreferences.getString(AMPLITUDE_DEVICE_ID_KEY, "");
|
||||
if (!TextUtils.isEmpty(android_id)) {
|
||||
Amplitude.getInstance(instanceName).setDeviceId(android_id);
|
||||
} else {
|
||||
String amplitudeId = Amplitude.getInstance(instanceName).getDeviceId();
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString(JITSI_PREFERENCES, amplitudeId).apply();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ class AppInfoModule
|
||||
"version",
|
||||
packageInfo == null ? "" : packageInfo.versionName);
|
||||
constants.put("LIBRE_BUILD", BuildConfig.LIBRE_BUILD);
|
||||
constants.put("GOOGLE_SERVICES_ENABLED", BuildConfig.GOOGLE_SERVICES_ENABLED);
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,8 @@ class AudioDeviceHandlerConnectionService implements
|
||||
*/
|
||||
private int supportedRouteMask = -1;
|
||||
|
||||
public AudioDeviceHandlerConnectionService() {
|
||||
public AudioDeviceHandlerConnectionService(AudioManager audioManager) {
|
||||
this.audioManager = audioManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,11 +137,10 @@ class AudioDeviceHandlerConnectionService implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Context context, AudioModeModule audioModeModule) {
|
||||
public void start(AudioModeModule audioModeModule) {
|
||||
JitsiMeetLogger.i("Using " + TAG + " as the audio device handler");
|
||||
|
||||
module = audioModeModule;
|
||||
audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
|
||||
|
||||
RNConnectionService rcs = ReactInstanceManagerHolder.getNativeModule(RNConnectionService.class);
|
||||
if (rcs != null) {
|
||||
|
||||
@@ -117,7 +117,8 @@ class AudioDeviceHandlerGeneric implements
|
||||
}
|
||||
};
|
||||
|
||||
public AudioDeviceHandlerGeneric() {
|
||||
public AudioDeviceHandlerGeneric(AudioManager audioManager) {
|
||||
this.audioManager = audioManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,11 +179,10 @@ class AudioDeviceHandlerGeneric implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Context context, AudioModeModule audioModeModule) {
|
||||
public void start(AudioModeModule audioModeModule) {
|
||||
JitsiMeetLogger.i("Using " + TAG + " as the audio device handler");
|
||||
|
||||
module = audioModeModule;
|
||||
audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
|
||||
|
||||
// Setup runtime device change detection.
|
||||
audioManager.registerAudioDeviceCallback(audioDeviceCallback, null);
|
||||
|
||||
@@ -59,7 +59,8 @@ class AudioDeviceHandlerLegacy implements
|
||||
*/
|
||||
private BluetoothHeadsetMonitor bluetoothHeadsetMonitor;
|
||||
|
||||
public AudioDeviceHandlerLegacy() {
|
||||
public AudioDeviceHandlerLegacy(AudioManager audioManager) {
|
||||
this.audioManager = audioManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,11 +156,11 @@ class AudioDeviceHandlerLegacy implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Context context, AudioModeModule audioModeModule) {
|
||||
public void start(AudioModeModule audioModeModule) {
|
||||
JitsiMeetLogger.i("Using " + TAG + " as the audio device handler");
|
||||
|
||||
module = audioModeModule;
|
||||
audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
|
||||
Context context = module.getContext();
|
||||
|
||||
// Setup runtime device change detection.
|
||||
//
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
@@ -85,6 +86,12 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
return supportsConnectionService && useConnectionService_;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AudioManager} instance used to interact with the Android audio
|
||||
* subsystem.
|
||||
*/
|
||||
private AudioManager audioManager;
|
||||
|
||||
private AudioDeviceHandlerInterface audioDeviceHandler;
|
||||
|
||||
/**
|
||||
@@ -136,6 +143,8 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
*/
|
||||
public AudioModeModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
|
||||
audioManager = (AudioManager)reactContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,7 +207,12 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
setAudioDeviceHandler();
|
||||
runInAudioThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setAudioDeviceHandler();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setAudioDeviceHandler() {
|
||||
@@ -207,14 +221,14 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
|
||||
if (useConnectionService()) {
|
||||
audioDeviceHandler = new AudioDeviceHandlerConnectionService();
|
||||
audioDeviceHandler = new AudioDeviceHandlerConnectionService(audioManager);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
audioDeviceHandler = new AudioDeviceHandlerGeneric();
|
||||
audioDeviceHandler = new AudioDeviceHandlerGeneric(audioManager);
|
||||
} else {
|
||||
audioDeviceHandler = new AudioDeviceHandlerLegacy();
|
||||
audioDeviceHandler = new AudioDeviceHandlerLegacy(audioManager);
|
||||
}
|
||||
|
||||
audioDeviceHandler.start(getReactApplicationContext(), this);
|
||||
audioDeviceHandler.start(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,9 +306,14 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
* @param use Boolean indicator of where it should be used or not.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void setUseConnectionService(boolean use) {
|
||||
useConnectionService_ = use;
|
||||
setAudioDeviceHandler();
|
||||
public void setUseConnectionService(final boolean use) {
|
||||
runInAudioThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
useConnectionService_ = use;
|
||||
setAudioDeviceHandler();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -408,16 +427,24 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed on the legacy handler...
|
||||
*
|
||||
* @return Context for the application.
|
||||
*/
|
||||
Context getContext() {
|
||||
return getReactApplicationContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for the modules implementing the actual audio device management.
|
||||
*/
|
||||
interface AudioDeviceHandlerInterface {
|
||||
/**
|
||||
* Start detecting audio device changes.
|
||||
* @param context Android {@link Context} where detection should take place.
|
||||
* @param audioModeModule Reference to the main {@link AudioModeModule}.
|
||||
*/
|
||||
void start(Context context, AudioModeModule audioModeModule);
|
||||
void start(AudioModeModule audioModeModule);
|
||||
|
||||
/**
|
||||
* Stop audio device detection.
|
||||
|
||||
@@ -16,11 +16,14 @@
|
||||
*/
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
|
||||
public class JitsiMeet {
|
||||
|
||||
/**
|
||||
* Default {@link JitsiMeetConferenceOptions} which will be used for all conferences. When
|
||||
* joining a conference these options will be merged with the ones passed to
|
||||
@@ -72,4 +75,10 @@ public class JitsiMeet {
|
||||
reactInstanceManager.showDevOptionsDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isCrashReportingDisabled(Context context) {
|
||||
SharedPreferences preferences = context.getSharedPreferences("jitsi-default-preferences", Context.MODE_PRIVATE);
|
||||
String value = preferences.getString("isCrashReportingDisabled", "");
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
@@ -67,6 +68,9 @@ public class JitsiMeetActivity extends FragmentActivity
|
||||
|
||||
setContentView(R.layout.activity_jitsi_meet);
|
||||
|
||||
// Listen for conference events.
|
||||
getJitsiView().setListener(this);
|
||||
|
||||
if (!extraInitialize()) {
|
||||
initialize();
|
||||
}
|
||||
@@ -151,9 +155,6 @@ public class JitsiMeetActivity extends FragmentActivity
|
||||
}
|
||||
|
||||
protected void initialize() {
|
||||
// Listen for conference events.
|
||||
getJitsiView().setListener(this);
|
||||
|
||||
// Join the room specified by the URL the app was launched with.
|
||||
// Joining without the room option displays the welcome page.
|
||||
join(getConferenceOptions(getIntent()));
|
||||
@@ -164,6 +165,8 @@ public class JitsiMeetActivity extends FragmentActivity
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
JitsiMeetActivityDelegate.onActivityResult(this, requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
|
||||
@@ -288,6 +288,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
}
|
||||
|
||||
private JitsiMeetConferenceOptions(Parcel in) {
|
||||
serverURL = (URL) in.readSerializable();
|
||||
room = in.readString();
|
||||
subject = in.readString();
|
||||
token = in.readString();
|
||||
@@ -376,6 +377,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeSerializable(serverURL);
|
||||
dest.writeString(room);
|
||||
dest.writeString(subject);
|
||||
dest.writeString(token);
|
||||
|
||||
@@ -57,22 +57,6 @@ public class JitsiMeetFragment extends Fragment {
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
JitsiMeetActivityDelegate.onActivityResult(
|
||||
getActivity(), requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
if (view != null) {
|
||||
view.dispose();
|
||||
view = null;
|
||||
}
|
||||
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
@@ -201,4 +201,10 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
protected void onExternalAPIEvent(String name, ReadableMap data) {
|
||||
onExternalAPIEvent(LISTENER_METHODS, name, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
dispose();
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
* Copyright @ 2017-2018 Atlassian Pty Ltd
|
||||
* Copyright @ 2017-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +17,7 @@
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
|
||||
@@ -31,13 +31,12 @@ import com.facebook.react.devsupport.DevInternalSettings;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import com.oney.WebRTCModule.RTCVideoViewManager;
|
||||
import com.oney.WebRTCModule.WebRTCModule;
|
||||
|
||||
import org.webrtc.SoftwareVideoDecoderFactory;
|
||||
import org.webrtc.SoftwareVideoEncoderFactory;
|
||||
import org.webrtc.VideoDecoderFactory;
|
||||
import org.webrtc.VideoEncoderFactory;
|
||||
import org.webrtc.audio.AudioDeviceModule;
|
||||
import org.webrtc.audio.JavaAudioDeviceModule;
|
||||
|
||||
@@ -84,12 +83,10 @@ class ReactInstanceManagerHolder {
|
||||
|
||||
AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext)
|
||||
.createAudioDeviceModule();
|
||||
VideoDecoderFactory videoDecoderFactory = new SoftwareVideoDecoderFactory();
|
||||
VideoEncoderFactory videoEncoderFactory = new SoftwareVideoEncoderFactory();
|
||||
|
||||
options.setAudioDeviceModule(adm);
|
||||
options.setVideoDecoderFactory(videoDecoderFactory);
|
||||
options.setVideoEncoderFactory(videoEncoderFactory);
|
||||
|
||||
options.setVideoDecoderFactory(new SoftwareVideoDecoderFactory());
|
||||
options.setVideoEncoderFactory(new SoftwareVideoEncoderFactory());
|
||||
|
||||
nativeModules.add(new WebRTCModule(reactContext, options));
|
||||
|
||||
@@ -193,6 +190,7 @@ class ReactInstanceManagerHolder {
|
||||
new com.corbt.keepawake.KCKeepAwakePackage(),
|
||||
new com.facebook.react.shell.MainReactPackage(),
|
||||
new com.horcrux.svg.SvgPackage(),
|
||||
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
|
||||
new com.reactnativecommunity.netinfo.NetInfoPackage(),
|
||||
|
||||
@@ -9,6 +9,8 @@ include ':react-native-community-async-storage'
|
||||
project(':react-native-community-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
|
||||
include ':react-native-community_netinfo'
|
||||
project(':react-native-community_netinfo').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/netinfo/android')
|
||||
include ':react-native-default-preference'
|
||||
project(':react-native-default-preference').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-default-preference/android')
|
||||
include ':react-native-google-signin'
|
||||
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/google-signin/android')
|
||||
include ':react-native-immersive'
|
||||
@@ -24,4 +26,4 @@ project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../
|
||||
include ':react-native-webrtc'
|
||||
project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
|
||||
include ':react-native-webview'
|
||||
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
|
||||
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
|
||||
2
app.js
@@ -6,10 +6,10 @@ import 'jQuery-Impromptu';
|
||||
|
||||
import conference from './conference';
|
||||
import API from './modules/API';
|
||||
import UI from './modules/UI/UI';
|
||||
import keyboardshortcut from './modules/keyboardshortcut/keyboardshortcut';
|
||||
import remoteControl from './modules/remotecontrol/RemoteControl';
|
||||
import translation from './modules/translation/translation';
|
||||
import UI from './modules/UI/UI';
|
||||
|
||||
window.APP = {
|
||||
API,
|
||||
|
||||
596
conference.js
@@ -1,24 +1,20 @@
|
||||
/* global $, APP, JitsiMeetJS, config, interfaceConfig */
|
||||
|
||||
import { openConnection } from './connection';
|
||||
import EventEmitter from 'events';
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
import Recorder from './modules/recorder/Recorder';
|
||||
|
||||
import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
|
||||
|
||||
import * as RemoteControlEvents
|
||||
from './service/remotecontrol/RemoteControlEvents';
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
import UIUtil from './modules/UI/util/UIUtil';
|
||||
import { createTaskQueue } from './modules/util/helpers';
|
||||
import * as JitsiMeetConferenceEvents from './ConferenceEvents';
|
||||
|
||||
import { openConnection } from './connection';
|
||||
import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
import UIUtil from './modules/UI/util/UIUtil';
|
||||
import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
|
||||
import Recorder from './modules/recorder/Recorder';
|
||||
import { createTaskQueue } from './modules/util/helpers';
|
||||
import {
|
||||
createDeviceChangedEvent,
|
||||
createStartSilentEvent,
|
||||
createScreenSharingEvent,
|
||||
createStreamSwitchDelayEvent,
|
||||
createTrackMutedEvent,
|
||||
sendAnalytics
|
||||
} from './react/features/analytics';
|
||||
@@ -27,9 +23,6 @@ import {
|
||||
redirectToStaticPage,
|
||||
reloadWithStoredParams
|
||||
} from './react/features/app';
|
||||
|
||||
import EventEmitter from 'events';
|
||||
|
||||
import {
|
||||
AVATAR_ID_COMMAND,
|
||||
AVATAR_URL_COMMAND,
|
||||
@@ -79,7 +72,6 @@ import {
|
||||
setVideoAvailable,
|
||||
setVideoMuted
|
||||
} from './react/features/base/media';
|
||||
import { showNotification } from './react/features/notifications';
|
||||
import {
|
||||
dominantSpeakerChanged,
|
||||
getLocalParticipant,
|
||||
@@ -112,25 +104,47 @@ import {
|
||||
import { getJitsiMeetGlobalNS } from './react/features/base/util';
|
||||
import { showDesktopPicker } from './react/features/desktop-picker';
|
||||
import { appendSuffix } from './react/features/display-name';
|
||||
import { setE2EEKey } from './react/features/e2ee';
|
||||
import {
|
||||
maybeOpenFeedbackDialog,
|
||||
submitFeedback
|
||||
} from './react/features/feedback';
|
||||
import { showNotification } from './react/features/notifications';
|
||||
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay';
|
||||
import { suspendDetected } from './react/features/power-monitor';
|
||||
import { setSharedVideoStatus } from './react/features/shared-video';
|
||||
import { createPresenterEffect } from './react/features/stream-effects/presenter';
|
||||
import { endpointMessageReceived } from './react/features/subtitles';
|
||||
import {
|
||||
initPrejoin,
|
||||
isPrejoinPageEnabled,
|
||||
isPrejoinPageVisible,
|
||||
replacePrejoinAudioTrack,
|
||||
replacePrejoinVideoTrack
|
||||
} from './react/features/prejoin';
|
||||
import { createRnnoiseProcessorPromise } from './react/features/rnnoise';
|
||||
import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
|
||||
import { setSharedVideoStatus } from './react/features/shared-video';
|
||||
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
|
||||
import { createPresenterEffect } from './react/features/stream-effects/presenter';
|
||||
import { endpointMessageReceived } from './react/features/subtitles';
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
import * as RemoteControlEvents
|
||||
from './service/remotecontrol/RemoteControlEvents';
|
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
const eventEmitter = new EventEmitter();
|
||||
|
||||
let room;
|
||||
let connection;
|
||||
|
||||
/**
|
||||
* The promise is used when the prejoin screen is shown.
|
||||
* While the user configures the devices the connection can be made.
|
||||
*
|
||||
* @type {Promise<Object>}
|
||||
* @private
|
||||
*/
|
||||
let _connectionPromise;
|
||||
|
||||
/**
|
||||
* This promise is used for chaining mutePresenterVideo calls in order to avoid calling GUM multiple times if it takes
|
||||
* a while to finish.
|
||||
@@ -338,6 +352,7 @@ class ConferenceConnector {
|
||||
}
|
||||
|
||||
case JitsiConferenceErrors.FOCUS_LEFT:
|
||||
case JitsiConferenceErrors.ICE_FAILED:
|
||||
case JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE:
|
||||
case JitsiConferenceErrors.OFFER_ANSWER_FAILED:
|
||||
APP.store.dispatch(conferenceWillLeave(room));
|
||||
@@ -435,7 +450,6 @@ export default {
|
||||
* the tracks won't exist).
|
||||
*/
|
||||
_localTracksInitialized: false,
|
||||
isModerator: false,
|
||||
isSharingScreen: false,
|
||||
|
||||
/**
|
||||
@@ -470,28 +484,13 @@ export default {
|
||||
localVideo: null,
|
||||
|
||||
/**
|
||||
* Creates local media tracks and connects to a room. Will show error
|
||||
* dialogs in case accessing the local microphone and/or camera failed. Will
|
||||
* show guidance overlay for users on how to give access to camera and/or
|
||||
* microphone.
|
||||
* @param {string} roomName
|
||||
* @param {object} options
|
||||
* @param {boolean} options.startAudioOnly=false - if <tt>true</tt> then
|
||||
* only audio track will be created and the audio only mode will be turned
|
||||
* on.
|
||||
* @param {boolean} options.startScreenSharing=false - if <tt>true</tt>
|
||||
* should start with screensharing instead of camera video.
|
||||
* @param {boolean} options.startWithAudioMuted - will start the conference
|
||||
* without any audio tracks.
|
||||
* @param {boolean} options.startWithVideoMuted - will start the conference
|
||||
* without any video tracks.
|
||||
* @returns {Promise.<JitsiLocalTrack[], JitsiConnection>}
|
||||
* Returns an object containing a promise which resolves with the created tracks &
|
||||
* the errors resulting from that process.
|
||||
*
|
||||
* @returns {Promise<JitsiLocalTrack[]>, Object}
|
||||
*/
|
||||
createInitialLocalTracksAndConnect(roomName, options = {}) {
|
||||
let audioAndVideoError,
|
||||
audioOnlyError,
|
||||
screenSharingError,
|
||||
videoOnlyError;
|
||||
createInitialLocalTracks(options = {}) {
|
||||
const errors = {};
|
||||
const initialDevices = [ 'audio' ];
|
||||
const requestedAudio = true;
|
||||
let requestedVideo = false;
|
||||
@@ -523,7 +522,7 @@ export default {
|
||||
// FIXME is there any simpler way to rewrite this spaghetti below ?
|
||||
if (options.startScreenSharing) {
|
||||
tryCreateLocalTracks = this._createDesktopTrack()
|
||||
.then(desktopStream => {
|
||||
.then(([ desktopStream ]) => {
|
||||
if (!requestedAudio) {
|
||||
return [ desktopStream ];
|
||||
}
|
||||
@@ -532,21 +531,21 @@ export default {
|
||||
.then(([ audioStream ]) =>
|
||||
[ desktopStream, audioStream ])
|
||||
.catch(error => {
|
||||
audioOnlyError = error;
|
||||
errors.audioOnlyError = error;
|
||||
|
||||
return [ desktopStream ];
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('Failed to obtain desktop stream', error);
|
||||
screenSharingError = error;
|
||||
errors.screenSharingError = error;
|
||||
|
||||
return requestedAudio
|
||||
? createLocalTracksF({ devices: [ 'audio' ] }, true)
|
||||
: [];
|
||||
})
|
||||
.catch(error => {
|
||||
audioOnlyError = error;
|
||||
errors.audioOnlyError = error;
|
||||
|
||||
return [];
|
||||
});
|
||||
@@ -559,16 +558,16 @@ export default {
|
||||
if (requestedAudio && requestedVideo) {
|
||||
|
||||
// Try audio only...
|
||||
audioAndVideoError = err;
|
||||
errors.audioAndVideoError = err;
|
||||
|
||||
return (
|
||||
createLocalTracksF({ devices: [ 'audio' ] }, true));
|
||||
} else if (requestedAudio && !requestedVideo) {
|
||||
audioOnlyError = err;
|
||||
errors.audioOnlyError = err;
|
||||
|
||||
return [];
|
||||
} else if (requestedVideo && !requestedAudio) {
|
||||
videoOnlyError = err;
|
||||
errors.videoOnlyError = err;
|
||||
|
||||
return [];
|
||||
}
|
||||
@@ -579,7 +578,7 @@ export default {
|
||||
if (!requestedAudio) {
|
||||
logger.error('The impossible just happened', err);
|
||||
}
|
||||
audioOnlyError = err;
|
||||
errors.audioOnlyError = err;
|
||||
|
||||
// Try video only...
|
||||
return requestedVideo
|
||||
@@ -591,7 +590,7 @@ export default {
|
||||
if (!requestedVideo) {
|
||||
logger.error('The impossible just happened', err);
|
||||
}
|
||||
videoOnlyError = err;
|
||||
errors.videoOnlyError = err;
|
||||
|
||||
return [];
|
||||
});
|
||||
@@ -602,8 +601,44 @@ export default {
|
||||
// cases, when auth is rquired, for instance, that won't happen until
|
||||
// the user inputs their credentials, but the dialog would be
|
||||
// overshadowed by the overlay.
|
||||
tryCreateLocalTracks.then(() =>
|
||||
APP.store.dispatch(mediaPermissionPromptVisibilityChanged(false)));
|
||||
tryCreateLocalTracks.then(tracks => {
|
||||
APP.store.dispatch(mediaPermissionPromptVisibilityChanged(false));
|
||||
|
||||
return tracks;
|
||||
});
|
||||
|
||||
return {
|
||||
tryCreateLocalTracks,
|
||||
errors
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates local media tracks and connects to a room. Will show error
|
||||
* dialogs in case accessing the local microphone and/or camera failed. Will
|
||||
* show guidance overlay for users on how to give access to camera and/or
|
||||
* microphone.
|
||||
* @param {string} roomName
|
||||
* @param {object} options
|
||||
* @param {boolean} options.startAudioOnly=false - if <tt>true</tt> then
|
||||
* only audio track will be created and the audio only mode will be turned
|
||||
* on.
|
||||
* @param {boolean} options.startScreenSharing=false - if <tt>true</tt>
|
||||
* should start with screensharing instead of camera video.
|
||||
* @param {boolean} options.startWithAudioMuted - will start the conference
|
||||
* without any audio tracks.
|
||||
* @param {boolean} options.startWithVideoMuted - will start the conference
|
||||
* without any video tracks.
|
||||
* @returns {Promise.<JitsiLocalTrack[], JitsiConnection>}
|
||||
*/
|
||||
createInitialLocalTracksAndConnect(roomName, options = {}) {
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(options);
|
||||
const {
|
||||
audioAndVideoError,
|
||||
audioOnlyError,
|
||||
screenSharingError,
|
||||
videoOnlyError
|
||||
} = errors;
|
||||
|
||||
return Promise.all([ tryCreateLocalTracks, connect(roomName) ])
|
||||
.then(([ tracks, con ]) => {
|
||||
@@ -635,103 +670,132 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
startConference(con, tracks) {
|
||||
tracks.forEach(track => {
|
||||
if ((track.isAudioTrack() && this.isLocalAudioMuted())
|
||||
|| (track.isVideoTrack() && this.isLocalVideoMuted())) {
|
||||
const mediaType = track.getType();
|
||||
|
||||
sendAnalytics(
|
||||
createTrackMutedEvent(mediaType, 'initial mute'));
|
||||
logger.log(`${mediaType} mute: initially muted.`);
|
||||
track.mute();
|
||||
}
|
||||
});
|
||||
logger.log(`Initialized with ${tracks.length} local tracks`);
|
||||
|
||||
this._localTracksInitialized = true;
|
||||
con.addEventListener(JitsiConnectionEvents.CONNECTION_FAILED, _connectionFailedHandler);
|
||||
APP.connection = connection = con;
|
||||
|
||||
// Desktop sharing related stuff:
|
||||
this.isDesktopSharingEnabled
|
||||
= JitsiMeetJS.isDesktopSharingEnabled();
|
||||
eventEmitter.emit(JitsiMeetConferenceEvents.DESKTOP_SHARING_ENABLED_CHANGED, this.isDesktopSharingEnabled);
|
||||
|
||||
APP.store.dispatch(
|
||||
setDesktopSharingEnabled(this.isDesktopSharingEnabled));
|
||||
|
||||
this._createRoom(tracks);
|
||||
APP.remoteControl.init();
|
||||
|
||||
// if user didn't give access to mic or camera or doesn't have
|
||||
// them at all, we mark corresponding toolbar buttons as muted,
|
||||
// so that the user can try unmute later on and add audio/video
|
||||
// to the conference
|
||||
if (!tracks.find(t => t.isAudioTrack())) {
|
||||
this.setAudioMuteStatus(true);
|
||||
}
|
||||
|
||||
if (!tracks.find(t => t.isVideoTrack())) {
|
||||
this.setVideoMuteStatus(true);
|
||||
}
|
||||
|
||||
if (config.iAmRecorder) {
|
||||
this.recorder = new Recorder();
|
||||
}
|
||||
|
||||
if (config.startSilent) {
|
||||
sendAnalytics(createStartSilentEvent());
|
||||
APP.store.dispatch(showNotification({
|
||||
descriptionKey: 'notify.startSilentDescription',
|
||||
titleKey: 'notify.startSilentTitle'
|
||||
}));
|
||||
}
|
||||
|
||||
// XXX The API will take care of disconnecting from the XMPP
|
||||
// server (and, thus, leaving the room) on unload.
|
||||
return new Promise((resolve, reject) => {
|
||||
(new ConferenceConnector(resolve, reject)).connect();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Open new connection and join to the conference.
|
||||
* @param {object} options
|
||||
* @param {string} roomName - The name of the conference.
|
||||
* Open new connection and join the conference when prejoin page is not enabled.
|
||||
* If prejoin page is enabled open an new connection in the background
|
||||
* and create local tracks.
|
||||
*
|
||||
* @param {{ roomName: string }} options
|
||||
* @returns {Promise}
|
||||
*/
|
||||
init(options) {
|
||||
this.roomName = options.roomName;
|
||||
async init({ roomName }) {
|
||||
const initialOptions = {
|
||||
startAudioOnly: config.startAudioOnly,
|
||||
startScreenSharing: config.startScreenSharing,
|
||||
startWithAudioMuted: config.startWithAudioMuted
|
||||
|| config.startSilent
|
||||
|| isUserInteractionRequiredForUnmute(APP.store.getState()),
|
||||
startWithVideoMuted: config.startWithVideoMuted
|
||||
|| isUserInteractionRequiredForUnmute(APP.store.getState())
|
||||
};
|
||||
|
||||
return (
|
||||
this.roomName = roomName;
|
||||
|
||||
window.addEventListener('hashchange', this.onHashChange.bind(this), false);
|
||||
|
||||
try {
|
||||
// Initialize the device list first. This way, when creating tracks
|
||||
// based on preferred devices, loose label matching can be done in
|
||||
// cases where the exact ID match is no longer available, such as
|
||||
// when the camera device has switched USB ports.
|
||||
// when in startSilent mode we want to start with audio muted
|
||||
this._initDeviceList()
|
||||
.catch(error => logger.warn(
|
||||
'initial device list initialization failed', error))
|
||||
.then(() => this.createInitialLocalTracksAndConnect(
|
||||
options.roomName, {
|
||||
startAudioOnly: config.startAudioOnly,
|
||||
startScreenSharing: config.startScreenSharing,
|
||||
startWithAudioMuted: config.startWithAudioMuted
|
||||
|| config.startSilent
|
||||
|| isUserInteractionRequiredForUnmute(APP.store.getState()),
|
||||
startWithVideoMuted: config.startWithVideoMuted
|
||||
|| isUserInteractionRequiredForUnmute(APP.store.getState())
|
||||
}))
|
||||
.then(([ tracks, con ]) => {
|
||||
tracks.forEach(track => {
|
||||
if ((track.isAudioTrack() && this.isLocalAudioMuted())
|
||||
|| (track.isVideoTrack() && this.isLocalVideoMuted())) {
|
||||
const mediaType = track.getType();
|
||||
await this._initDeviceList();
|
||||
} catch (error) {
|
||||
logger.warn('initial device list initialization failed', error);
|
||||
}
|
||||
|
||||
sendAnalytics(
|
||||
createTrackMutedEvent(mediaType, 'initial mute'));
|
||||
logger.log(`${mediaType} mute: initially muted.`);
|
||||
track.mute();
|
||||
}
|
||||
});
|
||||
logger.log(`initialized with ${tracks.length} local tracks`);
|
||||
this._localTracksInitialized = true;
|
||||
con.addEventListener(
|
||||
JitsiConnectionEvents.CONNECTION_FAILED,
|
||||
_connectionFailedHandler);
|
||||
APP.connection = connection = con;
|
||||
if (isPrejoinPageEnabled(APP.store.getState())) {
|
||||
_connectionPromise = connect(roomName);
|
||||
|
||||
// Desktop sharing related stuff:
|
||||
this.isDesktopSharingEnabled
|
||||
= JitsiMeetJS.isDesktopSharingEnabled();
|
||||
eventEmitter.emit(
|
||||
JitsiMeetConferenceEvents.DESKTOP_SHARING_ENABLED_CHANGED,
|
||||
this.isDesktopSharingEnabled);
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions);
|
||||
const tracks = await tryCreateLocalTracks;
|
||||
|
||||
APP.store.dispatch(
|
||||
setDesktopSharingEnabled(this.isDesktopSharingEnabled));
|
||||
// Initialize device list a second time to ensure device labels
|
||||
// get populated in case of an initial gUM acceptance; otherwise
|
||||
// they may remain as empty strings.
|
||||
this._initDeviceList(true);
|
||||
|
||||
this._createRoom(tracks);
|
||||
APP.remoteControl.init();
|
||||
return APP.store.dispatch(initPrejoin(tracks, errors));
|
||||
}
|
||||
|
||||
// if user didn't give access to mic or camera or doesn't have
|
||||
// them at all, we mark corresponding toolbar buttons as muted,
|
||||
// so that the user can try unmute later on and add audio/video
|
||||
// to the conference
|
||||
if (!tracks.find(t => t.isAudioTrack())) {
|
||||
this.setAudioMuteStatus(true);
|
||||
}
|
||||
const [ tracks, con ] = await this.createInitialLocalTracksAndConnect(
|
||||
roomName, initialOptions);
|
||||
|
||||
if (!tracks.find(t => t.isVideoTrack())) {
|
||||
this.setVideoMuteStatus(true);
|
||||
}
|
||||
this._initDeviceList(true);
|
||||
|
||||
// Initialize device list a second time to ensure device labels
|
||||
// get populated in case of an initial gUM acceptance; otherwise
|
||||
// they may remain as empty strings.
|
||||
this._initDeviceList(true);
|
||||
return this.startConference(con, tracks);
|
||||
},
|
||||
|
||||
if (config.iAmRecorder) {
|
||||
this.recorder = new Recorder();
|
||||
}
|
||||
/**
|
||||
* Joins conference after the tracks have been configured in the prejoin screen.
|
||||
*
|
||||
* @param {Object[]} tracks - An array with the configured tracks
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async prejoinStart(tracks) {
|
||||
const con = await _connectionPromise;
|
||||
|
||||
if (config.startSilent) {
|
||||
sendAnalytics(createStartSilentEvent());
|
||||
APP.store.dispatch(showNotification({
|
||||
descriptionKey: 'notify.startSilentDescription',
|
||||
titleKey: 'notify.startSilentTitle'
|
||||
}));
|
||||
}
|
||||
|
||||
// XXX The API will take care of disconnecting from the XMPP
|
||||
// server (and, thus, leaving the room) on unload.
|
||||
return new Promise((resolve, reject) => {
|
||||
(new ConferenceConnector(resolve, reject)).connect();
|
||||
});
|
||||
})
|
||||
);
|
||||
return this.startConference(con, tracks);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -926,14 +990,6 @@ export default {
|
||||
this.muteVideo(!this.isLocalVideoMuted(), showUI);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve list of conference participants (without local user).
|
||||
* @returns {JitsiParticipant[]}
|
||||
*/
|
||||
listMembers() {
|
||||
return room.getParticipants();
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve list of ids of conference participants (without local user).
|
||||
* @returns {string[]}
|
||||
@@ -953,6 +1009,16 @@ export default {
|
||||
return user && user.isModerator();
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve list of conference participants (without local user).
|
||||
* @returns {JitsiParticipant[]}
|
||||
*
|
||||
* NOTE: Used by jitsi-meet-torture!
|
||||
*/
|
||||
listMembers() {
|
||||
return room.getParticipants();
|
||||
},
|
||||
|
||||
get membersCount() {
|
||||
return room.getParticipants().length + 1;
|
||||
},
|
||||
@@ -1174,6 +1240,34 @@ export default {
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Handled location hash change events.
|
||||
*/
|
||||
onHashChange() {
|
||||
const items = {};
|
||||
const parts = window.location.hash.substr(1).split('&');
|
||||
|
||||
for (const part of parts) {
|
||||
const param = part.split('=');
|
||||
const key = param[0];
|
||||
|
||||
if (!key) {
|
||||
continue; // eslint-disable-line no-continue
|
||||
}
|
||||
|
||||
items[key] = param[1];
|
||||
}
|
||||
|
||||
if (typeof items.e2eekey !== undefined) {
|
||||
APP.store.dispatch(setE2EEKey(items.e2eekey));
|
||||
|
||||
// Clean URL in browser history.
|
||||
const cleanUrl = window.location.href.split('#')[0];
|
||||
|
||||
history.replaceState(history.state, document.title, cleanUrl);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Exposes a Command(s) API on this instance. It is necessitated by (1) the
|
||||
* desire to keep room private to this instance and (2) the need of other
|
||||
@@ -1285,6 +1379,13 @@ export default {
|
||||
options.confID = `${locationURL.host}${locationURL.pathname}`;
|
||||
options.createVADProcessor = createRnnoiseProcessorPromise;
|
||||
|
||||
// Disable CallStats, if requessted.
|
||||
if (options.disableThirdPartyRequests) {
|
||||
delete options.callStatsID;
|
||||
delete options.callStatsSecret;
|
||||
delete options.getWiFiStatsMethod;
|
||||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
@@ -1312,6 +1413,18 @@ export default {
|
||||
useVideoStream(newStream) {
|
||||
return new Promise((resolve, reject) => {
|
||||
_replaceLocalVideoTrackQueue.enqueue(onFinish => {
|
||||
/**
|
||||
* When the prejoin page is visible there is no conference object
|
||||
* created. The prejoin tracks are managed separately,
|
||||
* so this updates the prejoin video track.
|
||||
*/
|
||||
if (isPrejoinPageVisible(APP.store.getState())) {
|
||||
return APP.store.dispatch(replacePrejoinVideoTrack(newStream))
|
||||
.then(resolve)
|
||||
.catch(reject)
|
||||
.then(onFinish);
|
||||
}
|
||||
|
||||
APP.store.dispatch(
|
||||
replaceLocalTrack(this.localVideo, newStream, room))
|
||||
.then(() => {
|
||||
@@ -1365,6 +1478,18 @@ export default {
|
||||
useAudioStream(newStream) {
|
||||
return new Promise((resolve, reject) => {
|
||||
_replaceLocalAudioTrackQueue.enqueue(onFinish => {
|
||||
/**
|
||||
* When the prejoin page is visible there is no conference object
|
||||
* created. The prejoin tracks are managed separately,
|
||||
* so this updates the prejoin audio stream.
|
||||
*/
|
||||
if (isPrejoinPageVisible(APP.store.getState())) {
|
||||
return APP.store.dispatch(replacePrejoinAudioTrack(newStream))
|
||||
.then(resolve)
|
||||
.catch(reject)
|
||||
.then(onFinish);
|
||||
}
|
||||
|
||||
APP.store.dispatch(
|
||||
replaceLocalTrack(this.localAudio, newStream, room))
|
||||
.then(() => {
|
||||
@@ -1415,7 +1540,7 @@ export default {
|
||||
* in case it fails.
|
||||
* @private
|
||||
*/
|
||||
_turnScreenSharingOff(didHaveVideo) {
|
||||
async _turnScreenSharingOff(didHaveVideo) {
|
||||
this._untoggleScreenSharing = null;
|
||||
this.videoSwitchInProgress = true;
|
||||
const { receiver } = APP.remoteControl;
|
||||
@@ -1425,6 +1550,9 @@ export default {
|
||||
}
|
||||
|
||||
this._stopProxyConnection();
|
||||
if (config.enableScreenshotCapture) {
|
||||
APP.store.dispatch(toggleScreenshotCaptureEffect(false));
|
||||
}
|
||||
|
||||
// It can happen that presenter GUM is in progress while screensharing is being turned off. Here it needs to
|
||||
// wait for that GUM to be resolved in order to prevent leaking the presenter track(this.localPresenterVideo
|
||||
@@ -1441,6 +1569,20 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
// If system audio was also shared stop the AudioMixerEffect and dispose of the desktop audio track.
|
||||
if (this._mixerEffect) {
|
||||
await this.localAudio.setEffect(undefined);
|
||||
await this._desktopAudioStream.dispose();
|
||||
this._mixerEffect = undefined;
|
||||
this._desktopAudioStream = undefined;
|
||||
|
||||
// In case there was no local audio when screen sharing was started the fact that we set the audio stream to
|
||||
// null will take care of the desktop audio stream cleanup.
|
||||
} else if (this._desktopAudioStream) {
|
||||
await this.useAudioStream(null);
|
||||
this._desktopAudioStream = undefined;
|
||||
}
|
||||
|
||||
if (didHaveVideo) {
|
||||
promise = promise.then(() => createLocalTracksF({ devices: [ 'video' ] }))
|
||||
.then(([ stream ]) => this.useVideoStream(stream))
|
||||
@@ -1461,8 +1603,6 @@ export default {
|
||||
promise = promise.then(() => this.useVideoStream(null));
|
||||
}
|
||||
|
||||
APP.store.dispatch(toggleScreenshotCaptureEffect(false));
|
||||
|
||||
return promise.then(
|
||||
() => {
|
||||
this.videoSwitchInProgress = false;
|
||||
@@ -1582,26 +1722,31 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
return getDesktopStreamPromise.then(([ desktopStream ]) => {
|
||||
return getDesktopStreamPromise.then(desktopStreams => {
|
||||
// Stores the "untoggle" handler which remembers whether was
|
||||
// there any video before and whether was it muted.
|
||||
this._untoggleScreenSharing
|
||||
= this._turnScreenSharingOff.bind(this, didHaveVideo);
|
||||
desktopStream.on(
|
||||
JitsiTrackEvents.LOCAL_TRACK_STOPPED,
|
||||
() => {
|
||||
// If the stream was stopped during screen sharing
|
||||
// session then we should switch back to video.
|
||||
this.isSharingScreen
|
||||
&& this._untoggleScreenSharing
|
||||
&& this._untoggleScreenSharing();
|
||||
}
|
||||
);
|
||||
|
||||
const desktopVideoStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
|
||||
|
||||
if (desktopVideoStream) {
|
||||
desktopVideoStream.on(
|
||||
JitsiTrackEvents.LOCAL_TRACK_STOPPED,
|
||||
() => {
|
||||
// If the stream was stopped during screen sharing
|
||||
// session then we should switch back to video.
|
||||
this.isSharingScreen
|
||||
&& this._untoggleScreenSharing
|
||||
&& this._untoggleScreenSharing();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// close external installation dialog on success.
|
||||
externalInstallation && $.prompt.close();
|
||||
|
||||
return desktopStream;
|
||||
return desktopStreams;
|
||||
}, error => {
|
||||
DSExternalInstallationInProgress = false;
|
||||
|
||||
@@ -1664,19 +1809,40 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new presenter track and apply the presenter effect.
|
||||
if (!this.localPresenterVideo && !mute) {
|
||||
// create a new presenter track and apply the presenter effect.
|
||||
let { height } = this.localVideo.track.getSettings();
|
||||
let { aspectRatio, height } = this.localVideo.track.getSettings();
|
||||
const { width } = this.localVideo.track.getSettings();
|
||||
let desktopResizeConstraints = {};
|
||||
let resizeDesktopStream = false;
|
||||
const DESKTOP_STREAM_CAP = 720;
|
||||
|
||||
// Workaround for Firefox since it doesn't return the correct width/height of the desktop stream
|
||||
// that is being currently shared.
|
||||
if (!height) {
|
||||
const desktopResizeConstraints = {
|
||||
// Determine the constraints if the desktop track needs to be resized.
|
||||
// Resizing is needed when the resolution cannot be determined or when
|
||||
// the window is bigger than 720p.
|
||||
if (height && width) {
|
||||
aspectRatio = aspectRatio ?? (width / height).toPrecision(4);
|
||||
const advancedConstraints = [ { aspectRatio } ];
|
||||
const isPortrait = height >= width;
|
||||
|
||||
// Determine which dimension needs resizing and resize only that side
|
||||
// keeping the aspect ratio same as before.
|
||||
if (isPortrait && width > DESKTOP_STREAM_CAP) {
|
||||
resizeDesktopStream = true;
|
||||
advancedConstraints.push({ width: DESKTOP_STREAM_CAP });
|
||||
} else if (!isPortrait && height > DESKTOP_STREAM_CAP) {
|
||||
resizeDesktopStream = true;
|
||||
advancedConstraints.push({ height: DESKTOP_STREAM_CAP });
|
||||
}
|
||||
desktopResizeConstraints.advanced = advancedConstraints;
|
||||
} else {
|
||||
resizeDesktopStream = true;
|
||||
desktopResizeConstraints = {
|
||||
width: 1280,
|
||||
height: 720,
|
||||
resizeMode: 'crop-and-scale'
|
||||
height: 720
|
||||
};
|
||||
|
||||
}
|
||||
if (resizeDesktopStream) {
|
||||
try {
|
||||
await this.localVideo.track.applyConstraints(desktopResizeConstraints);
|
||||
} catch (err) {
|
||||
@@ -1684,7 +1850,7 @@ export default {
|
||||
|
||||
return;
|
||||
}
|
||||
height = desktopResizeConstraints.height;
|
||||
height = this.localVideo.track.getSettings().height ?? DESKTOP_STREAM_CAP;
|
||||
}
|
||||
const defaultCamera = getUserSelectedCameraDeviceId(APP.store.getState());
|
||||
let effect;
|
||||
@@ -1731,10 +1897,34 @@ export default {
|
||||
this.videoSwitchInProgress = true;
|
||||
|
||||
return this._createDesktopTrack(options)
|
||||
.then(stream => this.useVideoStream(stream))
|
||||
.then(async streams => {
|
||||
const desktopVideoStream = streams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
|
||||
|
||||
if (desktopVideoStream) {
|
||||
await this.useVideoStream(desktopVideoStream);
|
||||
}
|
||||
|
||||
this._desktopAudioStream = streams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
|
||||
|
||||
if (this._desktopAudioStream) {
|
||||
// If there is a localAudio stream, mix in the desktop audio stream captured by the screen sharing
|
||||
// api.
|
||||
if (this.localAudio) {
|
||||
this._mixerEffect = new AudioMixerEffect(this._desktopAudioStream);
|
||||
|
||||
await this.localAudio.setEffect(this._mixerEffect);
|
||||
} else {
|
||||
// If no local stream is present ( i.e. no input audio devices) we use the screen share audio
|
||||
// stream as we would use a regular stream.
|
||||
await this.useAudioStream(this._desktopAudioStream);
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
this.videoSwitchInProgress = false;
|
||||
APP.store.dispatch(toggleScreenshotCaptureEffect(true));
|
||||
if (config.enableScreenshotCapture) {
|
||||
APP.store.dispatch(toggleScreenshotCaptureEffect(true));
|
||||
}
|
||||
sendAnalytics(createScreenSharingEvent('started'));
|
||||
logger.log('Screen sharing started');
|
||||
})
|
||||
@@ -1870,9 +2060,6 @@ export default {
|
||||
|
||||
logger.log(`USER ${id} connnected:`, user);
|
||||
APP.UI.addUser(user);
|
||||
|
||||
// check the roles for the new user and reflect them
|
||||
APP.UI.updateUserRole(user);
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.USER_LEFT, (id, user) => {
|
||||
@@ -1903,19 +2090,9 @@ export default {
|
||||
logger.info(`My role changed, new role: ${role}`);
|
||||
|
||||
APP.store.dispatch(localParticipantRoleChanged(role));
|
||||
|
||||
if (this.isModerator !== room.isModerator()) {
|
||||
this.isModerator = room.isModerator();
|
||||
APP.UI.updateLocalRole(room.isModerator());
|
||||
}
|
||||
APP.API.notifyUserRoleChanged(id, role);
|
||||
} else {
|
||||
APP.store.dispatch(participantRoleChanged(id, role));
|
||||
|
||||
const user = room.getParticipantById(id);
|
||||
|
||||
if (user) {
|
||||
APP.UI.updateUserRole(user);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2030,7 +2207,22 @@ export default {
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||
(...args) => APP.store.dispatch(endpointMessageReceived(...args)));
|
||||
(...args) => {
|
||||
APP.store.dispatch(endpointMessageReceived(...args));
|
||||
if (args && args.length >= 2) {
|
||||
const [ sender, eventData ] = args;
|
||||
|
||||
if (eventData.name === ENDPOINT_TEXT_MESSAGE_NAME) {
|
||||
APP.API.notifyEndpointTextMessageReceived({
|
||||
senderInfo: {
|
||||
jid: sender._jid,
|
||||
id: sender._id
|
||||
},
|
||||
eventData
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.LOCK_STATE_CHANGED,
|
||||
@@ -2156,18 +2348,6 @@ export default {
|
||||
});
|
||||
});
|
||||
|
||||
/* eslint-disable max-params */
|
||||
APP.UI.addListener(
|
||||
UIEvents.RESOLUTION_CHANGED,
|
||||
(id, oldResolution, newResolution, delay) => {
|
||||
sendAnalytics(createStreamSwitchDelayEvent(
|
||||
{
|
||||
'old_resolution': oldResolution,
|
||||
'new_resolution': newResolution,
|
||||
value: delay
|
||||
}));
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.AUTH_CLICKED, () => {
|
||||
AuthHandler.authenticate(room);
|
||||
});
|
||||
@@ -2261,9 +2441,19 @@ export default {
|
||||
|
||||
return stream;
|
||||
})
|
||||
.then(stream => this.useAudioStream(stream))
|
||||
.then(async stream => {
|
||||
// In case screen sharing audio is also shared we mix it with new input stream. The old _mixerEffect
|
||||
// will be cleaned up when the existing track is replaced.
|
||||
if (this._mixerEffect) {
|
||||
this._mixerEffect = new AudioMixerEffect(this._desktopAudioStream);
|
||||
|
||||
await stream.setEffect(this._mixerEffect);
|
||||
}
|
||||
|
||||
return this.useAudioStream(stream);
|
||||
})
|
||||
.then(() => {
|
||||
logger.log('switched local audio device');
|
||||
logger.log(`switched local audio device: ${this.localAudio?.getDeviceId()}`);
|
||||
|
||||
this._updateAudioDeviceId();
|
||||
})
|
||||
@@ -2640,12 +2830,6 @@ export default {
|
||||
// audio devices detected or if the local audio stream already exists.
|
||||
const available = audioDeviceCount > 0 || Boolean(this.localAudio);
|
||||
|
||||
logger.debug(
|
||||
`Microphone button enabled: ${available}`,
|
||||
`local audio: ${this.localAudio}`,
|
||||
`audio devices: ${audioMediaDevices}`,
|
||||
`device count: ${audioDeviceCount}`);
|
||||
|
||||
APP.store.dispatch(setAudioAvailable(available));
|
||||
APP.API.notifyAudioAvailabilityChanged(available);
|
||||
},
|
||||
@@ -2666,12 +2850,6 @@ export default {
|
||||
// config).
|
||||
const available = videoDeviceCount > 0 || Boolean(this.localVideo);
|
||||
|
||||
logger.debug(
|
||||
`Camera button enabled: ${available}`,
|
||||
`local video: ${this.localVideo}`,
|
||||
`video devices: ${videoMediaDevices}`,
|
||||
`device count: ${videoDeviceCount}`);
|
||||
|
||||
APP.store.dispatch(setVideoAvailable(available));
|
||||
APP.API.notifyVideoAvailabilityChanged(available);
|
||||
},
|
||||
@@ -2737,7 +2915,7 @@ export default {
|
||||
leaveRoomAndDisconnect() {
|
||||
APP.store.dispatch(conferenceWillLeave(room));
|
||||
|
||||
if (room.isJoined()) {
|
||||
if (room && room.isJoined()) {
|
||||
return room.leave().then(disconnect, disconnect);
|
||||
}
|
||||
|
||||
|
||||
85
config.js
@@ -44,9 +44,6 @@ var config = {
|
||||
//
|
||||
|
||||
testing: {
|
||||
// Enables experimental simulcast support on Firefox.
|
||||
enableFirefoxSimulcast: false,
|
||||
|
||||
// P2P test mode disables automatic switching to P2P when there are 2
|
||||
// participants in the conference.
|
||||
p2pTestMode: false
|
||||
@@ -75,6 +72,7 @@ var config = {
|
||||
|
||||
// Disable measuring of audio levels.
|
||||
// disableAudioLevels: false,
|
||||
// audioLevelsInterval: 200,
|
||||
|
||||
// Enabling this will run the lib-jitsi-meet no audio detection module which
|
||||
// will notify the user if the current selected microphone has no audio
|
||||
@@ -109,12 +107,11 @@ var config = {
|
||||
|
||||
// w3c spec-compliant video constraints to use for video capture. Currently
|
||||
// used by browsers that return true from lib-jitsi-meet's
|
||||
// util#browser#usesNewGumFlow. The constraints are independency from
|
||||
// this config's resolution value. Defaults to requesting an ideal aspect
|
||||
// ratio of 16:9 with an ideal resolution of 720.
|
||||
// util#browser#usesNewGumFlow. The constraints are independent from
|
||||
// this config's resolution value. Defaults to requesting an ideal
|
||||
// resolution of 720p.
|
||||
// constraints: {
|
||||
// video: {
|
||||
// aspectRatio: 16 / 9,
|
||||
// height: {
|
||||
// ideal: 720,
|
||||
// max: 720,
|
||||
@@ -229,6 +226,14 @@ var config = {
|
||||
// disabled, then bandwidth estimations are disabled.
|
||||
// enableRemb: false,
|
||||
|
||||
// Enables ICE restart logic in LJM and displays the page reload overlay on
|
||||
// ICE failure. Current disabled by default because it's causing issues with
|
||||
// signaling when Octo is enabled. Also when we do an "ICE restart"(which is
|
||||
// not a real ICE restart), the client maintains the TCC sequence number
|
||||
// counter, but the bridge resets it. The bridge sends media packets with
|
||||
// TCC sequence numbers starting from 0.
|
||||
// enableIceRestart: false,
|
||||
|
||||
// Defines the minimum number of participants to start a call (the default
|
||||
// is set in Jicofo and set to 2).
|
||||
// minParticipants: 2,
|
||||
@@ -292,6 +297,9 @@ var config = {
|
||||
// and microsoftApiApplicationClientID
|
||||
// enableCalendarIntegration: false,
|
||||
|
||||
// When 'true', it shows an intermediate page before joining, where the user can configure its devices.
|
||||
// prejoinPageEnabled: false,
|
||||
|
||||
// Stats
|
||||
//
|
||||
|
||||
@@ -301,16 +309,19 @@ var config = {
|
||||
// estimation tests.
|
||||
// gatherStats: false,
|
||||
|
||||
// The interval at which PeerConnection.getStats() is called. Defaults to 10000
|
||||
// pcStatsInterval: 10000,
|
||||
|
||||
// To enable sending statistics to callstats.io you must provide the
|
||||
// Application ID and Secret.
|
||||
// callStatsID: '',
|
||||
// callStatsSecret: '',
|
||||
|
||||
// enables sending participants display name to callstats
|
||||
// enableDisplayNameInStats: false
|
||||
// enableDisplayNameInStats: false,
|
||||
|
||||
// enables sending participants email if available to callstats and other analytics
|
||||
// enableEmailInStats: false
|
||||
// enableEmailInStats: false,
|
||||
|
||||
// Privacy
|
||||
//
|
||||
@@ -339,11 +350,9 @@ var config = {
|
||||
// The STUN servers that will be used in the peer to peer connections
|
||||
stunServers: [
|
||||
|
||||
// { urls: 'stun:jitsi-meet.example.com:443' },
|
||||
{ urls: 'stun:stun.l.google.com:19302' },
|
||||
{ urls: 'stun:stun1.l.google.com:19302' },
|
||||
{ urls: 'stun:stun2.l.google.com:19302' }
|
||||
],
|
||||
// { urls: 'stun:jitsi-meet.example.com:4446' },
|
||||
{ urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
|
||||
]
|
||||
|
||||
// Sets the ICE transport policy for the p2p connection. At the time
|
||||
// of this writing the list of possible values are 'all' and 'relay',
|
||||
@@ -355,7 +364,7 @@ var config = {
|
||||
|
||||
// If set to true, it will prefer to use H.264 for P2P calls (if H.264
|
||||
// is supported).
|
||||
preferH264: true
|
||||
// preferH264: true
|
||||
|
||||
// If set to true, disable H.264 video codec by stripping it out of the
|
||||
// SDP.
|
||||
@@ -370,6 +379,10 @@ var config = {
|
||||
// The Google Analytics Tracking ID:
|
||||
// googleAnalyticsTrackingId: 'your-tracking-id-UA-123456-1'
|
||||
|
||||
// Matomo configuration:
|
||||
// matomoEndpoint: 'https://your-matomo-endpoint/',
|
||||
// matomoSiteID: '42',
|
||||
|
||||
// The Amplitude APP Key:
|
||||
// amplitudeAPPKey: '<APP_KEY>'
|
||||
|
||||
@@ -386,7 +399,10 @@ var config = {
|
||||
// shard: "shard1",
|
||||
// region: "europe",
|
||||
// userRegion: "asia"
|
||||
}
|
||||
},
|
||||
|
||||
// Decides whether the start/stop recording audio notifications should play on record.
|
||||
// disableRecordAudioNotification: false,
|
||||
|
||||
// Information for the chrome extension banner
|
||||
// chromeExtensionBanner: {
|
||||
@@ -400,7 +416,7 @@ var config = {
|
||||
// path: 'jitsi-logo-48x48.png'
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// },
|
||||
|
||||
// Local Recording
|
||||
//
|
||||
@@ -418,7 +434,7 @@ var config = {
|
||||
// format: 'flac'
|
||||
//
|
||||
|
||||
// }
|
||||
// },
|
||||
|
||||
// Options related to end-to-end (participant to participant) ping.
|
||||
// e2eping: {
|
||||
@@ -430,22 +446,30 @@ var config = {
|
||||
// // with the measured RTT will be sent. Defaults to 60000, set
|
||||
// // to <= 0 to disable.
|
||||
// analyticsInterval: 60000,
|
||||
// }
|
||||
// },
|
||||
|
||||
// If set, will attempt to use the provided video input device label when
|
||||
// triggering a screenshare, instead of proceeding through the normal flow
|
||||
// for obtaining a desktop stream.
|
||||
// NOTE: This option is experimental and is currently intended for internal
|
||||
// use only.
|
||||
// _desktopSharingSourceDevice: 'sample-id-or-label'
|
||||
// _desktopSharingSourceDevice: 'sample-id-or-label',
|
||||
|
||||
// If true, any checks to handoff to another application will be prevented
|
||||
// and instead the app will continue to display in the current browser.
|
||||
// disableDeepLinking: false
|
||||
// disableDeepLinking: false,
|
||||
|
||||
// A property to disable the right click context menu for localVideo
|
||||
// the menu has option to flip the locally seen video for local presentations
|
||||
// disableLocalVideoFlip: false
|
||||
// disableLocalVideoFlip: false,
|
||||
|
||||
// Mainly privacy related settings
|
||||
|
||||
// Disables all invite functions from the app (share, invite, dial out...etc)
|
||||
// disableInviteFunctions: true,
|
||||
|
||||
// Disables storing the room name to the recents list
|
||||
// doNotStoreRoom: true,
|
||||
|
||||
// Deployment specific URLs.
|
||||
// deploymentUrls: {
|
||||
@@ -455,7 +479,16 @@ var config = {
|
||||
// // If specified a 'Download our apps' button will be displayed in the overflow menu with a link
|
||||
// // to the specified URL for an app download page.
|
||||
// downloadAppsUrl: 'https://docs.example.com/our-apps.html'
|
||||
// }
|
||||
// },
|
||||
|
||||
// Options related to the remote participant menu.
|
||||
// remoteVideoMenu: {
|
||||
// // If set to true the 'Kick out' button will be disabled.
|
||||
// disableKick: true
|
||||
// },
|
||||
|
||||
// If set to true all muting operations of remote participants will be disabled.
|
||||
// disableRemoteMute: true,
|
||||
|
||||
// List of undocumented settings used in jitsi-meet
|
||||
/**
|
||||
@@ -507,6 +540,12 @@ var config = {
|
||||
startBitrate
|
||||
*/
|
||||
|
||||
|
||||
// Allow all above example options to include a trailing comma and
|
||||
// prevent fear when commenting out the last value.
|
||||
makeJsonParserHappy: 'even if last key had a trailing comma'
|
||||
|
||||
// no configuration value should follow this line.
|
||||
};
|
||||
|
||||
/* eslint-enable no-unused-vars, no-var */
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/* global APP, JitsiMeetJS, config */
|
||||
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
import jitsiLocalStorage from './modules/util/JitsiLocalStorage';
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
import { jitsiLocalStorage } from 'js-utils';
|
||||
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
import {
|
||||
connectionEstablished,
|
||||
connectionFailed
|
||||
@@ -13,7 +14,14 @@ import {
|
||||
JitsiConnectionEvents
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
/**
|
||||
* The feature announced so we can distinguish jibri participants.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const DISCO_JIBRI_FEATURE = 'http://jitsi.org/protocol/jibri';
|
||||
|
||||
/**
|
||||
* Checks if we have data to use attach instead of connect. If we have the data
|
||||
@@ -91,6 +99,10 @@ function connect(id, password, roomName) {
|
||||
jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
|
||||
connectionConfig);
|
||||
|
||||
if (config.iAmRecorder) {
|
||||
connection.addFeature(DISCO_JIBRI_FEATURE);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
connection.addEventListener(
|
||||
JitsiConnectionEvents.CONNECTION_ESTABLISHED,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* global config, createConnectionExternally */
|
||||
|
||||
import getRoomName from '../react/features/base/config/getRoomName';
|
||||
import parseURLParams from '../react/features/base/config/parseURLParams';
|
||||
import { parseURLParams } from '../react/features/base/util/parseURLParams';
|
||||
|
||||
/**
|
||||
* Implements external connect using createConnectionExternally function defined
|
||||
|
||||
135
css/_audio-preview.css
Normal file
@@ -0,0 +1,135 @@
|
||||
.audio-preview {
|
||||
&-content {
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
max-height: 456px;
|
||||
overflow: auto;
|
||||
width: 328px;
|
||||
}
|
||||
|
||||
&-header {
|
||||
color: #fff;
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
|
||||
&-icon {
|
||||
color: #A4B8D1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&-text {
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&-entry {
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
padding: 12px 0;
|
||||
margin-left: 48px;
|
||||
|
||||
&--selected {
|
||||
background: rgba(28,32,37,0.5);
|
||||
cursor: initial;
|
||||
margin-left: 0;
|
||||
padding-left: 21px;
|
||||
}
|
||||
|
||||
&-text {
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
display: inline-block;
|
||||
line-height: 24px;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 213px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&-speaker {
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
.audio-preview-entry {
|
||||
background: rgba(255,255,255, 0.2);
|
||||
margin-left: 0;
|
||||
padding-left: 48px;
|
||||
|
||||
&--selected {
|
||||
padding-left: 21px;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-preview-test-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.audio-preview-entry-text {
|
||||
max-width: 196px;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-preview-entry-text {
|
||||
max-width: 256px;
|
||||
}
|
||||
}
|
||||
|
||||
&-microphone {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
&-icon {
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
|
||||
& svg {
|
||||
fill: #1C2025;
|
||||
}
|
||||
|
||||
&--check {
|
||||
background: #31B76A;
|
||||
margin-right: 13px;
|
||||
}
|
||||
|
||||
&--exclamation {
|
||||
margin-left: 6px;
|
||||
& svg {
|
||||
fill: #E54B4B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-test-button {
|
||||
display: none;
|
||||
background: #FFF;
|
||||
border: 1px solid #D1DBE8;
|
||||
border-radius: 3px;
|
||||
color: #1C2025;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
padding: 4px 16px;
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
&-meter-mic {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 18px;
|
||||
}
|
||||
|
||||
// Override @atlaskit/InlineDialog container which is made with styled components
|
||||
& > div > div:nth-child(2) > div > div {
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
@@ -20,18 +20,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-foreign {
|
||||
align-items: center;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
font-size: 40pt;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.avatar-svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@@ -63,4 +51,4 @@
|
||||
@include avatarBadge;
|
||||
background-color: $presence-idle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
75
css/_country-picker.scss
Normal file
@@ -0,0 +1,75 @@
|
||||
.cpick {
|
||||
border: 1px solid #A4B8D1;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 15px;
|
||||
height: 38px;
|
||||
line-height: 24px;
|
||||
|
||||
&-selector {
|
||||
align-items: center;
|
||||
background-color: #283447;
|
||||
border-right: 1px solid #A4B8D1;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
padding: 8px 10px;
|
||||
position: relative;
|
||||
width: 88px;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
margin-right: 8px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 12px;
|
||||
|
||||
& > svg {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&-input {
|
||||
padding: 8px;
|
||||
background: #1C2025;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
caret-color: #0376DA;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&-dropdown {
|
||||
height: 190px;
|
||||
overflow-y: auto;
|
||||
width: 343px;
|
||||
}
|
||||
|
||||
&-dropdown-entry {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
padding: 0 10px;
|
||||
|
||||
&:hover {
|
||||
background-color: #66768b;
|
||||
}
|
||||
|
||||
&-text {
|
||||
color: #fff;
|
||||
flex-grow: 1;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override @Atlaskit/inline-dialog styles
|
||||
.cpick-container > div > div:nth-child(2) > div > div {
|
||||
outline: none;
|
||||
padding: 8px 0 0 0;
|
||||
}
|
||||
68
css/_labels.scss
Normal file
@@ -0,0 +1,68 @@
|
||||
.large-video-labels {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 30px;
|
||||
transition: right 0.5s;
|
||||
z-index: $zindex3;
|
||||
|
||||
.circular-label {
|
||||
align-items: center;
|
||||
color: white;
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
justify-content: center;
|
||||
margin-left: 8px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.circular-label {
|
||||
background: #B8C7E0;
|
||||
}
|
||||
|
||||
.circular-label.e2ee {
|
||||
align-items: center;
|
||||
background: #76CF9C;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.circular-label.file {
|
||||
background: #FF5630;
|
||||
}
|
||||
|
||||
.circular-label.local-rec {
|
||||
background: #FF5630;
|
||||
}
|
||||
|
||||
.circular-label.stream {
|
||||
background: #0065FF;
|
||||
}
|
||||
|
||||
.circular-label.insecure {
|
||||
background: $defaultWarningColor;
|
||||
}
|
||||
|
||||
.recording-label.center-message {
|
||||
background: $videoStateIndicatorBackground;
|
||||
bottom: 50%;
|
||||
display: block;
|
||||
left: 50%;
|
||||
padding: 10px;
|
||||
position: fixed;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: $centeredVideoLabelZ;
|
||||
}
|
||||
}
|
||||
|
||||
.circular-label {
|
||||
background: $videoStateIndicatorBackground;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
font-size: 13px;
|
||||
height: $videoStateIndicatorSize;
|
||||
line-height: $videoStateIndicatorSize;
|
||||
text-align: center;
|
||||
min-width: $videoStateIndicatorSize;
|
||||
}
|
||||
30
css/_meter.css
Normal file
@@ -0,0 +1,30 @@
|
||||
.jitsi-icon {
|
||||
&.metr {
|
||||
display: inline-block;
|
||||
|
||||
& > svg {
|
||||
fill: #4E5E6C;
|
||||
width: 38px;
|
||||
}
|
||||
}
|
||||
|
||||
&.metr--disabled {
|
||||
& > svg {
|
||||
fill: #4E5E6C;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.metr-l-0 {
|
||||
rect:first-child {
|
||||
fill: #31B76A;
|
||||
}
|
||||
}
|
||||
|
||||
@for $i from 1 through 7 {
|
||||
.metr-l-#{$i} {
|
||||
rect:nth-child(-n+#{$i+1}) {
|
||||
fill: #31B76A;
|
||||
}
|
||||
}
|
||||
}
|
||||
182
css/_prejoin-dialog.scss
Normal file
@@ -0,0 +1,182 @@
|
||||
.prejoin-dialog {
|
||||
background: #1C2025;
|
||||
box-shadow: 0px 2px 20px rgba(0, 0, 0, 0.5);
|
||||
border-radius: 5px;
|
||||
color: #fff;
|
||||
height: 400px;
|
||||
width: 375px;
|
||||
|
||||
&--small {
|
||||
height: 300;
|
||||
width: 400;
|
||||
}
|
||||
|
||||
&-label {
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
|
||||
&-num {
|
||||
background: #2b3b4b;
|
||||
border: 1px solid #A4B8D1;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
margin-right: 8px;
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&-container {
|
||||
align-items: center;
|
||||
background: rgba(0,0,0,0.6);
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
&-flag {
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
&-title {
|
||||
display: inline-block;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
cursor: pointer;
|
||||
|
||||
> svg {
|
||||
fill: #A4B8D1;
|
||||
}
|
||||
}
|
||||
|
||||
&-btn {
|
||||
width: 309px;
|
||||
}
|
||||
|
||||
&-dialin-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-delimiter {
|
||||
background: #5f6266;
|
||||
border: 0;
|
||||
height: 1px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
|
||||
&-container {
|
||||
margin: 16px 0 24px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-txt-container {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top: -8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-txt {
|
||||
background: #1C2025;
|
||||
color: #5f6266;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-dialog-callout {
|
||||
padding: 16px;
|
||||
|
||||
&-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&-picker {
|
||||
margin: 8px 0 16px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-dialog-dialin {
|
||||
text-align: center;
|
||||
|
||||
&-header {
|
||||
align-items: center;
|
||||
margin: 16px 0 32px 16px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&-num {
|
||||
background: #3e474f;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
margin: 4px;
|
||||
padding: 8px;
|
||||
|
||||
&-container {
|
||||
min-height: 48px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-link {
|
||||
color: #6FB1EA;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&-spaced-label {
|
||||
margin-bottom: 16px;
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
&-btns {
|
||||
&> div {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-dialog-calling {
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
|
||||
&-header {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&-label {
|
||||
font-size: 15px;
|
||||
margin: 8px 0 16px 0;
|
||||
}
|
||||
|
||||
&-number {
|
||||
font-size: 19px;
|
||||
line-height: 28px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
353
css/_prejoin.scss
Normal file
@@ -0,0 +1,353 @@
|
||||
.prejoin {
|
||||
&-full-page {
|
||||
background: #1C2025;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: $toolbarZ + 1;
|
||||
}
|
||||
|
||||
&-input-area-container {
|
||||
position: absolute;
|
||||
bottom: 48px;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&-input-area {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
&-title {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
&-btn {
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
padding: 7px 16px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 286px;
|
||||
|
||||
&--primary {
|
||||
background: #0376DA;
|
||||
border: 1px solid #0376DA;
|
||||
}
|
||||
|
||||
&--secondary {
|
||||
background: #2A3A4B;
|
||||
border: 1px solid #5E6D7A;
|
||||
}
|
||||
|
||||
&--text {
|
||||
width: auto;
|
||||
font-size: 13px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
background: #5E6D7A;
|
||||
border: 1px solid #5E6D7A;
|
||||
color: #AFB6BC;
|
||||
cursor: initial;
|
||||
|
||||
.prejoin-btn-icon {
|
||||
& > svg {
|
||||
fill: #AFB6BC;
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-btn-options {
|
||||
border-left: 1px solid #AFB6BC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-btn-options {
|
||||
align-items: center;
|
||||
border-left: 1px solid #fff;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
&-text-btns {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&-input-label {
|
||||
color: #A4B8D1;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin-top: 32px 0 8px 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-checkbox {
|
||||
border: 0;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
padding: 0;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
&-checkbox-container {
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
display: none;
|
||||
font-size: 13px;
|
||||
justify-content: center;
|
||||
line-height: 20px;
|
||||
margin-top: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin name-placeholder {
|
||||
color: #fff;
|
||||
font-weight: 300;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.prejoin-preview {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
&--no-video {
|
||||
background: radial-gradient(50% 50% at 50% 50%, #5B6F80 0%, #365067 100%), #FFFFFF;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-video {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-name {
|
||||
color: #fff;
|
||||
font-size: 19px;
|
||||
line-height: 28px;
|
||||
|
||||
&--editable {
|
||||
background: none;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #D1DBE8;
|
||||
margin: 24px 0 16px 0;
|
||||
outline: none;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
@include name-placeholder;
|
||||
}
|
||||
&::-moz-placeholder {
|
||||
@include name-placeholder;
|
||||
}
|
||||
&:-ms-input-placeholder {
|
||||
@include name-placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
&--text {
|
||||
margin: 16px 0;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-avatar.avatar {
|
||||
background: #A4B8D1;
|
||||
margin: 200px auto 0 auto;
|
||||
}
|
||||
|
||||
&-btn-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 32px;
|
||||
width: 100%;
|
||||
|
||||
&> div {
|
||||
margin: 0 12px;
|
||||
}
|
||||
|
||||
.settings-button-small-icon {
|
||||
right: -8px;
|
||||
|
||||
&--hovered {
|
||||
right: -10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-overlay {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
background: linear-gradient(0deg, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3));
|
||||
}
|
||||
|
||||
&-bottom-overlay {
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.9) 100%);
|
||||
bottom: 0;
|
||||
height: 50%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&-status {
|
||||
align-items: center;
|
||||
bottom: 0;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
min-height: 24px;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
|
||||
&--warning {
|
||||
background: rgba(241, 173, 51, 0.5)
|
||||
}
|
||||
&--ok {
|
||||
background: rgba(49, 183, 106, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
&-error-desc {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.settings-button-container {
|
||||
width: 49px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
&-dropdown-btns {
|
||||
width: 320px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
&-dropdown-btn {
|
||||
align-items: center;
|
||||
color: #1C2025;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
padding: 0 16px;
|
||||
|
||||
&:hover {
|
||||
background-color: #DAEBFA;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-icon {
|
||||
display: inline-block;
|
||||
margin-right: 16px;
|
||||
|
||||
& > svg {
|
||||
fill: #1C2025;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-container {
|
||||
& > div > div:nth-child(2) > div > div {
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.prejoin-copy {
|
||||
&-meeting {
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
font-weight: 300;
|
||||
line-height: 24px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-url {
|
||||
max-width: 278px;
|
||||
padding: 8px 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&-badge {
|
||||
border-radius: 4px;
|
||||
height: 100%;
|
||||
line-height: 38px;
|
||||
position: absolute;
|
||||
padding-left: 10px;
|
||||
text-align: left;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
|
||||
&--hover {
|
||||
background: #1C2025;
|
||||
}
|
||||
|
||||
&--done {
|
||||
background: #31B76A;
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 8px;
|
||||
|
||||
&--white {
|
||||
&> svg > path {
|
||||
fill: #fff
|
||||
}
|
||||
}
|
||||
|
||||
&--light {
|
||||
&> svg > path {
|
||||
fill: #D1DBE8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-textarea {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,11 @@
|
||||
display: flex;
|
||||
margin-top: 5px;
|
||||
text-align: right;
|
||||
flex-direction: column;
|
||||
|
||||
.help-container {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.live-stream-cta {
|
||||
|
||||
80
css/_settings-button.scss
Normal file
@@ -0,0 +1,80 @@
|
||||
.settings-button {
|
||||
&-container {
|
||||
position: relative;
|
||||
|
||||
.toolbox-icon {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #d1dbe8;
|
||||
justify-content: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
|
||||
&:hover {
|
||||
background-color: #daebfa;
|
||||
border: 1px solid #daebfa;
|
||||
}
|
||||
|
||||
&.toggled {
|
||||
background: #2a3a4b;
|
||||
border: 1px solid #5e6d7a;
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #5e6d7a;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled, .disabled & {
|
||||
cursor: initial;
|
||||
color: #fff;
|
||||
background-color: #a4b8d1;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #5e6d7a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-small-icon {
|
||||
background: #FFF;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-radius: 50%;
|
||||
bottom: 0;
|
||||
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.25);
|
||||
cursor: pointer;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
right: 4px;
|
||||
width: 16px;
|
||||
|
||||
&> svg {
|
||||
fill: #5e6d7a;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
background-color: #a4b8d1;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&--hovered {
|
||||
bottom: -1px;
|
||||
height: 20px;
|
||||
right: 2px;
|
||||
width: 20px;
|
||||
|
||||
&> svg {
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
70
css/_video-preview.css
Normal file
@@ -0,0 +1,70 @@
|
||||
.video-preview {
|
||||
background: none;
|
||||
max-height: 290px;
|
||||
|
||||
&-container {
|
||||
overflow: auto;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
&-entry {
|
||||
cursor: pointer;
|
||||
height: 135px;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
width: 240px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&--selected {
|
||||
border: 3px solid #31B76A;
|
||||
cursor: default;
|
||||
height: 129px;
|
||||
width: 234px;
|
||||
}
|
||||
}
|
||||
|
||||
&-video {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-overlay {
|
||||
background: rgba(42, 58, 75, 0.6);
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&-error {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-label {
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
overflow: hidden;
|
||||
padding: 8px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
width: 220px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
// Override @atlaskit/InlineDialog container which is made with styled components
|
||||
& > div > div:nth-child(2) > div > div {
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
@@ -181,12 +181,6 @@
|
||||
visibility: hidden;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
&.audio-only {
|
||||
.videoThumbnailProblemFilter {
|
||||
filter: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#localVideoWrapper {
|
||||
@@ -573,21 +567,6 @@
|
||||
filter: grayscale(.5) opacity(0.8);
|
||||
}
|
||||
|
||||
.remoteVideoProblemFilter {
|
||||
-webkit-filter: grayscale(100%);
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
.videoProblemFilter {
|
||||
-webkit-filter: blur(10px) grayscale(.5) opacity(0.8);
|
||||
filter: blur(10px) grayscale(.5) opacity(0.8);
|
||||
}
|
||||
|
||||
.videoThumbnailProblemFilter {
|
||||
-webkit-filter: grayscale(100%);
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
#remotePresenceMessage,
|
||||
#remoteConnectionMessage {
|
||||
position: absolute;
|
||||
@@ -618,24 +597,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
#localConnectionMessage {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
top:50%;
|
||||
z-index: $zindex2;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
color: #FFF;
|
||||
opacity: .80;
|
||||
text-shadow: 0px 0px 1px rgba(0,0,0,0.3),
|
||||
0px 1px 1px rgba(0,0,0,0.3),
|
||||
1px 0px 1px rgba(0,0,0,0.3),
|
||||
0px 0px 1px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.display-avatar-with-name {
|
||||
.avatar-container {
|
||||
visibility: visible;
|
||||
|
||||
@@ -71,9 +71,6 @@ body.welcome-page {
|
||||
text-align: left;
|
||||
color: #253858;
|
||||
height: fit-content;
|
||||
border-width: $welcomePageEnterRoomInputContainerBorderWidth;
|
||||
border-style: $welcomePageEnterRoomInputContainerBorderStyle;
|
||||
border-image: $welcomePageEnterRoomInputContainerBorderImage;
|
||||
|
||||
.enter-room-title {
|
||||
display: $welcomePageEnterRoomTitleDisplay;
|
||||
@@ -83,12 +80,26 @@ body.welcome-page {
|
||||
}
|
||||
|
||||
.enter-room-input {
|
||||
border: none;
|
||||
border-width: $welcomePageEnterRoomInputContainerBorderWidth;
|
||||
border-style: $welcomePageEnterRoomInputContainerBorderStyle;
|
||||
border-image: $welcomePageEnterRoomInputContainerBorderImage;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.insecure-room-name-warning {
|
||||
align-items: center;
|
||||
color: $defaultWarningColor;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 5px;
|
||||
|
||||
svg {
|
||||
fill: $defaultWarningColor
|
||||
}
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #253858;
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
height: calc(100vh - 200px);
|
||||
width: 100vw;
|
||||
margin: 100px 0px;
|
||||
}
|
||||
|
||||
.filmstrip__videos .videocontainer {
|
||||
@@ -77,9 +78,9 @@
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
height: 100vh;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
justify-content: center;
|
||||
padding: 100px 0;
|
||||
|
||||
.videocontainer {
|
||||
border: 0;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
#localConnectionMessage,
|
||||
#remoteConnectionMessage,
|
||||
.watermark {
|
||||
z-index: $filmstripVideosZ + 1;
|
||||
|
||||
@@ -136,6 +136,13 @@
|
||||
display: flex;
|
||||
transition: opacity 1s;
|
||||
}
|
||||
|
||||
.hide-scrollbar#filmstripRemoteVideos {
|
||||
margin-right: 7px; // Scrollbar size
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,6 +75,7 @@ $flagsImagePath: "../images/";
|
||||
@import 'filmstrip/tile_view_overrides';
|
||||
@import 'filmstrip/vertical_filmstrip';
|
||||
@import 'filmstrip/vertical_filmstrip_overrides';
|
||||
@import 'labels';
|
||||
@import 'unsupported-browser/main';
|
||||
@import 'modals/invite/add-people';
|
||||
@import 'deep-linking/main';
|
||||
@@ -86,5 +87,14 @@ $flagsImagePath: "../images/";
|
||||
@import 'avatar';
|
||||
@import 'promotional-footer';
|
||||
@import 'chrome-extension-banner';
|
||||
@import 'settings-button';
|
||||
@import 'meter';
|
||||
@import 'audio-preview';
|
||||
@import 'video-preview';
|
||||
@import 'prejoin';
|
||||
@import 'prejoin-dialog';
|
||||
@import 'country-picker';
|
||||
@import 'modals/invite/invite_more';
|
||||
@import 'modals/security/security';
|
||||
|
||||
/* Modules END */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
.modal-dialog-form {
|
||||
.add-people-form-wrap {
|
||||
margin-top: 8px;
|
||||
|
||||
.error {
|
||||
padding-left: 5px;
|
||||
|
||||
@@ -3,47 +3,6 @@
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
|
||||
.info-dialog-action-link {
|
||||
display: inline-block;
|
||||
line-height: 1.5em;
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.info-dialog-action-link:before {
|
||||
color: $linkFontColor;
|
||||
content: '\2022';
|
||||
font-size: 1.5em;
|
||||
padding: 0 10px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.info-dialog-action-link:first-child:before {
|
||||
content: '';
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.info-dialog-action-links {
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.info-dialog-action-separator {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.info-dialog-copy-element {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.info-dialog-column {
|
||||
margin-right: 10px;
|
||||
overflow: hidden;
|
||||
@@ -56,52 +15,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.info-dialog-conference-url,
|
||||
.info-dialog-live-stream-url {
|
||||
width: max-content;
|
||||
width: -moz-max-content;
|
||||
width: -webkit-max-content;
|
||||
word-break: break-all;
|
||||
max-width: 400px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info-dialog-dial-in {
|
||||
word-break: break-all;
|
||||
|
||||
.conference-id,
|
||||
.phone-number {
|
||||
user-select: text;
|
||||
}
|
||||
}
|
||||
|
||||
.info-dialog-icon {
|
||||
color: #6453C0;
|
||||
font-size: 16px;
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
.info-dialog-url-text,
|
||||
.info-dialog-url-text:hover {
|
||||
color: inherit;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.info-dialog-url-icon {
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
|
||||
svg {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.info-dialog-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.info-dialog-password,
|
||||
.info-password,
|
||||
.info-password-form {
|
||||
@@ -125,6 +38,7 @@
|
||||
}
|
||||
|
||||
.info-password-input {
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: inherit;
|
||||
@@ -223,10 +137,4 @@
|
||||
-moz-user-select: text;
|
||||
-webkit-user-select: text;
|
||||
}
|
||||
|
||||
.info-dialog-url-text-unselectable {
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
252
css/modals/invite/_invite_more.scss
Normal file
@@ -0,0 +1,252 @@
|
||||
.invite-more {
|
||||
&-container {
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
z-index: $zindex2;
|
||||
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
|
||||
|
||||
&.elevated {
|
||||
z-index: $filmstripVideosZ + 1;
|
||||
}
|
||||
}
|
||||
|
||||
&-header {
|
||||
font-size: 19px;
|
||||
line-height: 28px;
|
||||
margin: 24px 0 16px 0;
|
||||
}
|
||||
|
||||
&-button {
|
||||
display: flex;
|
||||
margin: auto;
|
||||
padding: 8px 16px;
|
||||
width: fit-content;
|
||||
width: -moz-fit-content;
|
||||
height: 24px;
|
||||
background: #0376DA;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #278ADF;
|
||||
}
|
||||
|
||||
&-text {
|
||||
margin-left: 8px;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
&-dialog {
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
|
||||
& > span {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 16px 16px 24px;
|
||||
width: calc(100% - 32px);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
|
||||
& > div > svg {
|
||||
cursor: pointer;
|
||||
fill: #A4B8D1;
|
||||
}
|
||||
}
|
||||
|
||||
&.copy-link {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 8px 8px 16px;
|
||||
margin-top: 8px;
|
||||
width: calc(100% - 24px);
|
||||
height: 24px;
|
||||
|
||||
background: #0376DA;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #278ADF;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&-text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 292px;
|
||||
|
||||
&.selected {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&.clicked {
|
||||
background: #31B76A;
|
||||
}
|
||||
|
||||
& > div > svg > path {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&.separator {
|
||||
margin: 24px 0 24px -20px;
|
||||
padding: 0 20px;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: #5E6D7A;
|
||||
}
|
||||
|
||||
&.email-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 8px 8px 16px;
|
||||
margin-top: 24px;
|
||||
width: calc(100% - 26px);
|
||||
height: 22px;
|
||||
|
||||
background: #2A3A4B;
|
||||
border: 1px solid #5E6D7A;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.icon-container {
|
||||
display: none;
|
||||
|
||||
&.active {
|
||||
display: flex;
|
||||
width: calc(100% - 26px);
|
||||
padding: 8px 8px 8px 16px;
|
||||
|
||||
background: #2A3A4B;
|
||||
border: 1px solid #5E6D7A;
|
||||
border-top: none;
|
||||
border-radius: 0 0 3px 3px;
|
||||
|
||||
& > * {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:hover > div:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
& > :not(:last-child) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.copy-invite-icon > div > svg > path {
|
||||
fill: #A4B8D1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.dial-in-display {
|
||||
.info-label {
|
||||
color: #A4B8D1;
|
||||
}
|
||||
|
||||
.dial-in-copy {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-left: 21px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&.invite-buttons {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
margin-top: 8px;
|
||||
|
||||
& > a {
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
width: 48px;
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&-cancel {
|
||||
margin-right: 16px;
|
||||
padding: 7px 15px;
|
||||
background: #2A3A4B;
|
||||
border: 1px solid #5E6D7A;
|
||||
}
|
||||
|
||||
&-add {
|
||||
padding: 8px 16px;
|
||||
background: #0376DA;
|
||||
}
|
||||
}
|
||||
|
||||
&.stream {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 8px 8px 16px;
|
||||
margin-top: 8px;
|
||||
width: calc(100% - 26px);
|
||||
height: 22px;
|
||||
|
||||
background: #2A3A4B;
|
||||
border: 1px solid #5E6D7A;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&-text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 292px;
|
||||
|
||||
&.selected {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&.clicked {
|
||||
background: #31B76A;
|
||||
border: 1px solid #31B76A;
|
||||
}
|
||||
|
||||
& > div > svg > path {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
css/modals/security/_security.scss
Normal file
@@ -0,0 +1,37 @@
|
||||
.security {
|
||||
&-dialog {
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
|
||||
&.password {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
&-actions {
|
||||
a {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
color: #6FB1EA;
|
||||
}
|
||||
|
||||
& > :first-child:not(:last-child) {
|
||||
margin-right: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new-toolbox .toolbox-content .toolbox-icon.security-toolbar-button,
|
||||
.new-toolbox .toolbox-content .toolbox-icon.toggled.security-toolbar-button {
|
||||
background: rgba(241, 173, 51, 0.7);
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
|
||||
&:hover {
|
||||
background: rgba(241, 173, 51, 0.7);
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
}
|
||||
@@ -144,58 +144,3 @@
|
||||
#videoResolutionLabel {
|
||||
z-index: $zindex3 + 1;
|
||||
}
|
||||
|
||||
.large-video-labels {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 30px;
|
||||
transition: right 0.5s;
|
||||
z-index: $zindex3;
|
||||
|
||||
.circular-label {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.circular-label {
|
||||
background: #B8C7E0;
|
||||
}
|
||||
|
||||
.circular-label.file {
|
||||
background: #FF5630;
|
||||
}
|
||||
|
||||
.circular-label.local-rec {
|
||||
background: #FF5630;
|
||||
}
|
||||
|
||||
.circular-label.stream {
|
||||
background: #0065FF;
|
||||
}
|
||||
|
||||
.recording-label.center-message {
|
||||
background: $videoStateIndicatorBackground;
|
||||
bottom: 50%;
|
||||
display: block;
|
||||
left: 50%;
|
||||
padding: 10px;
|
||||
position: fixed;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: $centeredVideoLabelZ;
|
||||
}
|
||||
}
|
||||
|
||||
.circular-label {
|
||||
background: $videoStateIndicatorBackground;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
font-size: 13px;
|
||||
height: $videoStateIndicatorSize;
|
||||
line-height: $videoStateIndicatorSize;
|
||||
text-align: center;
|
||||
min-width: $videoStateIndicatorSize;
|
||||
}
|
||||
|
||||
2
debian/compat
vendored
@@ -1 +1 @@
|
||||
8
|
||||
12
|
||||
|
||||
14
debian/control
vendored
@@ -3,7 +3,7 @@ Section: net
|
||||
Priority: extra
|
||||
Maintainer: Jitsi Team <dev@jitsi.org>
|
||||
Uploaders: Emil Ivov <emcho@jitsi.org>, Damian Minkov <damencho@jitsi.org>
|
||||
Build-Depends: debhelper (>= 8.0.0)
|
||||
Build-Depends: debhelper (>= 8.0.0), nodejs
|
||||
Standards-Version: 3.9.6
|
||||
Homepage: https://jitsi.org/meet
|
||||
|
||||
@@ -16,8 +16,7 @@ Description: WebRTC JavaScript video conferences
|
||||
Videobridge to provide high quality, scalable video conferences.
|
||||
.
|
||||
It is a web interface to Jitsi Videobridge for audio and video
|
||||
forwarding and relaying, configured to work with jetty instance
|
||||
running embedded into Jitsi Videobridge
|
||||
forwarding and relaying.
|
||||
|
||||
Package: jitsi-meet-web-config
|
||||
Architecture: all
|
||||
@@ -27,9 +26,7 @@ Description: Configuration for web serving of Jitsi Meet
|
||||
Videobridge to provide high quality, scalable video conferences.
|
||||
.
|
||||
It is a web interface to Jitsi Videobridge for audio and video
|
||||
forwarding and relaying, configured to work with jetty instance
|
||||
running embedded into Jitsi Videobridge or using a webserver Nginx or
|
||||
Apache2.
|
||||
forwarding and relaying, using a webserver Nginx or Apache2.
|
||||
.
|
||||
This package contains configuration for Nginx to be used with
|
||||
Jitsi Meet.
|
||||
@@ -43,8 +40,7 @@ Description: Prosody configuration for Jitsi Meet
|
||||
Videobridge to provide high quality, scalable video conferences.
|
||||
.
|
||||
It is a web interface to Jitsi Videobridge for audio and video
|
||||
forwarding and relaying, configured to work with jetty instance
|
||||
running embedded into Jitsi Videobridge
|
||||
forwarding and relaying.
|
||||
.
|
||||
This package contains configuration for Prosody to be used with
|
||||
Jitsi Meet.
|
||||
@@ -56,5 +52,7 @@ Description: Prosody token authentication plugin for Jitsi Meet
|
||||
|
||||
Package: jitsi-meet-turnserver
|
||||
Architecture: all
|
||||
Breaks: apache2
|
||||
Pre-Depends: jitsi-meet-web-config
|
||||
Depends: ${misc:Depends}, nginx (>= 1.13.10) | nginx-full (>= 1.13.10) | nginx-extras (>= 1.13.10), jitsi-meet-prosody, coturn, dnsutils
|
||||
Description: Configures coturn to be used with Jitsi Meet
|
||||
|
||||
24
debian/jitsi-meet-prosody.postinst
vendored
@@ -17,6 +17,9 @@ set -e
|
||||
# for details, see http://www.debian.org/doc/debian-policy/ or
|
||||
# the debian-policy package
|
||||
|
||||
function generateRandomPassword() {
|
||||
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 16
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
@@ -51,7 +54,7 @@ case "$1" in
|
||||
db_get jicofo/jicofo-authpassword
|
||||
if [ -z "$RET" ] ; then
|
||||
# if password is missing generate it, and store it
|
||||
JICOFO_AUTH_PASSWORD=`head -c 8 /dev/urandom | tr '\0-\377' 'a-zA-Z0-9a-zA-Z0-9a-zA-Z0-9a-zA-Z0-9@@@@####'`
|
||||
JICOFO_AUTH_PASSWORD=`generateRandomPassword`
|
||||
db_set jicofo/jicofo-authpassword "$JICOFO_AUTH_PASSWORD"
|
||||
else
|
||||
JICOFO_AUTH_PASSWORD="$RET"
|
||||
@@ -60,7 +63,7 @@ case "$1" in
|
||||
db_get jicofo/jicofosecret
|
||||
if [ -z "$RET" ] ; then
|
||||
# if secret is missing generate it, and store it
|
||||
JICOFO_SECRET=`head -c 8 /dev/urandom | tr '\0-\377' 'a-zA-Z0-9a-zA-Z0-9a-zA-Z0-9a-zA-Z0-9@@@@####'`
|
||||
JICOFO_SECRET=`generateRandomPassword`
|
||||
db_set jicofo/jicofosecret "$JICOFO_SECRET"
|
||||
else
|
||||
JICOFO_SECRET="$RET"
|
||||
@@ -83,7 +86,7 @@ case "$1" in
|
||||
db_get jitsi-meet-prosody/turn-secret
|
||||
if [ -z "$RET" ] ; then
|
||||
# 8-chars random secret used for the turnserver
|
||||
TURN_SECRET=`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1`
|
||||
TURN_SECRET=`generateRandomPassword`
|
||||
db_set jitsi-meet-prosody/turn-secret "$TURN_SECRET"
|
||||
else
|
||||
TURN_SECRET="$RET"
|
||||
@@ -134,9 +137,9 @@ case "$1" in
|
||||
# as we are migrating configs
|
||||
if [ -f $PROSODY_HOST_CONFIG ] && ! grep -q "internal.auth.$JVB_HOSTNAME" $PROSODY_HOST_CONFIG; then
|
||||
echo -e "\nComponent \"internal.auth.$JVB_HOSTNAME\" \"muc\"" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " storage = \"null\"" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " storage = \"memory\"" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " modules_enabled = { \"ping\"; }" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " admins = { \"focusUser@auth.jitmeet.example.com\", \"jvb@auth.jitmeet.example.com\" }" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " admins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\", \"jvb@auth.$JVB_HOSTNAME\" }" >> $PROSODY_HOST_CONFIG
|
||||
fi
|
||||
|
||||
if [ ! -f /var/lib/prosody/$JVB_HOSTNAME.crt ]; then
|
||||
@@ -148,14 +151,13 @@ case "$1" in
|
||||
ln -sf /var/lib/prosody/$JVB_HOSTNAME.crt /etc/prosody/certs/$JVB_HOSTNAME.crt
|
||||
fi
|
||||
|
||||
PR11_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-0.11' 2>/dev/null | awk '{print $3}' || true)"
|
||||
PRTRUNK_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-trunk' 2>/dev/null | awk '{print $3}' || true)"
|
||||
PR10_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-0.10' 2>/dev/null | awk '{print $3}' || true)"
|
||||
PR_VER_INSTALLED=$(dpkg-query -f='${Version}\n' --show prosody 2>/dev/null || true)
|
||||
if [ "$PR11_INSTALL_CHECK" = "installed" ] \
|
||||
|| [ "$PR11_INSTALL_CHECK" = "unpacked" ] \
|
||||
|| dpkg --compare-versions "$PR_VER_INSTALLED" gt "0.11" ; then
|
||||
if [ "$PRTRUNK_INSTALL_CHECK" = "installed" ] \
|
||||
|| [ "$PRTRUNK_INSTALL_CHECK" = "unpacked" ] ; then
|
||||
if [ -f $PROSODY_HOST_CONFIG ]; then
|
||||
sed -i 's/storage = \"null\"/storage = \"memory\"/g' $PROSODY_HOST_CONFIG
|
||||
sed -i 's/storage = \"memory\"/storage = \"null\"/g' $PROSODY_HOST_CONFIG
|
||||
|
||||
# trigger a restart
|
||||
PROSODY_CONFIG_PRESENT="false"
|
||||
@@ -168,7 +170,7 @@ case "$1" in
|
||||
# if the version is 0.10.X (>0.10 and <0.11)
|
||||
if [ -f $PROSODY_HOST_CONFIG ] \
|
||||
&& dpkg --compare-versions "$PR_VER_INSTALLED" lt "0.11" ; then
|
||||
sed -i 's/storage = \"null\"/storage = \"none\"/g' $PROSODY_HOST_CONFIG
|
||||
sed -i 's/storage = \"memory\"/storage = \"none\"/g' $PROSODY_HOST_CONFIG
|
||||
|
||||
# trigger a restart
|
||||
PROSODY_CONFIG_PRESENT="false"
|
||||
|
||||
10
debian/jitsi-meet-prosody.postrm
vendored
@@ -36,13 +36,17 @@ case "$1" in
|
||||
rm -f /etc/prosody/conf.avail/$JVB_HOSTNAME.cfg.lua
|
||||
rm -f /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua
|
||||
|
||||
JICOFO_AUTH_DOMAIN="auth.$JVB_HOSTNAME"
|
||||
# clean up generated certificates
|
||||
rm -f /etc/prosody/certs/$JVB_HOSTNAME.crt
|
||||
rm -f /etc/prosody/certs/$JVB_HOSTNAME.key
|
||||
rm -f /etc/prosody/certs/auth.$JVB_HOSTNAME.crt
|
||||
rm -f /etc/prosody/certs/auth.$JVB_HOSTNAME.key
|
||||
rm -rf /var/lib/prosody/auth.$JVB_HOSTNAME.*
|
||||
rm -f /etc/prosody/certs/$JICOFO_AUTH_DOMAIN.crt
|
||||
rm -f /etc/prosody/certs/$JICOFO_AUTH_DOMAIN.key
|
||||
rm -rf /var/lib/prosody/$JICOFO_AUTH_DOMAIN.*
|
||||
rm -rf /var/lib/prosody/$JVB_HOSTNAME.*
|
||||
|
||||
# clean created users
|
||||
rm -rf /var/lib/prosody/`echo $JICOFO_AUTH_DOMAIN | sed -e "s/\./%2e/g"`
|
||||
fi
|
||||
|
||||
# Clear the debconf variable
|
||||
|
||||
13
debian/jitsi-meet-tokens.postinst
vendored
@@ -69,12 +69,15 @@ case "$1" in
|
||||
echo "Failed to install basexx - try installing it manually"
|
||||
fi
|
||||
|
||||
PR11_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-0.11' 2>/dev/null | awk '{print $3}' || true)"
|
||||
PR10_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-0.10' 2>/dev/null | awk '{print $3}' || true)"
|
||||
PRTRUNK_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-trunk' 2>/dev/null | awk '{print $3}' || true)"
|
||||
PR_VER_INSTALLED=$(dpkg-query -f='${Version}\n' --show prosody 2>/dev/null || true)
|
||||
if [ "$PR11_INSTALL_CHECK" = "installed" ] \
|
||||
|| [ "$PR11_INSTALL_CHECK" = "unpacked" ] \
|
||||
|| dpkg --compare-versions "$PR_VER_INSTALLED" gt "0.11" ; then
|
||||
sed -i 's/module:hook/module:hook_global/g' /usr/share/jitsi-meet/prosody-plugins/mod_auth_token.lua
|
||||
if [ "$PR10_INSTALL_CHECK" = "installed" ] \
|
||||
|| "$PR10_INSTALL_CHECK" = "unpacked" \
|
||||
|| "$PRTRUNK_INSTALL_CHECK" = "installed" \
|
||||
|| "$PRTRUNK_INSTALL_CHECK" = "unpacked" \
|
||||
|| dpkg --compare-versions "$PR_VER_INSTALLED" lt "0.11" ; then
|
||||
sed -i 's/module:hook_global(/module:hook(/g' /usr/share/jitsi-meet/prosody-plugins/mod_auth_token.lua
|
||||
fi
|
||||
|
||||
if [ -x "/etc/init.d/prosody" ]; then
|
||||
|
||||
5
debian/jitsi-meet-turnserver.install
vendored
@@ -1,2 +1,3 @@
|
||||
doc/debian/jitsi-meet-turn/turnserver.conf /usr/share/jitsi-meet-turnserver/
|
||||
doc/debian/jitsi-meet/jitsi-meet.conf /usr/share/jitsi-meet-turnserver/
|
||||
doc/debian/jitsi-meet-turn/turnserver.conf /usr/share/jitsi-meet-turnserver/
|
||||
doc/debian/jitsi-meet/jitsi-meet.conf /usr/share/jitsi-meet-turnserver/
|
||||
doc/debian/jitsi-meet-turn/coturn-certbot-deploy.sh /usr/share/jitsi-meet-turnserver/
|
||||
|
||||
1
debian/jitsi-meet-turnserver.links
vendored
@@ -1 +0,0 @@
|
||||
/usr/share/jitsi-meet-turnserver/jitsi-meet.conf /etc/nginx/modules-enabled/60-jitsi-meet.conf
|
||||
128
debian/jitsi-meet-turnserver.postinst
vendored
@@ -36,20 +36,60 @@ case "$1" in
|
||||
NGINX_CONFIG="/etc/nginx/sites-available/$JVB_HOSTNAME.conf"
|
||||
JITSI_MEET_CONFIG="/etc/jitsi/meet/$JVB_HOSTNAME-config.js"
|
||||
|
||||
NGINX_SITES_ENABLED="/etc/nginx/sites-enabled/"
|
||||
NGINX_CONFIG_ENABLED="${NGINX_SITES_ENABLED}${JVB_HOSTNAME}.conf"
|
||||
NGINX_MULTIPLEXING="true"
|
||||
for site in ${NGINX_SITES_ENABLED}*; do
|
||||
# if it is not a file continue
|
||||
[ -f "${site}" ] || continue
|
||||
# if it is our config skip
|
||||
[ "${site}" != "${NGINX_CONFIG_ENABLED}" ] || continue
|
||||
# check whether other enabled hosts has listen 443
|
||||
if cat ${site} | grep -v "^[[:space:]]*#" | grep listen | grep -q "^.*[[:space:]:]443[;[:space:]].*" ; then
|
||||
# nothing to do
|
||||
echo "------------------------------------------------"
|
||||
echo ""
|
||||
echo "turnserver is listening on tcp 4445 as other nginx sites use port 443"
|
||||
echo ""
|
||||
echo "------------------------------------------------"
|
||||
NGINX_MULTIPLEXING="false"
|
||||
fi
|
||||
done
|
||||
|
||||
# if there was a turn config backup it so we can configure
|
||||
# we cannot recognize at the moment is this a user config or default config when installing coturn
|
||||
if [[ -f $TURN_CONFIG ]] && ! grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then
|
||||
mv $TURN_CONFIG $TURN_CONFIG.bak
|
||||
fi
|
||||
|
||||
# detect dpkg-reconfigure, just delete old links
|
||||
db_get jitsi-meet-turnserver/jvb-hostname
|
||||
JVB_HOSTNAME_OLD=$RET
|
||||
if [ -n "$RET" ] && [ ! "$JVB_HOSTNAME_OLD" = "$JVB_HOSTNAME" ] ; then
|
||||
rm -f $TURN_CONFIG
|
||||
if [[ -f $TURN_CONFIG ]] && grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then
|
||||
rm -f $TURN_CONFIG
|
||||
fi
|
||||
fi
|
||||
|
||||
# this detect only old installations with no nginx
|
||||
db_get jitsi-meet/jvb-serve || true
|
||||
if [ ! -f $NGINX_CONFIG -o "$RET" = "true" ] ; then
|
||||
# nothing to do
|
||||
echo "------------------------------------------------"
|
||||
echo ""
|
||||
echo "turnserver not configured as no nginx found to multiplex traffic"
|
||||
echo "turnserver not configured"
|
||||
echo ""
|
||||
echo "------------------------------------------------"
|
||||
db_stop
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -f $TURN_CONFIG ]] ; then
|
||||
echo "------------------------------------------------"
|
||||
echo ""
|
||||
echo "turnserver is already configured on this machine, skipping."
|
||||
echo ""
|
||||
echo "------------------------------------------------"
|
||||
db_stop
|
||||
exit 0
|
||||
fi
|
||||
@@ -65,45 +105,61 @@ case "$1" in
|
||||
fi
|
||||
TURN_SECRET="$RET"
|
||||
|
||||
if [[ -f $TURN_CONFIG ]] && ! grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then
|
||||
PUBLIC_IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
|
||||
cp /usr/share/jitsi-meet-turnserver/turnserver.conf $TURN_CONFIG
|
||||
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $TURN_CONFIG
|
||||
sed -i "s/__turnSecret__/$TURN_SECRET/g" $TURN_CONFIG
|
||||
sed -i "s/__external_ip_address__/$JVB_HOSTNAME/g" $TURN_CONFIG
|
||||
# no turn config exists, lt's copy template and fill it in
|
||||
PUBLIC_IP=$(dig -4 +short myip.opendns.com a @resolver1.opendns.com) || true
|
||||
if [ -z "$PUBLIC_IP" ] ; then
|
||||
PUBLIC_IP="127.0.0.1"
|
||||
echo "------------------------------------------------"
|
||||
echo "Warning! Could not resolve your external ip address! Error:^"
|
||||
echo "Your turn server will not work till you edit your $TURN_CONFIG config file."
|
||||
echo "You need to set your external ip address in external-ip and restart coturn service."
|
||||
echo "------------------------------------------------"
|
||||
fi
|
||||
cp /usr/share/jitsi-meet-turnserver/turnserver.conf $TURN_CONFIG
|
||||
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $TURN_CONFIG
|
||||
sed -i "s/__turnSecret__/$TURN_SECRET/g" $TURN_CONFIG
|
||||
sed -i "s/__external_ip_address__/$PUBLIC_IP/g" $TURN_CONFIG
|
||||
|
||||
# SSL for nginx
|
||||
db_get jitsi-meet/cert-choice
|
||||
CERT_CHOICE="$RET"
|
||||
# SSL for nginx
|
||||
db_get jitsi-meet/cert-choice
|
||||
CERT_CHOICE="$RET"
|
||||
|
||||
if [ "$CERT_CHOICE" = "I want to use my own certificate" ] ; then
|
||||
db_get jitsi-meet/cert-path-key
|
||||
CERT_KEY="$RET"
|
||||
db_get jitsi-meet/cert-path-crt
|
||||
CERT_CRT="$RET"
|
||||
if [ "$CERT_CHOICE" = "I want to use my own certificate" ] ; then
|
||||
db_get jitsi-meet/cert-path-key
|
||||
CERT_KEY="$RET"
|
||||
db_get jitsi-meet/cert-path-crt
|
||||
CERT_CRT="$RET"
|
||||
|
||||
# replace self-signed certificate paths with user provided ones
|
||||
CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g')
|
||||
CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g')
|
||||
sed -i "s/pkey=\/etc\/jitsi\/meet\/.*key/pkey=$CERT_KEY_ESC/g" $TURN_CONFIG
|
||||
CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g')
|
||||
CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g')
|
||||
sed -i "s/cert=\/etc\/jitsi\/meet\/.*crt/cert=$CERT_CRT_ESC/g" $TURN_CONFIG
|
||||
# replace self-signed certificate paths with user provided ones
|
||||
CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g')
|
||||
CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g')
|
||||
sed -i "s/pkey=\/etc\/jitsi\/meet\/.*key/pkey=$CERT_KEY_ESC/g" $TURN_CONFIG
|
||||
CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g')
|
||||
CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g')
|
||||
sed -i "s/cert=\/etc\/jitsi\/meet\/.*crt/cert=$CERT_CRT_ESC/g" $TURN_CONFIG
|
||||
fi
|
||||
|
||||
sed -i "s/#TURNSERVER_ENABLED/TURNSERVER_ENABLED/g" /etc/default/coturn
|
||||
invoke-rc.d coturn restart || true
|
||||
|
||||
NGINX_STREAM_CONFIG="/etc/nginx/modules-enabled/60-jitsi-meet.conf"
|
||||
if [ $NGINX_MULTIPLEXING = "true" ] && [ ! -f $NGINX_STREAM_CONFIG ] && [ -f $NGINX_CONFIG ] ; then
|
||||
ln -s /usr/share/jitsi-meet-turnserver/jitsi-meet.conf $NGINX_STREAM_CONFIG
|
||||
sed -i "s/listen 443 ssl/listen 4444 ssl http2/g" $NGINX_CONFIG
|
||||
sed -i "s/listen \[\:\:\]\:443 ssl/listen \[\:\:\]\:4444 ssl http2/g" $NGINX_CONFIG
|
||||
invoke-rc.d nginx reload || true
|
||||
else
|
||||
PROSODY_HOST_CONFIG="/etc/prosody/conf.avail/$JVB_HOSTNAME.cfg.lua"
|
||||
if [ -f $PROSODY_HOST_CONFIG ] ; then
|
||||
# If we are not multiplexing we need to change the port in prosody config
|
||||
sed -i 's/"443"/"4445"/g' $PROSODY_HOST_CONFIG
|
||||
invoke-rc.d prosody restart || true
|
||||
fi
|
||||
fi
|
||||
|
||||
sed -i "s/#TURNSERVER_ENABLED/TURNSERVER_ENABLED/g" /etc/default/coturn
|
||||
invoke-rc.d coturn restart || true
|
||||
|
||||
NGINX_STREAM_CONFIG="/etc/nginx/modules-enabled/60-jitsi-meet.conf"
|
||||
if [ -f $NGINX_STREAM_CONFIG ] && [ -f $NGINX_CONFIG ] ; then
|
||||
sed -i "s/listen 443 ssl/listen 4444 ssl http2/g" $NGINX_CONFIG
|
||||
invoke-rc.d nginx reload || true
|
||||
fi
|
||||
|
||||
# Enable turn server in config.js
|
||||
if [ -f $JITSI_MEET_CONFIG ] ; then
|
||||
sed -i "s/\/\/ useStunTurn: true/useStunTurn: true/g" $JITSI_MEET_CONFIG
|
||||
fi
|
||||
# Enable turn server in config.js
|
||||
if [ -f $JITSI_MEET_CONFIG ] ; then
|
||||
sed -i "s/\/\/ useStunTurn: true/useStunTurn: true/g" $JITSI_MEET_CONFIG
|
||||
fi
|
||||
|
||||
# and we're done with debconf
|
||||
|
||||
63
debian/jitsi-meet-turnserver.postrm
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
# postrm script for jitsi-meet-turnserver
|
||||
#
|
||||
# see: dh_installdeb(1)
|
||||
|
||||
set -e
|
||||
|
||||
# summary of how this script can be called:
|
||||
# * <postrm> `remove'
|
||||
# * <postrm> `purge'
|
||||
# * <old-postrm> `upgrade' <new-version>
|
||||
# * <new-postrm> `failed-upgrade' <old-version>
|
||||
# * <new-postrm> `abort-install'
|
||||
# * <new-postrm> `abort-install' <old-version>
|
||||
# * <new-postrm> `abort-upgrade' <old-version>
|
||||
# * <disappearer's-postrm> `disappear' <overwriter>
|
||||
# <overwriter-version>
|
||||
# for details, see http://www.debian.org/doc/debian-policy/ or
|
||||
# the debian-policy package
|
||||
|
||||
# Load debconf
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
|
||||
case "$1" in
|
||||
remove)
|
||||
rm -rf /etc/nginx/modules-enabled/60-jitsi-meet.conf
|
||||
if [ -x "/etc/init.d/nginx" ]; then
|
||||
invoke-rc.d nginx reload || true
|
||||
fi
|
||||
if [ -x "/etc/init.d/apache2" ]; then
|
||||
invoke-rc.d apache2 reload || true
|
||||
fi
|
||||
;;
|
||||
purge)
|
||||
rm -rf /etc/nginx/modules-enabled/60-jitsi-meet.conf
|
||||
rm -rf /etc/turnserver.conf
|
||||
if [ -x "/etc/init.d/nginx" ]; then
|
||||
invoke-rc.d nginx reload || true
|
||||
fi
|
||||
if [ -x "/etc/init.d/apache2" ]; then
|
||||
invoke-rc.d apache2 reload || true
|
||||
fi
|
||||
# Clear the debconf variable
|
||||
db_purge
|
||||
;;
|
||||
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "postrm called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# dh_installdeb will replace this with shell code automatically
|
||||
# generated by other debhelper scripts.
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
db_stop
|
||||
|
||||
exit 0
|
||||
64
debian/jitsi-meet-web-config.postinst
vendored
@@ -68,12 +68,11 @@ case "$1" in
|
||||
FORCE_APACHE="true"
|
||||
fi
|
||||
|
||||
UPLOADED_CERT_CHOICE="I want to use my own certificate"
|
||||
# if first time config ask for certs, or if we are reconfiguring
|
||||
if [ -z "$JVB_HOSTNAME_OLD" ] || [ "$RECONFIGURING" = "true" ] ; then
|
||||
# SSL for nginx
|
||||
db_get jitsi-meet/cert-choice
|
||||
CERT_CHOICE="$RET"
|
||||
UPLOADED_CERT_CHOICE="I want to use my own certificate"
|
||||
|
||||
if [ "$CERT_CHOICE" = "$UPLOADED_CERT_CHOICE" ] ; then
|
||||
db_set jitsi-meet/cert-path-key "/etc/ssl/$JVB_HOSTNAME.key"
|
||||
@@ -104,24 +103,57 @@ case "$1" in
|
||||
if [ ! -f $JITSI_MEET_CONFIG ] ; then
|
||||
cp /usr/share/jitsi-meet-web-config/config.js $JITSI_MEET_CONFIG
|
||||
# replaces needed config for multidomain as it works only with nginx
|
||||
sed -i "s/conference.jitsi-meet.example.com/conference.<\!--# echo var=\"subdomain\" default=\"\" -->jitsi-meet.example.com/g" $JITSI_MEET_CONFIG
|
||||
if [[ "$FORCE_NGINX" = "true" ]] ; then
|
||||
sed -i "s/conference.jitsi-meet.example.com/conference.<\!--# echo var=\"subdomain\" default=\"\" -->jitsi-meet.example.com/g" $JITSI_MEET_CONFIG
|
||||
fi
|
||||
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $JITSI_MEET_CONFIG
|
||||
fi
|
||||
|
||||
# this is new install let's configure jvb to serve meet
|
||||
# no-nginx, no-apache installed on machine, this is new install or reconfiguring old one which have jvb_serve set
|
||||
# getting rid of jetty serving web
|
||||
if [[ "$JVB_SERVE" = "true" ]] ; then
|
||||
echo ""
|
||||
echo "------------------------------------------------"
|
||||
echo "You are using jetty to serve jitsi-meet, it is recommended to uninstall(purge) and use default installation that comes with nginx!"
|
||||
echo ""
|
||||
echo "When using the following command, any custom config will be LOST, please backup /etc/jitsi !!!"
|
||||
echo ""
|
||||
echo "You can purge your installation using the following command:"
|
||||
echo "apt-get purge jitsi-meet jitsi-meet-web-config jitsi-meet-prosody jitsi-meet-web jicofo jitsi-videobridge"
|
||||
echo "------------------------------------------------"
|
||||
echo ""
|
||||
elif [[ "$FORCE_NGINX" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
|
||||
JVB_CONFIG="/etc/jitsi/videobridge/sip-communicator.properties"
|
||||
|
||||
# we will write to the file if missing create it
|
||||
if [ -f $JVB_CONFIG ] ; then
|
||||
echo ""
|
||||
echo "------------------------------------------------"
|
||||
echo ""
|
||||
echo "You are using jetty to serve jitsi-meet, we are now upgrading you to use nginx!"
|
||||
echo ""
|
||||
echo "If you are using Let’s Encrypt certificates please re-run the script."
|
||||
echo ""
|
||||
echo "------------------------------------------------"
|
||||
echo ""
|
||||
|
||||
sed -i "s/org.jitsi.videobridge.rest.jetty/#org.jitsi.videobridge.rest.jetty/g" $JVB_CONFIG
|
||||
sed -i "s/org.jitsi.videobridge.TCP_HARVESTER_PORT/#org.jitsi.videobridge.TCP_HARVESTER_PORT/g" $JVB_CONFIG
|
||||
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl restart jitsi-videobridge2.service >/dev/null || true
|
||||
fi
|
||||
|
||||
# Removing this value will force nginx or apache to be locally configured
|
||||
JVB_HOSTNAME_OLD=""
|
||||
|
||||
db_get jitsi-meet/cert-choice
|
||||
CERT_CHOICE="$RET"
|
||||
# Fix certs on upgrade from jetty
|
||||
if [ "$CERT_CHOICE" = "$UPLOADED_CERT_CHOICE" ] ; then
|
||||
db_get jitsi-meet/cert-path-key
|
||||
CERT_KEY="$RET"
|
||||
db_get jitsi-meet/cert-path-crt
|
||||
CERT_CRT="$RET"
|
||||
else
|
||||
# create self-signed certs
|
||||
CERT_KEY="/etc/jitsi/meet/$JVB_HOSTNAME.key"
|
||||
CERT_CRT="/etc/jitsi/meet/$JVB_HOSTNAME.crt"
|
||||
fi
|
||||
fi
|
||||
|
||||
db_set jitsi-meet/jvb-serve "false"
|
||||
fi
|
||||
|
||||
if [[ "$FORCE_NGINX" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
|
||||
|
||||
# this is a reconfigure, lets just delete old links
|
||||
if [ "$RECONFIGURING" = "true" ] ; then
|
||||
|
||||
1
debian/jitsi-meet-web.install
vendored
@@ -11,4 +11,5 @@ fonts /usr/share/jitsi-meet/
|
||||
images /usr/share/jitsi-meet/
|
||||
lang /usr/share/jitsi-meet/
|
||||
connection_optimization /usr/share/jitsi-meet/
|
||||
resources/robots.txt /usr/share/jitsi-meet/
|
||||
resources/*.sh /usr/share/jitsi-meet/scripts/
|
||||
|
||||
3
doc/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Documentation
|
||||
|
||||
The Jitsi documentation has been moved to [The Handbook](https://jitsi.github.io/handbook/).
|
||||
597
doc/api.md
@@ -1,598 +1,3 @@
|
||||
# Jitsi Meet API
|
||||
|
||||
You can use the Jitsi Meet API to embed Jitsi Meet in to your application. You are also welcome to use it for embedding the globally distributed and highly available deployment on meet.jit.si itself. The only thing we ask for in that case is that you please DO NOT remove the jitsi.org logo from the top left corner.
|
||||
|
||||
## Installation
|
||||
|
||||
To embed Jitsi Meet in your application you need to add the Jitsi Meet API library:
|
||||
|
||||
```javascript
|
||||
<script src='https://meet.jit.si/external_api.js'></script>
|
||||
```
|
||||
## API
|
||||
|
||||
### `api = new JitsiMeetExternalAPI(domain, options)`
|
||||
|
||||
The next step for embedding Jitsi Meet is to create the Jitsi Meet API object.
|
||||
Its constructor gets a number of options:
|
||||
|
||||
* **domain**: domain used to build the conference URL, 'meet.jit.si' for
|
||||
example.
|
||||
* **options**: object with properties - the optional arguments:
|
||||
* **roomName**: (optional) name of the room to join.
|
||||
* **width**: (optional) width for the iframe which will be created. If a number is specified it's treated as pixel units. If a string is specified the format is number followed by 'px', 'em', 'pt' or '%'.
|
||||
* **height**: (optional) height for the iframe which will be created. If a number is specified it's treated as pixel units. If a string is specified the format is number followed by 'px', 'em', 'pt' or '%'.
|
||||
* **parentNode**: (optional) HTML DOM Element where the iframe will be added as a child.
|
||||
* **configOverwrite**: (optional) JS object with overrides for options defined in [config.js].
|
||||
* **interfaceConfigOverwrite**: (optional) JS object with overrides for options defined in [interface_config.js].
|
||||
* **noSSL**: (optional, defaults to true) Boolean indicating if the server should be contacted using HTTP or HTTPS.
|
||||
* **jwt**: (optional) [JWT](https://jwt.io/) token.
|
||||
* **onload**: (optional) handler for the iframe onload event.
|
||||
* **invitees**: (optional) Array of objects containing information about new participants that will be invited in the call.
|
||||
* **devices**: (optional) A map containing information about the initial devices that will be used in the call.
|
||||
* **userInfo**: (optional) JS object containing information about the participant opening the meeting, such as `email`.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
const domain = 'meet.jit.si';
|
||||
const options = {
|
||||
roomName: 'JitsiMeetAPIExample',
|
||||
width: 700,
|
||||
height: 700,
|
||||
parentNode: document.querySelector('#meet')
|
||||
};
|
||||
const api = new JitsiMeetExternalAPI(domain, options);
|
||||
```
|
||||
|
||||
You can set the initial media devices for the call:
|
||||
|
||||
```javascript
|
||||
const domain = 'meet.jit.si';
|
||||
const options = {
|
||||
...
|
||||
devices: {
|
||||
audioInput: '<deviceLabel>',
|
||||
audioOutput: '<deviceLabel>',
|
||||
videoInput: '<deviceLabel>'
|
||||
},
|
||||
...
|
||||
};
|
||||
const api = new JitsiMeetExternalAPI(domain, options);
|
||||
```
|
||||
|
||||
You can overwrite options set in [config.js] and [interface_config.js].
|
||||
For example, to enable the filmstrip-only interface mode, you can use:
|
||||
|
||||
```javascript
|
||||
const options = {
|
||||
...
|
||||
interfaceConfigOverwrite: { filmStripOnly: true },
|
||||
...
|
||||
};
|
||||
const api = new JitsiMeetExternalAPI(domain, options);
|
||||
```
|
||||
|
||||
You can also pass a jwt token to Jitsi Meet:
|
||||
|
||||
```javascript
|
||||
const options = {
|
||||
...
|
||||
jwt: '<jwt_token>',
|
||||
noSsl: false,
|
||||
...
|
||||
};
|
||||
const api = new JitsiMeetExternalAPI(domain, options);
|
||||
```
|
||||
|
||||
You can set the userInfo(email) for the call:
|
||||
|
||||
```javascript
|
||||
var domain = "meet.jit.si";
|
||||
var options = {
|
||||
...
|
||||
userInfo: {
|
||||
email: 'email@jitsiexamplemail.com'
|
||||
}
|
||||
}
|
||||
var api = new JitsiMeetExternalAPI(domain, options);
|
||||
```
|
||||
|
||||
### Controlling the embedded Jitsi Meet Conference
|
||||
|
||||
Device management `JitsiMeetExternalAPI` methods:
|
||||
* **getAvailableDevices** - Retrieve a list of available devices.
|
||||
|
||||
```javascript
|
||||
api.getAvailableDevices().then(devices => {
|
||||
// devices = {
|
||||
// audioInput: [{
|
||||
// deviceId: 'ID'
|
||||
// groupId: 'grpID'
|
||||
// kind: 'audioinput'
|
||||
// label: 'label'
|
||||
// },....],
|
||||
// audioOutput: [{
|
||||
// deviceId: 'ID'
|
||||
// groupId: 'grpID'
|
||||
// kind: 'audioOutput'
|
||||
// label: 'label'
|
||||
// },....],
|
||||
// videoInput: [{
|
||||
// deviceId: 'ID'
|
||||
// groupId: 'grpID'
|
||||
// kind: 'videoInput'
|
||||
// label: 'label'
|
||||
// },....]
|
||||
// }
|
||||
...
|
||||
});
|
||||
```
|
||||
* **getCurrentDevices** - Retrieve a list with the devices that are currently selected.
|
||||
|
||||
```javascript
|
||||
api.getCurrentDevices().then(devices => {
|
||||
// devices = {
|
||||
// audioInput: {
|
||||
// deviceId: 'ID'
|
||||
// groupId: 'grpID'
|
||||
// kind: 'videoInput'
|
||||
// label: 'label'
|
||||
// },
|
||||
// audioOutput: {
|
||||
// deviceId: 'ID'
|
||||
// groupId: 'grpID'
|
||||
// kind: 'videoInput'
|
||||
// label: 'label'
|
||||
// },
|
||||
// videoInput: {
|
||||
// deviceId: 'ID'
|
||||
// groupId: 'grpID'
|
||||
// kind: 'videoInput'
|
||||
// label: 'label'
|
||||
// }
|
||||
// }
|
||||
...
|
||||
});
|
||||
```
|
||||
* **isDeviceChangeAvailable** - Resolves with true if the device change is available and with false if not.
|
||||
|
||||
```javascript
|
||||
// The accepted deviceType values are - 'output', 'input' or undefined.
|
||||
api.isDeviceChangeAvailable(deviceType).then(isDeviceChangeAvailable => {
|
||||
...
|
||||
});
|
||||
```
|
||||
* **isDeviceListAvailable** - Resolves with true if the device list is available and with false if not.
|
||||
|
||||
```javascript
|
||||
api.isDeviceListAvailable().then(isDeviceListAvailable => {
|
||||
...
|
||||
});
|
||||
```
|
||||
* **isMultipleAudioInputSupported** - Resolves with true if multiple audio input is supported and with false if not.
|
||||
|
||||
```javascript
|
||||
api.isMultipleAudioInputSupported().then(isMultipleAudioInputSupported => {
|
||||
...
|
||||
});
|
||||
```
|
||||
* **setAudioInputDevice** - Sets the audio input device to the one with the label or id that is passed.
|
||||
|
||||
```javascript
|
||||
api.setAudioInputDevice(deviceLabel, deviceId);
|
||||
```
|
||||
* **setAudioOutputDevice** - Sets the audio output device to the one with the label or id that is passed.
|
||||
|
||||
```javascript
|
||||
api.setAudioOutputDevice(deviceLabel, deviceId);
|
||||
```
|
||||
* **setVideoInputDevice** - Sets the video input device to the one with the label or id that is passed.
|
||||
|
||||
```javascript
|
||||
api.setVideoInputDevice(deviceLabel, deviceId);
|
||||
```
|
||||
|
||||
You can control the embedded Jitsi Meet conference using the `JitsiMeetExternalAPI` object by using `executeCommand`:
|
||||
|
||||
```javascript
|
||||
api.executeCommand(command, ...arguments);
|
||||
```
|
||||
|
||||
The `command` parameter is String object with the name of the command. The following commands are currently supported:
|
||||
|
||||
* **displayName** - Sets the display name of the local participant. This command requires one argument - the new display name to be set.
|
||||
```javascript
|
||||
api.executeCommand('displayName', 'New Nickname');
|
||||
```
|
||||
|
||||
* **password** - Sets the password for the room. This command requires one argument - the password name to be set.
|
||||
```javascript
|
||||
api.executeCommand('password', 'The Password');
|
||||
```
|
||||
|
||||
* **sendTones** - Play touch tones.
|
||||
```javascript
|
||||
api.executeCommand('sendTones', {
|
||||
tones: string, // The dial pad touch tones to play. For example, '12345#'.
|
||||
duration: number, // Optional. The number of milliseconds each tone should play. The default is 200.
|
||||
pause: number // Optional. The number of milliseconds between each tone. The default is 200.
|
||||
});
|
||||
```
|
||||
|
||||
* **subject** - Sets the subject of the conference. This command requires one argument - the new subject to be set.
|
||||
```javascript
|
||||
api.executeCommand('subject', 'New Conference Subject');
|
||||
```
|
||||
|
||||
* **toggleAudio** - Mutes / unmutes the audio for the local participant. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleAudio');
|
||||
```
|
||||
|
||||
* **toggleVideo** - Mutes / unmutes the video for the local participant. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleVideo');
|
||||
```
|
||||
|
||||
* **toggleFilmStrip** - Hides / shows the filmstrip. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleFilmStrip');
|
||||
```
|
||||
|
||||
* **toggleChat** - Hides / shows the chat. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleChat');
|
||||
```
|
||||
|
||||
* **toggleShareScreen** - Starts / stops screen sharing. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleShareScreen');
|
||||
```
|
||||
|
||||
* **toggleTileView** - Enter / exit tile view layout mode. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleTileView');
|
||||
```
|
||||
|
||||
* **hangup** - Hangups the call. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('hangup');
|
||||
```
|
||||
|
||||
* **email** - Changes the local email address. This command requires one argument - the new email address to be set.
|
||||
```javascript
|
||||
api.executeCommand('email', 'example@example.com');
|
||||
```
|
||||
|
||||
* **avatarUrl** - Changes the local avatar URL. This command requires one argument - the new avatar URL to be set.
|
||||
```javascript
|
||||
api.executeCommand('avatarUrl', 'https://avatars0.githubusercontent.com/u/3671647');
|
||||
```
|
||||
|
||||
You can also execute multiple commands using the `executeCommands` method:
|
||||
```javascript
|
||||
api.executeCommands(commands);
|
||||
```
|
||||
The `commands` parameter is an object with the names of the commands as keys and the arguments for the commands as values:
|
||||
```javascript
|
||||
api.executeCommands({
|
||||
displayName: [ 'nickname' ],
|
||||
toggleAudio: []
|
||||
});
|
||||
```
|
||||
|
||||
You can add event listeners to the embedded Jitsi Meet using the `addEventListener` method.
|
||||
**NOTE: This method still exists but it is deprecated. JitsiMeetExternalAPI class extends [EventEmitter]. Use [EventEmitter] methods (`addListener` or `on`).**
|
||||
```javascript
|
||||
api.addEventListener(event, listener);
|
||||
```
|
||||
|
||||
The `event` parameter is a String object with the name of the event.
|
||||
The `listener` parameter is a Function object with one argument that will be notified when the event occurs with data related to the event.
|
||||
|
||||
The following events are currently supported:
|
||||
* **cameraError** - event notifications about Jitsi-Meet having failed to access the camera. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
type: string, // A constant representing the overall type of the error.
|
||||
message: string // Additional information about the error.
|
||||
}
|
||||
```
|
||||
|
||||
* **avatarChanged** - event notifications about avatar
|
||||
changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
id: string, // the id of the participant that changed his avatar.
|
||||
avatarURL: string // the new avatar URL.
|
||||
}
|
||||
```
|
||||
|
||||
* **audioAvailabilityChanged** - event notifications about audio availability status changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
available: boolean // new available status - boolean
|
||||
}
|
||||
```
|
||||
|
||||
* **audioMuteStatusChanged** - event notifications about audio mute status changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
muted: boolean // new muted status - boolean
|
||||
}
|
||||
```
|
||||
|
||||
* **micError** - event notifications about Jitsi-Meet having failed to access the mic. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
type: string, // A constant representing the overall type of the error.
|
||||
message: string // Additional information about the error.
|
||||
}
|
||||
```
|
||||
|
||||
* **screenSharingStatusChanged** - receives event notifications about turning on/off the local user screen sharing. The listener will receive object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
on: boolean, //whether screen sharing is on
|
||||
details: {
|
||||
|
||||
// From where the screen sharing is capturing, if known. Values which are
|
||||
// passed include 'window', 'screen', 'proxy', 'device'. The value undefined
|
||||
// will be passed if the source type is unknown or screen share is off.
|
||||
sourceType: string|undefined
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* **dominantSpeakerChanged** - receives event notifications about change in the dominant speaker. The listener will receive object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
id: string //participantId of the new dominant speaker
|
||||
}
|
||||
```
|
||||
|
||||
* **tileViewChanged** - event notifications about tile view layout mode being entered or exited. The listener will receive object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
enabled: boolean, // whether tile view is not displayed or not
|
||||
}
|
||||
```
|
||||
|
||||
* **incomingMessage** - Event notifications about incoming
|
||||
messages. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
from: string, // The id of the user that sent the message
|
||||
nick: string, // the nickname of the user that sent the message
|
||||
message: string // the text of the message
|
||||
}
|
||||
```
|
||||
|
||||
* **outgoingMessage** - Event notifications about outgoing
|
||||
messages. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
message: string // the text of the message
|
||||
}
|
||||
```
|
||||
|
||||
* **displayNameChange** - event notifications about display name
|
||||
changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
id: string, // the id of the participant that changed his display name
|
||||
displayname: string // the new display name
|
||||
}
|
||||
```
|
||||
|
||||
* **deviceListChanged** - event notifications about device list changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
devices: Object // the new list of available devices.
|
||||
}
|
||||
```
|
||||
NOTE: The devices object has the same format as the getAvailableDevices result format.
|
||||
|
||||
* **emailChange** - event notifications about email
|
||||
changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
id: string, // the id of the participant that changed his email
|
||||
email: string // the new email
|
||||
}
|
||||
```
|
||||
* **feedbackSubmitted** - event notifications about conference feedback submission
|
||||
```javascript
|
||||
{
|
||||
error: string // The error which occurred during submission, if any.
|
||||
}
|
||||
```
|
||||
|
||||
* **filmstripDisplayChanged** - event notifications about the visibility of the filmstrip being updated.
|
||||
```javascript
|
||||
{
|
||||
visible: boolean // Whether or not the filmstrip is displayed or hidden.
|
||||
}
|
||||
```
|
||||
|
||||
* **participantJoined** - event notifications about new participants who join the room. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
id: string, // the id of the participant
|
||||
displayName: string // the display name of the participant
|
||||
}
|
||||
```
|
||||
|
||||
* **participantKickedOut** - event notifications about a participants being removed from the room. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
kicked: {
|
||||
id: string, // the id of the participant removed from the room
|
||||
local: boolean // whether or not the participant is the local particiapnt
|
||||
},
|
||||
kicker: {
|
||||
id: string // the id of the participant who kicked out the other participant
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* **participantLeft** - event notifications about participants that leave the room. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
id: string // the id of the participant
|
||||
}
|
||||
```
|
||||
|
||||
* **passwordRequired** - event notifications fired when failing to join a room because it has a password.
|
||||
|
||||
* **videoConferenceJoined** - event notifications fired when the local user has joined the video conference. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
roomName: string, // the room name of the conference
|
||||
id: string, // the id of the local participant
|
||||
displayName: string, // the display name of the local participant
|
||||
avatarURL: string // the avatar URL of the local participant
|
||||
}
|
||||
```
|
||||
|
||||
* **videoConferenceLeft** - event notifications fired when the local user has left the video conference. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
roomName: string // the room name of the conference
|
||||
}
|
||||
```
|
||||
|
||||
* **videoAvailabilityChanged** - event notifications about video availability status changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
available: boolean // new available status - boolean
|
||||
}
|
||||
```
|
||||
|
||||
* **videoMuteStatusChanged** - event notifications about video mute status changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
muted: boolean // new muted status - boolean
|
||||
}
|
||||
```
|
||||
|
||||
* **readyToClose** - event notification fired when Jitsi Meet is ready to be closed (hangup operations are completed).
|
||||
|
||||
* **subjectChange** - event notifications about subject of conference changes.
|
||||
The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
subject: string // the new subject
|
||||
}
|
||||
```
|
||||
|
||||
* **suspendDetected** - event notifications about detecting suspend event in host computer.
|
||||
|
||||
You can also add multiple event listeners by using `addEventListeners`.
|
||||
This method requires one argument of type Object. The object argument must
|
||||
have the names of the events as keys and the listeners of the events as values.
|
||||
**NOTE: This method still exists but it is deprecated. JitsiMeetExternalAPI class extends [EventEmitter]. Use [EventEmitter] methods.**
|
||||
|
||||
```javascript
|
||||
function incomingMessageListener(object)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
function outgoingMessageListener(object)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
api.addEventListeners({
|
||||
incomingMessage: incomingMessageListener,
|
||||
outgoingMessage: outgoingMessageListener
|
||||
});
|
||||
```
|
||||
|
||||
If you want to remove a listener you can use `removeEventListener` method with argument the name of the event.
|
||||
**NOTE: This method still exists but it is deprecated. JitsiMeetExternalAPI class extends [EventEmitter]. Use [EventEmitter] methods( `removeListener`).**
|
||||
```javascript
|
||||
api.removeEventListener('incomingMessage');
|
||||
```
|
||||
|
||||
If you want to remove more than one event you can use `removeEventListeners` method with an Array with the names of the events as an argument.
|
||||
**NOTE: This method still exists but it is deprecated. JitsiMeetExternalAPI class extends [EventEmitter]. Use [EventEmitter] methods.**
|
||||
```javascript
|
||||
api.removeEventListeners([ 'incomingMessage', 'outgoingMessageListener' ]);
|
||||
```
|
||||
|
||||
You can get the number of participants in the conference with the following API function:
|
||||
```javascript
|
||||
const numberOfParticipants = api.getNumberOfParticipants();
|
||||
```
|
||||
|
||||
You can get the avatar URL of a participant in the conference with the following API function:
|
||||
```javascript
|
||||
const avatarURL = api.getAvatarURL(participantId);
|
||||
```
|
||||
|
||||
You can get the display name of a participant in the conference with the following API function:
|
||||
```javascript
|
||||
const displayName = api.getDisplayName(participantId);
|
||||
```
|
||||
|
||||
You can get the email of a participant in the conference with the following API function:
|
||||
```javascript
|
||||
const email = api.getEmail(participantId);
|
||||
```
|
||||
|
||||
You can get the iframe HTML element where Jitsi Meet is loaded with the following API function:
|
||||
```javascript
|
||||
const iframe = api.getIFrame();
|
||||
```
|
||||
|
||||
You can check whether the audio is muted with the following API function:
|
||||
```javascript
|
||||
api.isAudioMuted().then(muted => {
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
You can check whether the video is muted with the following API function:
|
||||
```javascript
|
||||
api.isVideoMuted().then(muted => {
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
You can check whether the audio is available with the following API function:
|
||||
```javascript
|
||||
api.isAudioAvailable().then(available => {
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
You can check whether the video is available with the following API function:
|
||||
```javascript
|
||||
api.isVideoAvailable().then(available => {
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
You can invite new participants to the call with the following API function:
|
||||
```javascript
|
||||
api.invite([ {...}, {...}, {...} ]).then(() => {
|
||||
// success
|
||||
}).catch(() => {
|
||||
// failure
|
||||
});
|
||||
```
|
||||
**NOTE: The format of the invitees in the array depends on the invite service used for the deployment.**
|
||||
|
||||
You can remove the embedded Jitsi Meet Conference with the following API function:
|
||||
```javascript
|
||||
api.dispose();
|
||||
```
|
||||
|
||||
NOTE: It's a good practice to remove the conference before the page is unloaded.
|
||||
|
||||
[config.js]: https://github.com/jitsi/jitsi-meet/blob/master/config.js
|
||||
[interface_config.js]: https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js
|
||||
[EventEmitter]: https://nodejs.org/api/events.html
|
||||
This document has been moved [here](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-iframe).
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Jitsi Meet Cloud API
|
||||
|
||||
The Jitsi Meet Cloud API is a specification for services which can support the integration of Jitsi Meet into other applications, for mapping conferences for dial-in support, and for supporting directory search and user invitations to conferences.
|
||||
|
||||
The swagger for these services is provided in [cloud-api.swagger](cloud-api.swagger) in this same repository and directory.
|
||||
@@ -1,93 +0,0 @@
|
||||
# Comments
|
||||
|
||||
* Comments documenting the source code are required.
|
||||
|
||||
* Comments from which documentation is automatically generated are **not**
|
||||
subject to case-by-case decisions. Such comments are used, for example, on
|
||||
types and their members. Examples of tools which automatically generate
|
||||
documentation from such comments include JSDoc, Javadoc, Doxygen.
|
||||
|
||||
* Comments which are not automatically processed are strongly encouraged. They
|
||||
are subject to case-by-case decisions. Such comments are often observed in
|
||||
function bodies.
|
||||
|
||||
* Comments should be formatted as proper English sentences. Such formatting pays
|
||||
attention to, for example, capitalization and punctuation.
|
||||
|
||||
# Duplication
|
||||
|
||||
* Don't copy-paste source code. Reuse it.
|
||||
|
||||
# Formatting
|
||||
|
||||
* Line length is limited to 80 characters.
|
||||
|
||||
* Sort by alphabetical order in order to make the addition of new entities as
|
||||
easy as looking a word up in a dictionary. Otherwise, one risks duplicate
|
||||
entries (with conflicting values in the cases of key-value pairs). For
|
||||
example:
|
||||
|
||||
* Within an `import` of multiple names from a module, sort the names in
|
||||
alphabetical order. (Of course, the default name stays first as required by
|
||||
the `import` syntax.)
|
||||
|
||||
````javascript
|
||||
import {
|
||||
DOMINANT_SPEAKER_CHANGED,
|
||||
JITSI_CLIENT_CONNECTED,
|
||||
JITSI_CLIENT_CREATED,
|
||||
JITSI_CLIENT_DISCONNECTED,
|
||||
JITSI_CLIENT_ERROR,
|
||||
JITSI_CONFERENCE_JOINED,
|
||||
MODERATOR_CHANGED,
|
||||
PEER_JOINED,
|
||||
PEER_LEFT,
|
||||
RTC_ERROR
|
||||
} from './actionTypes';
|
||||
````
|
||||
|
||||
* Within a group of imports (e.g. groups of imports delimited by an empty line
|
||||
may be: third-party modules, then project modules, and eventually the
|
||||
private files of a module), sort the module names in alphabetical order.
|
||||
|
||||
````javascript
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
````
|
||||
|
||||
# Indentation
|
||||
|
||||
* Align `switch` and `case`/`default`. Don't indent the `case`/`default` more
|
||||
than its `switch`.
|
||||
|
||||
````javascript
|
||||
switch (i) {
|
||||
case 0:
|
||||
...
|
||||
break;
|
||||
default:
|
||||
...
|
||||
}
|
||||
````
|
||||
|
||||
# Naming
|
||||
|
||||
* An abstraction should have one name within the project and across multiple
|
||||
projects. For example:
|
||||
|
||||
* The instance of lib-jitsi-meet's `JitsiConnection` type should be named
|
||||
`connection` or `jitsiConnection` in jitsi-meet, not `client`.
|
||||
|
||||
* The class `ReducerRegistry` should be defined in ReducerRegistry.js and its
|
||||
imports in other files should use the same name. Don't define the class
|
||||
`Registry` in ReducerRegistry.js and then import it as `Reducers` in other
|
||||
files.
|
||||
|
||||
* The names of global constants (including ES6 module-global constants) should
|
||||
be written in uppercase with underscores to separate words. For example,
|
||||
`BACKGROUND_COLOR`.
|
||||
|
||||
* The underscore character at the beginning of a name signals that the
|
||||
respective variable, function, property is non-public i.e. private, protected,
|
||||
or internal. In contrast, the lack of an underscore at the beginning of a name
|
||||
signals public API.
|
||||
@@ -6,13 +6,20 @@ muc_mapper_domain_base = "jitmeet.example.com";
|
||||
turncredentials_secret = "__turnSecret__";
|
||||
|
||||
turncredentials = {
|
||||
{ type = "stun", host = "jitmeet.example.com", port = "443" },
|
||||
{ type = "turn", host = "jitmeet.example.com", port = "443", transport = "udp" },
|
||||
{ type = "stun", host = "jitmeet.example.com", port = "4446" },
|
||||
{ type = "turn", host = "jitmeet.example.com", port = "4446", transport = "udp" },
|
||||
{ type = "turns", host = "jitmeet.example.com", port = "443", transport = "tcp" }
|
||||
};
|
||||
|
||||
cross_domain_bosh = false;
|
||||
consider_bosh_secure = true;
|
||||
-- https_ports = { }; -- Remove this line to prevent listening on port 5284
|
||||
|
||||
-- https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4
|
||||
ssl = {
|
||||
protocol = "tlsv1_2+";
|
||||
ciphers = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
|
||||
}
|
||||
|
||||
VirtualHost "jitmeet.example.com"
|
||||
-- enabled = false -- Remove this line to enable this host
|
||||
@@ -43,21 +50,25 @@ VirtualHost "jitmeet.example.com"
|
||||
c2s_require_encryption = false
|
||||
|
||||
Component "conference.jitmeet.example.com" "muc"
|
||||
storage = "null"
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"muc_meeting_id";
|
||||
"muc_domain_mapper";
|
||||
-- "token_verification";
|
||||
}
|
||||
admins = { "focusUser@auth.jitmeet.example.com" }
|
||||
muc_room_locking = false
|
||||
muc_room_default_public_jids = true
|
||||
|
||||
-- internal muc component
|
||||
Component "internal.auth.jitmeet.example.com" "muc"
|
||||
storage = "null"
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"ping";
|
||||
}
|
||||
admins = { "focusUser@auth.jitmeet.example.com", "jvb@auth.jitmeet.example.com" }
|
||||
muc_room_locking = false
|
||||
muc_room_default_public_jids = true
|
||||
|
||||
VirtualHost "auth.jitmeet.example.com"
|
||||
authentication = "internal_plain"
|
||||
|
||||
45
doc/debian/jitsi-meet-turn/coturn-certbot-deploy.sh
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
COTURN_CERT_DIR="/etc/coturn/certs"
|
||||
TURN_CONFIG="/etc/turnserver.conf"
|
||||
|
||||
# create a directory to store certs if it does not exists
|
||||
if [ ! -d "$COTURN_CERT_DIR" ]; then
|
||||
mkdir -p $COTURN_CERT_DIR
|
||||
chown -R turnserver:turnserver /etc/coturn/
|
||||
chmod -R 700 /etc/coturn/
|
||||
fi
|
||||
|
||||
# This is a template and when copied to /etc/letsencrypt/renewal-hooks/deploy/
|
||||
# during creating the Let's encrypt certs script
|
||||
# jitsi-meet.example.com will be replaced with the real domain of deployment
|
||||
for domain in $RENEWED_DOMAINS; do
|
||||
case $domain in
|
||||
jitsi-meet.example.com)
|
||||
# Make sure the certificate and private key files are
|
||||
# never world readable, even just for an instant while
|
||||
# we're copying them into daemon_cert_root.
|
||||
umask 077
|
||||
|
||||
cp "$RENEWED_LINEAGE/fullchain.pem" "$COTURN_CERT_DIR/$domain.fullchain.pem"
|
||||
cp "$RENEWED_LINEAGE/privkey.pem" "$COTURN_CERT_DIR/$domain.privkey.pem"
|
||||
|
||||
# Apply the proper file ownership and permissions for
|
||||
# the daemon to read its certificate and key.
|
||||
chown turnserver "$COTURN_CERT_DIR/$domain.fullchain.pem" \
|
||||
"$COTURN_CERT_DIR/$domain.privkey.pem"
|
||||
chmod 400 "$COTURN_CERT_DIR/$domain.fullchain.pem" \
|
||||
"$COTURN_CERT_DIR/$domain.privkey.pem"
|
||||
|
||||
if [ -f $TURN_CONFIG ] && grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then
|
||||
echo "Configuring turnserver"
|
||||
sed -i "/^cert/c\cert=\/etc\/coturn\/certs\/${domain}.fullchain.pem" $TURN_CONFIG
|
||||
sed -i "/^pkey/c\pkey=\/etc\/coturn\/certs\/${domain}.privkey.pem" $TURN_CONFIG
|
||||
fi
|
||||
service coturn restart
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# jitsi-meet coturn config. Do not modify this line
|
||||
lt-cred-mech
|
||||
use-auth-secret
|
||||
keep-address-family
|
||||
static-auth-secret=__turnSecret__
|
||||
@@ -8,6 +7,12 @@ cert=/etc/jitsi/meet/jitsi-meet.example.com.crt
|
||||
pkey=/etc/jitsi/meet/jitsi-meet.example.com.key
|
||||
|
||||
no-tcp
|
||||
listening-port=443
|
||||
listening-port=4446
|
||||
tls-listening-port=4445
|
||||
external-ip=__external_ip_address__
|
||||
no-tlsv1
|
||||
no-tlsv1_1
|
||||
# https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4
|
||||
cipher-list=ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||
|
||||
syslog
|
||||
|
||||
@@ -11,14 +11,14 @@ stream {
|
||||
}
|
||||
# since 1.13.10
|
||||
map $ssl_preread_alpn_protocols $upstream {
|
||||
"h2" web;
|
||||
"http/1.1" web;
|
||||
"h2,http/1.1" web;
|
||||
~\bh2\b web;
|
||||
~\bhttp/1\. web;
|
||||
default turn;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443;
|
||||
listen [::]:443;
|
||||
|
||||
# since 1.11.5
|
||||
ssl_preread on;
|
||||
|
||||
@@ -2,6 +2,7 @@ server_names_hash_bucket_size 64;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name jitsi-meet.example.com;
|
||||
|
||||
location ^~ /.well-known/acme-challenge/ {
|
||||
@@ -17,13 +18,19 @@ server {
|
||||
}
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name jitsi-meet.example.com;
|
||||
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_ciphers "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA256:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EDH+aRSA+AESGCM:EDH+aRSA+SHA256:EDH+aRSA:EECDH:!aNULL:!eNULL:!MEDIUM:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4:!SEED";
|
||||
# Mozilla Guideline v5.4, nginx 1.17.7, OpenSSL 1.1.1d, intermediate configuration
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=31536000";
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:10m; # about 40000 sessions
|
||||
ssl_session_tickets off;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
ssl_certificate /etc/jitsi/meet/jitsi-meet.example.com.crt;
|
||||
ssl_certificate_key /etc/jitsi/meet/jitsi-meet.example.com.key;
|
||||
@@ -37,6 +44,10 @@ server {
|
||||
index index.html index.htm;
|
||||
error_page 404 /static/404.html;
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/javascript application/json;
|
||||
gzip_vary on;
|
||||
|
||||
location = /config.js {
|
||||
alias /etc/jitsi/meet/jitsi-meet.example.com-config.js;
|
||||
}
|
||||
|
||||
@@ -11,14 +11,15 @@
|
||||
|
||||
ServerName jitsi-meet.example.com
|
||||
|
||||
SSLProtocol TLSv1 TLSv1.1 TLSv1.2
|
||||
# enable HTTP/2, if available
|
||||
Protocols h2 http/1.1
|
||||
|
||||
SSLEngine on
|
||||
SSLProxyEngine on
|
||||
SSLCertificateFile /etc/jitsi/meet/jitsi-meet.example.com.crt
|
||||
SSLCertificateKeyFile /etc/jitsi/meet/jitsi-meet.example.com.key
|
||||
SSLCipherSuite "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA256:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EDH+aRSA+AESGCM:EDH+aRSA+SHA256:EDH+aRSA:EECDH:!aNULL:!eNULL:!MEDIUM:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4:!SEED"
|
||||
SSLHonorCipherOrder on
|
||||
Header set Strict-Transport-Security "max-age=31536000"
|
||||
|
||||
Header always set Strict-Transport-Security "max-age=63072000"
|
||||
|
||||
DocumentRoot "/usr/share/jitsi-meet"
|
||||
<Directory "/usr/share/jitsi-meet">
|
||||
@@ -48,3 +49,9 @@
|
||||
RewriteEngine on
|
||||
RewriteRule ^/([a-zA-Z0-9]+)$ /index.html
|
||||
</VirtualHost>
|
||||
|
||||
# Mozilla Guideline v5.4, Apache 2.4.41, OpenSSL 1.1.1d, intermediate configuration, no OCSP
|
||||
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
|
||||
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||
SSLHonorCipherOrder off
|
||||
SSLSessionTickets off
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
# Developing Jitsi Meet
|
||||
|
||||
## Building the sources
|
||||
|
||||
Node.js >= 10 and npm >= 6 are required.
|
||||
|
||||
On Debian/Ubuntu systems, the required packages can be installed with:
|
||||
```
|
||||
sudo apt-get install npm nodejs
|
||||
cd jitsi-meet
|
||||
npm install
|
||||
```
|
||||
|
||||
To build the Jitsi Meet application, just type
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
### Working with the library sources (lib-jitsi-meet)
|
||||
|
||||
By default the library is build from its git repository sources. The default dependency path in package.json is :
|
||||
```json
|
||||
"lib-jitsi-meet": "jitsi/lib-jitsi-meet",
|
||||
```
|
||||
|
||||
To work with local copy you must change the path to:
|
||||
```json
|
||||
"lib-jitsi-meet": "file:///Users/name/local-lib-jitsi-meet-copy",
|
||||
```
|
||||
|
||||
To make the project you must force it to take the sources as 'npm update':
|
||||
```
|
||||
npm install lib-jitsi-meet --force && make
|
||||
```
|
||||
|
||||
Or if you are making only changes to the library:
|
||||
```
|
||||
npm install lib-jitsi-meet --force && make deploy-lib-jitsi-meet
|
||||
```
|
||||
|
||||
Alternative way is to use [npm link](https://docs.npmjs.com/cli/link).
|
||||
It allows to link `lib-jitsi-meet` dependency to local source in few steps:
|
||||
|
||||
```bash
|
||||
cd lib-jitsi-meet
|
||||
|
||||
#### create global symlink for lib-jitsi-meet package
|
||||
npm link
|
||||
|
||||
cd ../jitsi-meet
|
||||
|
||||
#### create symlink from the local node_modules folder to the global lib-jitsi-meet symlink
|
||||
npm link lib-jitsi-meet
|
||||
```
|
||||
|
||||
After changes in local `lib-jitsi-meet` repository, you can rebuild it with `npm run install` and your `jitsi-meet` repository will use that modified library.
|
||||
Note: when using node version 4.x, the make file of jitsi-meet do npm update which will delete the link. It is no longer the case with version 6.x.
|
||||
|
||||
If you do not want to use local repository anymore you should run
|
||||
```bash
|
||||
cd jitsi-meet
|
||||
npm unlink lib-jitsi-meet
|
||||
npm install
|
||||
```
|
||||
### Running with webpack-dev-server for development
|
||||
|
||||
Use it at the CLI, type
|
||||
```
|
||||
make dev
|
||||
```
|
||||
|
||||
By default the backend deployment used is `beta.meet.jit.si`. You can point the Jitsi-Meet app at a different backend by using a proxy server. To do this, set the WEBPACK_DEV_SERVER_PROXY_TARGET variable:
|
||||
```
|
||||
export WEBPACK_DEV_SERVER_PROXY_TARGET=https://your-example-server.com
|
||||
make dev
|
||||
```
|
||||
|
||||
The app should be running at https://localhost:8080/
|
||||
@@ -0,0 +1,27 @@
|
||||
# Jitsi Conference Focus settings
|
||||
# sets the host name of the XMPP server
|
||||
JICOFO_HOST=localhost
|
||||
|
||||
# sets the XMPP domain (default: none)
|
||||
JICOFO_HOSTNAME=meet.example.com
|
||||
|
||||
# sets the secret used to authenticate as an XMPP component
|
||||
JICOFO_SECRET=$JICOFO_SECRET
|
||||
|
||||
# sets the port to use for the XMPP component connection
|
||||
JICOFO_PORT=5347
|
||||
|
||||
# sets the XMPP domain name to use for XMPP user logins
|
||||
JICOFO_AUTH_DOMAIN=auth.meet.example.com
|
||||
|
||||
# sets the username to use for XMPP user logins
|
||||
JICOFO_AUTH_USER=focus
|
||||
|
||||
# sets the password to use for XMPP user logins
|
||||
JICOFO_AUTH_PASSWORD=$JICOFO_PASSWORD
|
||||
|
||||
# extra options to pass to the jicofo daemon
|
||||
JICOFO_OPTS=""
|
||||
|
||||
# adds java system props that are passed to jicofo (default are for home and logging config file)
|
||||
JAVA_SYS_PROPS="-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION=/etc/jitsi -Dnet.java.sip.communicator.SC_HOME_DIR_NAME=jicofo -Dnet.java.sip.communicator.SC_LOG_DIR_LOCATION=/var/log/jitsi -Djava.util.logging.config.file=/etc/jitsi/jicofo/logging.properties"
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
org.jitsi.jicofo.BRIDGE_MUC=JvbBrewery@internal.auth.meet.example.com
|
||||
org.jitsi.jicofo.ALWAYS_TRUST_MODE_ENABLED=true
|
||||
|
||||
org.jitsi.jicofo.jibri.BREWERY=JibriBrewery@internal.auth.meet.example.com
|
||||
org.jitsi.jicofo.jibri.PENDING_TIMEOUT=90
|
||||
@@ -0,0 +1,88 @@
|
||||
plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
|
||||
|
||||
-- domain mapper options, must at least have domain base set to use the mapper
|
||||
muc_mapper_domain_base = "meet.example.com";
|
||||
|
||||
turncredentials_secret = "turncredentials_secret_test";
|
||||
|
||||
turncredentials = {
|
||||
{ type = "stun", host = "meet.example.com", port = "443" },
|
||||
{ type = "turn", host = "meet.example.com", port = "443", transport = "udp" },
|
||||
{ type = "turns", host = "meet.example.com", port = "443", transport = "tcp" }
|
||||
};
|
||||
|
||||
cross_domain_bosh = false;
|
||||
consider_bosh_secure = true;
|
||||
|
||||
VirtualHost "meet.example.com"
|
||||
-- enabled = false -- Remove this line to enable this host
|
||||
authentication = "anonymous"
|
||||
-- Properties below are modified by jitsi-meet-tokens package config
|
||||
-- and authentication above is switched to "token"
|
||||
--app_id="example_app_id"
|
||||
--app_secret="example_app_secret"
|
||||
-- Assign this host a certificate for TLS, otherwise it would use the one
|
||||
-- set in the global section (if any).
|
||||
-- Note that old-style SSL on port 5223 only supports one certificate, and will always
|
||||
-- use the global one.
|
||||
ssl = {
|
||||
key = "/etc/prosody/certs/meet.example.com.key";
|
||||
certificate = "/etc/prosody/certs/meet.example.com.crt";
|
||||
}
|
||||
speakerstats_component = "speakerstats.meet.example.com"
|
||||
conference_duration_component = "conferenceduration.meet.example.com"
|
||||
-- we need bosh
|
||||
modules_enabled = {
|
||||
"bosh";
|
||||
"pubsub";
|
||||
"ping"; -- Enable mod_ping
|
||||
"speakerstats";
|
||||
"turncredentials";
|
||||
"conference_duration";
|
||||
}
|
||||
c2s_require_encryption = false
|
||||
|
||||
Component "conference.meet.example.com" "muc"
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"muc_meeting_id";
|
||||
"muc_domain_mapper";
|
||||
-- "token_verification";
|
||||
}
|
||||
admins = { "focus@auth.meet.example.com" }
|
||||
muc_room_locking = false
|
||||
muc_room_default_public_jids = true
|
||||
|
||||
-- internal muc component
|
||||
-- Note: This is also used from jibris
|
||||
Component "internal.auth.meet.example.com" "muc"
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"ping";
|
||||
}
|
||||
admins = { "focus@auth.meet.example.com", "jvb@auth.meet.example.com" }
|
||||
|
||||
VirtualHost "auth.meet.example.com"
|
||||
ssl = {
|
||||
key = "/etc/prosody/certs/auth.meet.example.com.key";
|
||||
certificate = "/etc/prosody/certs/auth.meet.example.com.crt";
|
||||
}
|
||||
authentication = "internal_plain"
|
||||
|
||||
Component "focus.meet.example.com"
|
||||
component_secret = "jicofo_secret_test"
|
||||
|
||||
Component "speakerstats.meet.example.com" "speakerstats_component"
|
||||
muc_component = "conference.meet.example.com"
|
||||
|
||||
|
||||
Component "conferenceduration.meet.example.com" "conference_duration_component"
|
||||
muc_component = "conference.meet.example.com"
|
||||
|
||||
-- for Jibri
|
||||
VirtualHost "recorder.meet.example.com"
|
||||
modules_enabled = {
|
||||
"ping";
|
||||
}
|
||||
authentication = "internal_plain"
|
||||
c2s_require_encryption = false
|
||||