Skip to content

Commit db616b0

Browse files
authored
Merge pull request #243 from introlab/dev
Main merge for 1.2.5 release
2 parents 4d98fd3 + 04439e8 commit db616b0

File tree

63 files changed

+1837
-523
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1837
-523
lines changed

.gitignore

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,49 +51,34 @@ docs/_build/
5151
docs/--no-check-certificate
5252
docs/swagger.json
5353

54-
build-teraplus-Desktop_Qt_5_12_0_MSVC2017_64bit-Debug
5554
*.user
56-
build-teraserver-Desktop_Qt_5_12_0_MSVC2017_64bit-Debug
5755
.idea
58-
python-3.6
5956

6057
# Docker
6158
docker/prod/certificates/*.pem
6259

6360
# Node modules
6461
node_modules/
65-
/teraplus/CMakeLists.txt.user.4.9-pre1
66-
/teraserver/CMakeLists.txt.user.4.9-pre1
67-
/teraserver/python/config/TeraServerConfig.ini~0b30c4625222f7c8696ca353a9e47d17d15d6cb5
6862
/teraserver/python/messages/python/*
6963
/teraserver/python/certificates/ca_cert.pem
7064
/teraserver/python/certificates/ca_key.pem
7165
/teraserver/python/certificates/devices/client_certificate.pem
7266
/teraserver/python/certificates/devices/client_key.pem
7367
/teraserver/python/certificates/site_cert.pem
7468
/teraserver/python/certificates/site_key.pem
75-
/teraplus/client/resources/icons/device.psd
7669
/teraserver/python/uploads
77-
build-teraplus-Desktop_Qt_5_13_1_MSVC2017_64bit-Debug
78-
build-teraserver-Desktop_Qt_5_13_1_MSVC2017_64bit-Debug
79-
build-teraplus-Desktop_Qt_5_13_1_MSVC2017_64bit-Release
80-
build-teraserver-Desktop_Qt_5_13_2_MSVC2017_64bit-Debug
81-
build-teraserver-Desktop_Qt_5_14_0_MSVC2017_64bit-Debug
82-
build-teraserver-Desktop_Qt_5_14_1_MSVC2017_64bit-Debug
8370
protobuf
84-
teraserver/python/env/_python-3.6
85-
build-teraserver-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug
86-
python-3.8
87-
teraserver/python/services/BureauActif/uploads/
8871
/teraserver/python/OpenTeraServerVersion.py
8972
/teraserver/python/services/FileTransferService/upload
9073
files
9174
build-teraserver-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug
92-
python-3.10
9375
/teraserver/easyrtc/package-lock.json
9476
venv
9577
joss-paper/paper.jats
9678
joss-paper/paper.pdf
9779
/.vscode
98-
_python-3.10
9980
python-3.11
81+
build-teraserver-Desktop_Qt_6_6_1_MSVC2019_64bit-Debug
82+
python-3.10
83+
/teraserver/python/config/certificates
84+
/teraserver/python/tests/*.pem

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ You are welcome to participate in this effort. Leave us comments or report [Issu
5656

5757
## Publication(s)
5858

59+
* [![DOI](https://joss.theoj.org/papers/10.21105/joss.05497/status.svg)](https://doi.org/10.21105/joss.05497) Létourneau, D., Brière , S., et al., [OpenTera: A Framework for Telehealth Applications](https://doi.org/10.21105/joss.05497), Journal of Open Source Software, vol. 8, no 91, p. 5497 (2023)
5960
* Panchea, A.M., Létourneau, D., Brière, S. et al., [OpenTera: A microservice architecture solution for rapid prototyping of robotic solutions to COVID-19 challenges in care facilities](https://rdcu.be/cHzmf), Health Technol. 12, 583–596 (2022)
6061

6162
## Videos

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
88

99
project = 'OpenTera'
10-
copyright = '2023, Simon Brière, Dominic Létourneau'
10+
copyright = '2024, Simon Brière, Dominic Létourneau'
1111
author = 'Simon Brière, Dominic Létourneau'
12-
release = '1.2.4'
12+
release = '1.2.5'
1313
version = release
1414

1515
html_logo = 'images/LogoOpenTera200px.png'

teraserver/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ endif(NOT CMAKE_BUILD_TYPE)
1010
# Software version
1111
SET(OPENTERA_VERSION_MAJOR "1")
1212
SET(OPENTERA_VERSION_MINOR "2")
13-
SET(OPENTERA_VERSION_PATCH "4")
13+
SET(OPENTERA_VERSION_PATCH "5")
1414

1515
SET(OPENTERA_SERVER_VERSION OpenTera_v${OPENTERA_VERSION_MAJOR}.${OPENTERA_VERSION_MINOR}.${OPENTERA_VERSION_PATCH})
1616

teraserver/easyrtc/protected/index_users.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ <h5 class="modal-title" id="chronosDialogLongTitle" data-i18n="chronosDialog.tit
130130
</select>
131131
</div>
132132
<div class="form-group row pr-2">
133-
<label for="chronosTypeRadio" class="col-form-label col-4" data-i18n="chronosDialog.chrono-type">Type: </label>
133+
<label class="col-form-label col-4" data-i18n="chronosDialog.chrono-type">Type: </label>
134134
<div id="chronosTypeRadio" class="col col-form-label pl-0">
135135
<input id="optChronoType1" value=1 class="mr-1" type="radio" name="optChronoType" data-i18n="chronosDialog.chrono-countdown" checked /><label class="radio-inline pr-2" for="optChronoType1">Décompte</label>
136136
<input id="optChronoType2" value=2 class="mr-1" type="radio" name="optChronoType" data-i18n="chronosDialog.chrono-countup" /><label class="radio-inline" for="optChronoType2">Chronomètre</label>

teraserver/easyrtc/static/js/tera_layout_participants.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ function updateUserLocalViewLayout(){
9898
let remote_num = usedRemoteVideosIndexes.length;
9999
let usedLocalVideosIndexes = getVideoStreamsIndexes(localStreams);
100100
let local_num = usedLocalVideosIndexes.length;
101-
//console.log(usedLocalVideosIndexes);
101+
102102

103103
if (currentLargeViewId.startsWith('local') && local_num === 1){
104104
setColWidth(largeView, 10);
@@ -112,6 +112,7 @@ function updateUserLocalViewLayout(){
112112
}
113113

114114
switch(local_num){
115+
case 0:
115116
case 1:
116117
selfViewRow2.hide();
117118
break;

teraserver/easyrtc/static/js/tera_medias.js

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,61 @@ async function fillDefaultSourceList(){
1111
videoSources.length=0;
1212
audioSources.length=0;
1313

14-
// Open a stream to ask for permissions and allow listing of full name of devices.
15-
try{
16-
/*await navigator.mediaDevices.getUserMedia({
17-
audio: true,
18-
video: {
19-
width: {ideal: 1280, max: 1920 },
20-
height: {ideal: 720, max: 1080 },
21-
frameRate: {min: 15}//, ideal: 30}
22-
}
23-
});*/
24-
await navigator.mediaDevices.getUserMedia({
25-
audio: true,
26-
video: true
27-
});
14+
// Get devices ids in case the first camera returned by getUserMedia isn't valid (we use usermedia here only to get
15+
// camera names
16+
let all_devices;
17+
try {
18+
all_devices = await navigator.mediaDevices.enumerateDevices();
2819
}catch(err) {
29-
showError("fillDefaultSourceList() - getUserMedia",
30-
translator.translateForKey("errors.no-media-access", currentLang) + "<br><br>" +
31-
translator.translateForKey("errors.error-msg", currentLang) +
32-
":<br>" + err.name + " - " + err.message, true);
20+
showError("fillDefaultSourceList() - enumerate Initial Devices", err.name + " - " + err.message, true);
3321
throw err;
3422
}
3523

