Working datachannel, youtube support & server fix
This commit is contained in:
13
client/src/components/Player.vue
Normal file
13
client/src/components/Player.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div id="player"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -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({
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user