diff --git a/front/package.json b/front/package.json index 9c8cba9fee9d8382b4efd46ad54d44f8566946fd..d7fc8ea006364994cf14ea1d223a6ab176536e2e 100644 --- a/front/package.json +++ b/front/package.json @@ -20,6 +20,7 @@ "lodash": "^4.17.10", "masonry-layout": "^4.2.2", "moment": "^2.22.2", + "openvidu-browser": "^2.6.0", "raven-js": "^3.26.4", "semantic-ui-css": "^2.3.3", "showdown": "^1.8.6", diff --git a/front/src/main.js b/front/src/main.js index eca2e620545a46e66dfc84606c3b85a0c2bab9f6..f87aae8b7f5f51d9eed98489907b8353ec1fd5d5 100644 --- a/front/src/main.js +++ b/front/src/main.js @@ -99,6 +99,7 @@ axios.interceptors.response.use(function (response) { return response }, function (error) { error.backendErrors = [] + return if (error.response.status === 401) { store.commit('auth/authenticated', false) logger.default.warn('Received 401 response from API, redirecting to login form') diff --git a/front/src/router/index.js b/front/src/router/index.js index f6b4d309f2f1ef1c8b60518587feb3149d9d6841..381707c1f8ae0f5086f1b1482ab945da8a6f876f 100644 --- a/front/src/router/index.js +++ b/front/src/router/index.js @@ -36,6 +36,7 @@ import LibrariesHome from '@/views/content/libraries/Home' import LibrariesUpload from '@/views/content/libraries/Upload' import LibrariesDetail from '@/views/content/libraries/Detail' import LibrariesFiles from '@/views/content/libraries/Files' +import StreamHome from '@/views/stream/Home' import RemoteLibrariesHome from '@/views/content/remote/Home' import Notifications from '@/views/Notifications' @@ -55,6 +56,11 @@ export default new Router({ name: 'about', component: About }, + { + path: '/stream', + name: 'stream', + component: StreamHome + }, { path: '/login', name: 'login', diff --git a/front/src/views/stream/Home.vue b/front/src/views/stream/Home.vue new file mode 100644 index 0000000000000000000000000000000000000000..0de43d8d4c704f48d0bc59b0d00f7375d54bbbd8 --- /dev/null +++ b/front/src/views/stream/Home.vue @@ -0,0 +1,161 @@ +<template> + <div class="main pusher" v-title="labels.title"> + <div class="ui vertical aligned stripe segment"> + <div> + <h1>Join a video session</h1> + <p> + <label>Session:</label> + <input type="text" v-model="sessionName"> + </p> + <p> + <button @click="joinSession(true)">Join and publish</button> + <button @click="joinSession(false)">Join</button> + </p> + </div> + + <div id="session"> + <h1 id="session-header"></h1> + <input v-if="session" type="button" @click="session.disconnect()" value="LEAVE"> + <div> + <div id="publisher"><h3>YOU</h3></div> + <div id="subscriber"><h3>OTHERS</h3></div> + </div> + </div> + </div> + </div> +</template> + +<script> +import axios from "axios"; +import "openvidu-browser/static/js/openvidu-browser-2.6.0"; + +export default { + data() { + return { + sessionName: "hello2", + session: null, + serverUrl: "https://" + location.hostname + ":4443/api/", + serverSecret: "MY_SECRET" + }; + }, + created() {}, + destroyed() { + if (this.session) { + this.session.disconnect(); + } + }, + computed: { + labels() { + return { + title: this.$gettext("Live streams") + }; + }, + client() { + let headers = { + Authorization: "Basic " + btoa("OPENVIDUAPP:" + this.serverSecret), + "Content-Type": "application/json" + }; + return axios.create({ + baseURL: this.serverUrl, + timeout: 1000, + headers: headers + }); + } + }, + methods: { + joinSession(publish) { + let OV = new OpenVidu(); + this.session = OV.initSession(); + let session = this.session; + session.on("streamCreated", function(event) { + let subscriber = session.subscribe(event.stream, "subscriber", { + subscribeToAudio: true, + subscribeToVideo: false, + }); + }); + + this.getToken(this.sessionName).then(token => { + session + .connect(token) + .then(() => { + if (publish) { + let publisher = OV.initPublisher("publisher", { + publishAudio: true, + publishVideo: false + }); + session.publish(publisher); + } + }) + .catch(error => { + console.log( + "There was an error connecting to the session:", + error.code, + error.message + ); + }); + }); + }, + getToken(mySessionId) { + let self = this; + return this.createSession(mySessionId).then(sessionId => + self.createToken(sessionId) + ); + }, + createSession(sessionId) { + // See https://openvidu.io/docs/reference-docs/REST-API/#post-apisessions + let self = this; + return new Promise((resolve, reject) => { + self.client + .post(self.serverUrl + "sessions", { customSessionId: sessionId }) + .then( + response => { + resolve(response.data.id); + }, + error => { + if (error.response.status === 409) { + resolve(sessionId); + } else { + console.warn( + "No connection to OpenVidu Server. This may be a certificate error at " + + self.serverUrl + ); + if ( + window.confirm( + 'No connection to OpenVidu Server. This may be a certificate error at "' + + self.serverUrl + + '"\n\nClick OK to navigate and accept it. ' + + 'If no certificate warning is shown, then check that your OpenVidu Server is up and running at "' + + self.serverUrl + + '"' + ) + ) { + location.assign(self.serverUrl + "/accept-certificate"); + } + } + } + ); + }); + }, + createToken(sessionId) { + // See https://openvidu.io/docs/reference-docs/REST-API/#post-apitokens + let self = this; + return new Promise((resolve, reject) => { + self.client + .post(self.serverUrl + "tokens", { session: sessionId }) + .then( + response => { + resolve(response.data.token); + }, + error => { + reject(error); + } + ); + }); + } + } +}; +</script> + +<!-- Add "scoped" attribute to limit CSS to this component only --> +<style> +</style> diff --git a/front/yarn.lock b/front/yarn.lock index b3d6a460b9a089424ed76e390f02445b5fcd6cc6..a14461a1df2b1f066543021d1ddf84040c92be2b 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -683,6 +683,14 @@ version "10.5.8" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.8.tgz#6f14ccecad1d19332f063a6a764f8907801fece0" +"@types/node@10.12.0": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.0.tgz#ea6dcbddbc5b584c83f06c60e82736d8fbb0c235" + +"@types/platform@1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/platform/-/platform-1.3.1.tgz#5a66616693e39f311b3f2c64c77cf2249e3d8325" + "@vue/babel-preset-app@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-3.0.0.tgz#56bbd624eb78fa1d5f7bf992ac62e6fc7557bd71" @@ -3406,6 +3414,12 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +freeice@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/freeice/-/freeice-2.2.2.tgz#f66b3e0f67c2fda835161f76d4884881cde83be4" + dependencies: + normalice "^1.0.0" + fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -3663,6 +3677,12 @@ har-validator@~5.1.0: ajv "^5.3.0" har-schema "^2.0.0" +hark@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/hark/-/hark-1.2.3.tgz#959981400f561be5580ecd4321a9f55b16bacbd0" + dependencies: + wildemitter "^1.2.0" + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -5227,6 +5247,10 @@ nopt@^4.0.1: abbrev "1" osenv "^0.1.4" +normalice@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/normalice/-/normalice-1.0.1.tgz#03435c2eecd5631a6bca02da3930ec3e345a80f7" + normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: version "2.4.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" @@ -5400,6 +5424,19 @@ opener@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" +openvidu-browser@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/openvidu-browser/-/openvidu-browser-2.6.0.tgz#d152ed732e4ab1126fd9a0348f68532a3d43e05f" + dependencies: + "@types/node" "10.12.0" + "@types/platform" "1.3.1" + freeice "2.2.2" + hark "1.2.3" + platform "1.3.5" + uuid "3.3.2" + webrtc-adapter "6.4.4" + wolfy87-eventemitter "5.2.5" + opn@^5.1.0, opn@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c" @@ -5673,6 +5710,10 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" +platform@1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.5.tgz#fb6958c696e07e2918d2eeda0f0bc9448d733444" + pluralize@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" @@ -6592,6 +6633,12 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rtcpeerconnection-shim@^1.2.14: + version "1.2.14" + resolved "https://registry.yarnpkg.com/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.14.tgz#67903d6f7b7bf508d6d1e4791de3e3b49c3754c0" + dependencies: + sdp "^2.6.0" + run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -6674,6 +6721,10 @@ scss-tokenizer@^0.2.3: js-base64 "^2.1.8" source-map "^0.4.2" +sdp@^2.6.0, sdp@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/sdp/-/sdp-2.9.0.tgz#2eed2d9c0b26c81ff87593107895c68d6fb9a0a6" + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -7604,7 +7655,7 @@ utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" -uuid@^3.0.1, uuid@^3.1.0, uuid@^3.3.2: +uuid@3.3.2, uuid@^3.0.1, uuid@^3.1.0, uuid@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" @@ -7884,6 +7935,13 @@ webpack@^4.15.1: watchpack "^1.5.0" webpack-sources "^1.0.1" +webrtc-adapter@6.4.4: + version "6.4.4" + resolved "https://registry.yarnpkg.com/webrtc-adapter/-/webrtc-adapter-6.4.4.tgz#a1e54cc2765c57976d3d682a0de15110b91115d7" + dependencies: + rtcpeerconnection-shim "^1.2.14" + sdp "^2.9.0" + websocket-driver@>=0.5.1: version "0.7.0" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb" @@ -7933,6 +7991,10 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" +wildemitter@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/wildemitter/-/wildemitter-1.2.0.tgz#29dd3a72d699c3e279dd021c3cd2150b82c9a211" + window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" @@ -7944,6 +8006,10 @@ with@^5.0.0: acorn "^3.1.0" acorn-globals "^3.0.0" +wolfy87-eventemitter@5.2.5: + version "5.2.5" + resolved "https://registry.yarnpkg.com/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.5.tgz#e7af2adbb84e481c65edeb2a2e01032c8ff1b88f" + wordwrap@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"