Working datachannel, youtube support & server fix

This commit is contained in:
Thomas
2020-07-29 19:03:11 +02:00
parent 927fa2d1a2
commit e8247a1ba3
9 changed files with 295 additions and 16 deletions

View File

@@ -5727,6 +5727,11 @@
"integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
"dev": true
},
"get-youtube-id": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-youtube-id/-/get-youtube-id-1.0.1.tgz",
"integrity": "sha512-5yidLzoLXbtw82a/Wb7LrajkGn29BM6JuLWeHyNfzOGp1weGyW4+7eMz6cP23+etqj27VlOFtq8fFFDMLq/FXQ=="
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@@ -7058,6 +7063,11 @@
}
}
},
"load-script": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz",
"integrity": "sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ="
},
"loader-fs-cache": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz",
@@ -9923,6 +9933,11 @@
}
}
},
"sister": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/sister/-/sister-3.0.2.tgz",
"integrity": "sha512-p19rtTs+NksBRKW9qn0UhZ8/TUI9BPw9lmtHny+Y3TinWlOa9jWh9xB0AtPSdmOy49NJJJSSe0Ey4C7h0TrcYA=="
},
"slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
@@ -11314,6 +11329,15 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"vue-youtube": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/vue-youtube/-/vue-youtube-1.4.0.tgz",
"integrity": "sha512-PCyfGAouSt6rTX0GLUzpdX2XC52zYf7a9mUhdp53jeDlPoU40hpsvyV3Zg2+947pvbv27ORcmtzm2fqO08kh9Q==",
"requires": {
"get-youtube-id": "^1.0.0",
"youtube-player": "^5.4.0"
}
},
"vuex": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.5.1.tgz",
@@ -12328,6 +12352,31 @@
"dev": true
}
}
},
"youtube-player": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/youtube-player/-/youtube-player-5.5.2.tgz",
"integrity": "sha512-ZGtsemSpXnDky2AUYWgxjaopgB+shFHgXVpiJFeNB5nWEugpW1KWYDaHKuLqh2b67r24GtP6HoSW5swvf0fFIQ==",
"requires": {
"debug": "^2.6.6",
"load-script": "^1.0.0",
"sister": "^3.0.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
}
}
}

View File