24+
// Open a stream to ask for permissions and allow listing of full name of devices.
25+
if (all_devices.length === 0){
26+
showError(translator.translateForKey("errors.no-media-access", currentLang),
27+
translator.translateForKey("errors.error-msg", currentLang), true);
28+
return;
29+
}
30+
let current_dev_index = 0;
31+
let ready = false;
32+
let devices_anom = [];
33+
all_devices.forEach(device => {
34+
if (device.kind === "videoinput"){
35+
devices_anom.push(device);
36+
}
37+
});
38+
while(!ready){
39+
try{
40+
// await navigator.mediaDevices.getUserMedia({
41+
// audio: true,
42+
// video: {
43+
// width: {ideal: 1280, max: 1920 },
44+
// height: {ideal: 720, max: 1080 },
45+
// frameRate: {min: 15}//, ideal: 30}
46+
// }
47+
// });
48+
await navigator.mediaDevices.getUserMedia({
49+
audio: true,
50+
video: {
51+
deviceId: devices_anom[current_dev_index].deviceId
52+
}
53+
}).then(function() {
54+
ready = true;
55+
});
56+
}catch(err) {
57+
current_dev_index+=1; // Will try next source
58+
if (current_dev_index >= devices_anom.length){
59+
// Tried every device
60+
showError("fillDefaultSourceList() - getUserMedia",
61+
translator.translateForKey("errors.no-media-access", currentLang) + "<br><br>" +
62+
translator.translateForKey("errors.error-msg", currentLang) +
63+
":<br>" + err.name + " - " + err.message, true);
64+
throw err;
65+
}
66+
}
67+
}
68+
3669
try {
3770
let devices = await navigator.mediaDevices.enumerateDevices();
3871
devices.forEach(device => {

teraserver/easyrtc/static/js/tera_webrtc.js

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@ let localStreams = []; // {peerid, streamname, stream: MediaStream}, order is im
1212
var connected = false;
1313
var needToCallOtherUsers = false;
1414

15+
let preinitCameras = true;
16+
1517
function connect() {
1618

1719
console.log("Connecting...");
18-
playSound("audioCalling");
20+
if (!preinitCameras)
21+
playSound("audioCalling");
1922

2023
/*var localFilter = easyrtc.buildLocalSdpFilter( {
2124
audioRecvBitrate:20, videoRecvBitrate:30 ,videoRecvCodec:"h264"
@@ -41,11 +44,61 @@ function connect() {
4144
//Post-connect Event listeners
4245
//easyrtc.setOnHangup(streamDisconnected);
4346
//easyrtc.setOnCall(newStreamStarted);
47+
if (preinitCameras)
48+
preloadCameras();
49+
else{
50+
connected = true;
51+
updateLocalAudioVideoSource(1);
52+
showLayout(true);
53+
}
4454

45-
connected = true;
46-
updateLocalAudioVideoSource(1);
4755

48-
showLayout(true);
56+
}
57+
58+
// On some devices, there's a strange bug that delays access to the camera, unless we try to access it at least once...
59+
function preloadCameras(){
60+
navigator.mediaDevices.enumerateDevices()
61+
.then(function(devices) {
62+
let preload_devices = [];
63+
devices.forEach(function(device) {
64+
if (device.kind === "videoinput"){
65+
if (!device.label.includes(" IR ")) { // Filter "IR" camera, since they won't work.
66+
preload_devices.push(device);
67+
}
68+
}
69+
//console.log(device.kind + ": " + device.label + " id = " + device.deviceId);
70+
});
71+
preloadCamera(preload_devices, 0);
72+
})
73+
.catch(function(err) {
74+
console.log(err.name + ": " + err.message);
75+
});
76+
77+
}
78+
79+
function preloadCamera(devices, current_index){
80+
if (current_index >= devices.length || current_index < 0){
81+
return;
82+
}
83+
84+
navigator.mediaDevices.getUserMedia({video: {deviceId: { exact: devices[current_index].deviceId }},
85+
audio: false}).then(function(stream){
86+
console.log("Preloaded camera " + devices[current_index].label + "(" + devices[current_index].deviceId + ")");
87+
stream.getTracks().forEach(track => track.stop());
88+
// Did we get at least the first stream? If so, start everything!
89+
//if (current_index === 0){
90+
if (!connected){
91+
playSound("audioCalling");
92+
connected = true;
93+
updateLocalAudioVideoSource(1);
94+
showLayout(true);
95+
}
96+
preloadCamera(devices, current_index + 1);
97+
}).catch(async function() {
98+
console.log("Can't preload camera: " + devices[current_index].label);
99+
/*await new Promise(resolve => setTimeout(resolve, 1000))*/
100+
preloadCamera(devices, current_index + 1);
101+
});
49102
}
50103

51104
function muteMicro(local, index, new_state){
@@ -264,14 +317,15 @@ function setPrimaryView(peer_id, streamname){
264317
setPrimaryViewIcon(primaryView.peerid, primaryView.streamName);
265318
}
266319

267-
function updateLocalAudioVideoSource(streamindex){
320+
async function updateLocalAudioVideoSource(streamindex){
268321
if (connected === true){
269322
let streamname = "localStream" + streamindex;
270323
if (streamindex === 1) // Default stream = no name.
271324
streamname = "";
272-
if (streamindex < localStreams.length){
325+
if (streamindex <= localStreams.length){
273326
console.log("Updating audio/video source: " + streamname);
274-
327+
// Stopping previous stream
328+
localStreams[streamindex-1].stream.getTracks().forEach(track => track.stop());
275329
}else {
276330
console.log("Creating audio/video source: " + streamname);
277331
}
@@ -398,7 +452,13 @@ function localVideoStreamSuccess(stream){
398452
}
399453

400454
function localVideoStreamError(errorCode, errorText){
401-
showError("initMediaSource", "Error #" + errorCode + ": " + errorText, true);
455+
if (currentConfig.currentVideoSourceIndex + 1 < videoSources.length){
456+
console.log("initMediaSource - Unable to open current source " + videoSources[currentConfig.currentVideoSourceIndex].label + " - Trying next one..." );
457+
currentConfig.currentVideoSourceIndex += 1;
458+
updateLocalAudioVideoSource(1);
459+
}else{
460+
showError("initMediaSource", "Error #" + errorCode + ": " + errorText, true);
461+
}
402462
}
403463

404464
function forwardData(data)

teraserver/python/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ External microservices can use this package as a base.
1010
OpenTera is licensed under [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) .
1111

1212
# Documentation
13-
Please visit our [WiKi documentation on GitHub](https://github.com/introlab/opentera/wiki)
13+
Please visit our [Documentation on GitHub](https://introlab.github.io/opentera/)
1414

1515
# Dependencies
1616
OpenTera is based or uses the following Open Source technologies :

teraserver/python/TeraServer.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ def init_opentera_service(config: ConfigManager):
9595
if __name__ == '__main__':
9696
parser = argparse.ArgumentParser(description='OpenTera Server')
9797
parser.add_argument('--enable_tests', help='Test mode for server.', default=False)
98+
parser.add_argument('--create_defaults', help='Create default server values (test mode)', default=False)
9899
args = parser.parse_args()
99100

100101
config_man = ConfigManager()
@@ -147,7 +148,7 @@ def init_opentera_service(config: ConfigManager):
147148
Globals.db_man.open(config_man.server_config['debug_mode'])
148149

149150
# Create minimal values, if required
150-
Globals.db_man.create_defaults(config=config_man, test=False)
151+
Globals.db_man.create_defaults(config=config_man, test=args.create_defaults)
151152

152153
except OperationalError as e:
153154
print("Unable to connect to database - please check settings in config file!", e)
@@ -168,7 +169,7 @@ def init_opentera_service(config: ConfigManager):
168169
init_opentera_service(config=config_man)
169170

170171
# Main Flask module
171-
flask_module = FlaskModule(config_man)
172+
flask_module = FlaskModule(config_man, test_mode=args.enable_tests)
172173

173174
# LOGIN MANAGER, must be initialized after Flask
174175
#################################################
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""Add device register key
2+
3+
Revision ID: e6ee93ef205b
4+
Revises: f41b70d6513e
5+
Create Date: 2024-01-23 08:15:07.224075
6+
7+
"""
8+
from opentera.db.models.TeraServerSettings import TeraServerSettings
9+
10+
11+
# revision identifiers, used by Alembic.
12+
revision = 'e6ee93ef205b'
13+
down_revision = 'f41b70d6513e'
14+
branch_labels = None
15+
depends_on = None
16+
17+
18+
def upgrade():
19+
TeraServerSettings.set_server_setting(TeraServerSettings.ServerDeviceRegisterKey,
20+
TeraServerSettings.generate_token_key(10))
21+
22+
23+
def downgrade():
24+
pass

0 commit comments

Comments
 (0)