@@ -13,6 +13,7 @@
"register-service-worker": "^1.7.1",
"vue": "^2.6.11",
"vue-router": "^3.2.0",
"vue-youtube": "^1.4.0",
"vuex": "^3.4.0"
},
"devDependencies": {

View File

@@ -0,0 +1,13 @@
<template>
<div id="player"></div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -4,11 +4,13 @@ import './registerServiceWorker'
import router from './router'
import store from './store'
import Buefy from 'buefy'
import VueYoutube from 'vue-youtube'
import 'buefy/dist/buefy.css'
// import './assets/style.scss'
Vue.use(Buefy)
Vue.use(VueYoutube)
Vue.config.productionTip = false
new Vue({

View File

@@ -3,7 +3,16 @@ const state = {
roomStatus: {
roomName: '',
roomCode: '',
current: '',
player: {
timeCode: 0,
playing: true
},
current: {
link: '',
title: '',
votes: 0,
voters: []
},
playlist: []
}
}
@@ -23,6 +32,35 @@ const actions = {
},
setAdmin ({ commit }) {
commit('SET_ADMIN')
},
vote ({ commit, dispatch, state }, { link, isPositive, voterName }) {
console.log('vote on ' + link + ' (' + isPositive + ') by ' + voterName)
if (isPositive) {
commit('ADD_VOTE', {
link: link,
voterName: voterName
})
} else {
commit('REMOVE_VOTE', {
link: link,
voterName: voterName
})
}
dispatch('rtc/broadcast', { message: state.roomStatus, type: 'status' }, { root: true })
},
setCurrent ({ commit, dispatch }, { playerStatus, timeCode }) {
switch (playerStatus) {
case 0:
commit('CURRENT_END')
break
case 1:
commit('CURRENT_PLAY', timeCode)
break
case 2:
commit('CURRENT_PAUSE', timeCode)
break
}
dispatch('rtc/broadcast', { message: state.roomStatus, type: 'status' }, { root: true })
}
}
@@ -39,8 +77,58 @@ const mutations = {
SET_ADMIN (state) {
state.admin = true
},
BROADCAST_ROOMSTATUS (state) {
ADD_VOTE (state, { link, voterName }) {
var play = state.roomStatus.playlist.find(play => play.link === link)
if (play === undefined) {
play = {
link: link,
votes: 1,
voters: [
voterName
]
}
if (state.roomStatus.current.votes === 0) state.roomStatus.current = play
else state.roomStatus.playlist.push(play)
} else {
play.votes++
play.voters.push(voterName)
}
},
REMOVE_VOTE (state, { link, voterName }) {
var play = state.roomStatus.playlist.find(play => play.link === link)
play.votes--
const index = play.voters.indexOf(voterName)
if (index > -1) {
play.voters.splice(index, 1)
}
if (play.vote === 0) {
const index = state.roomStatus.playlist.indexOf(play)
if (index > -1) {
state.roomStatus.playlist.splice(index, 1)
}
}
},
CURRENT_END (state) {
if (state.roomStatus.playlist.length === 0) {
state.roomStatus.current.link = ''
state.roomStatus.current.title = ''
state.roomStatus.current.votes = 0
state.roomStatus.current.voters = []
} else {
state.roomStatus.playlist.sort((a, b) => {
return b.votes - a.votes
})
state.roomStatus.current = state.roomStatus.playlist.shift()
}
},
CURRENT_PAUSE (state, timeCode) {
state.roomStatus.player.playing = false
state.roomStatus.player.timeCode = timeCode
},
CURRENT_PLAY (state, timeCode) {
state.roomStatus.player.playing = true
state.roomStatus.player.timeCode = timeCode
}
}

View File

@@ -39,8 +39,8 @@ const actions = {
leave ({ commit }) {
commit('LEAVE')
},
broadcast ({ commit }, message) {
commit('BROADCAST', message)
broadcast ({ commit }, { message, type }) {
commit('BROADCAST', { message: message, type: type })
}
}
@@ -79,6 +79,7 @@ const mutations = {
var peer = state.peers.find(peer => peer.name === target)
peer.dataChannel = peer.connection.createDataChannel('dataChannel')
peer.dataChannel.onmessage = handleDataChannelMessage
peer.dataChannel.onopen = handleDataChannelStateChangeEvent
peer.dataChannel.onclose = handleDataChannelStateChangeEvent
@@ -127,9 +128,14 @@ const mutations = {
})
state.peers = {}
},
BROADCAST (state, message) {
BROADCAST (state, { message, type }) {
const data = JSON.stringify({
type: type,
message: message
})
console.log('[RTC] broadcast message ' + data)
state.peers.forEach(peer => {
peer.dataChannel.send(message)
peer.dataChannel.send(data)
})
}
}
@@ -167,17 +173,19 @@ function handleDataChannelCallback (event) {
peer.dataChannel.onopen = handleDataChannelStateChangeEvent
peer.dataChannel.onclose = handleDataChannelStateChangeEvent
store.dispatch('rtc/broadcast', store.state.room.roomStatus)
store.dispatch('rtc/broadcast', { message: store.state.room.roomStatus, type: 'status' })
}
function handleDataChannelMessage (event) {
console.log('[RTC] data channel message ' + event.data)
var data = event.data
var data = JSON.parse(event.data)
console.log('[RTC] data channel message type ' + data.type)
switch (data.type) {
case 'status':
store.dispatch('room/setRoomStatus', data.message)
break
case 'vote':
store.dispatch('room/vote', { link: data.message.link, isPositive: data.message.isPositive, voterName: data.message.voterName })
break
}
}

View File

@@ -4,7 +4,19 @@
<h2 class="subtitle">{{roomStatus.current.title}}</h2>
<b-tabs>
<b-tab-item label="Player" :visible="settings.showPlayer">
<div class="playerDiv">
<youtube
:video-id="roomStatus.current.link"
nocookie="true"
ref="youtube"
@playing="roomStatus.player.playing"
></youtube>
</div>
</b-tab-item>
<b-tab-item label="Playlist">
<b-button icon-left="plus" @click="addLinkPrompt">Add link</b-button>
<b-table :data="roomStatus.playlist" striped hoverable default-sort="vote">
<template slot-scope="props">
<b-table-column field="title" label="Title">
@@ -16,24 +28,37 @@
</b-table-column>
<b-table-column field="vote" label="Votes">
{{props.row.vote}}
{{props.row.votes}}
</b-table-column>
<b-table-column field="voters" label="Voters" :visible="isAdmin">
{{props.row.voters}}
</b-table-column>
<b-table-column>
<b-button icon-left="arrow-up-bold-outline" type="is-dark"/>
<b-button icon-left="arrow-down-bold-outline" type="is-dark"/>
<b-button v-if="hasVoted(props.row)" icon-left="arrow-down-bold-outline" type="is-dark" @click="vote (props.row.link, false)"/>
<b-button v-else icon-left="arrow-up-bold-outline" type="is-dark" @click="vote (props.row.link, true)"/>
</b-table-column>
</template>
</b-table>
</b-tab-item>
<b-tab-item label="Admin" :visible="showAdmin">
<b-tab-item label="Admin" :visible="isAdmin">
<b-button @click="broadcastStatus">Force status update</b-button>
<b-table :data="usersList" striped hoverable>
<template slot-scope="props">
<b-table-column field="name" label="Name">
{{props.row.name}}
</b-table-column>
<b-table-column field="connection" label="Connection">
{{props.row.connection.signalingState}}
</b-table-column>
<b-table-column field="data" label="DataChannel">
{{props.row.dataChannel.readyState}}
</b-table-column>
<b-table-column>
<b-button icon-left="karate" type="is-dark"/>
</b-table-column>
@@ -45,7 +70,17 @@
<h1 class="subtitle">{{roomStatus.roomCode}}</h1>
</b-tab-item>
<b-tab-item label="Settings">
<div class="field">
<b-switch v-model="settings.playLink">Play link</b-switch>
</div>
<div class="field">
<b-switch v-model="settings.showPlayer" :disabled="!settings.playLink">Show player</b-switch>
</div>
</b-tab-item>
</b-tabs>
<b-loading is-full-page :active.sync="isRoomLoading"/>
</div>
</template>
@@ -53,10 +88,13 @@
export default {
name: 'Room',
computed: {
player () {
return this.$refs.youtube.player
},
roomStatus () {
return this.$store.state.room.roomStatus
},
showAdmin () {
isAdmin () {
return this.$store.state.room.admin
},
usersList () {
@@ -65,14 +103,86 @@ export default {
},
isLoggedIn () {
return this.$store.state.app.loginSuccess
},
isRoomLoading () {
return this.roomStatus.roomName === ''
}
},
data () {
return {
settings: {
playLink: true,
showPlayer: true
}
}
},
mounted () {
if (!this.isLoggedIn) this.$router.push({ name: 'Home' })
this.player.addEventListener('onStateChange', this.playerStateChange)
this.player.playVideo()
},
watch: {
roomStatus: function (status) {
this.player.seekTo(status.player.timeCode, true)
if (status.player.playing) this.player.playVideo()
else this.player.pauseVideo()
}
},
methods: {
broadcastStatus () {
this.$store.dispatch('rtc/broadcast', { message: this.$store.state.room.roomStatus, type: 'status' })
},
addLinkPrompt () {
this.$buefy.dialog.prompt({
message: 'Add a youtube link',
trapFocus: true,
inputAttrs: {
placeholder: 'https://www.youtube.com/watch?v=YItIK09bpKk',
minlength: 10
},
cancelText: 'Nah',
confirmText: 'Add',
onConfirm: (link) => this.addLink(link)
})
},
addLink (link) {
const linkID = this.$youtube.getIdFromUrl(link)
if (linkID === null) {
this.$buefy.toast.open('Invalid youtube link')
return
}
if (this.isAdmin) {
this.$store.dispatch('room/vote', { link: linkID, isPositive: true, voterName: this.$store.state.rtc.name })
} else {
this.vote(linkID, true)
}
},
vote (link, isPositive) {
const message = {
type: 'vote',
link: link,
isPositive: isPositive,
voterName: this.$store.state.rtc.name
}
this.$store.dispatch('rtc/broadcast', { message: message, type: 'vote' })
},
hasVoted (play) {
return play.voters.includes(this.$store.state.rtc.name)
},
async playerStateChange (event) {
console.log('[PLAYER] Status change ' + event.data)
if (this.isAdmin) this.$store.dispatch('room/setCurrent', { playerStatus: event.data, timeCode: await this.$refs.youtube.player.getCurrentTime() })
}
}
}
</script>
<style>
.room {
margin-top: 20px;
}
.playerDiv {
height: 500px;
}
</style>