Improve playback
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Jack Hadrill 2021-01-06 03:55:38 +00:00
parent 9b967c2dde
commit 201ee9813f
11 changed files with 309 additions and 158 deletions

58
package-lock.json generated
View File

@ -9,12 +9,13 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"core-js": "^3.6.5", "core-js": "^3.6.5",
"crypto-js": "^4.0.0",
"mitt": "^2.1.0", "mitt": "^2.1.0",
"pretty-bytes": "^5.5.0", "pretty-bytes": "^5.5.0",
"pretty-ms": "^7.0.1",
"primeflex": "^2.0.0", "primeflex": "^2.0.0",
"primeicons": "^4.1.0", "primeicons": "^4.1.0",
"primevue": "^3.1.1", "primevue": "^3.1.1",
"sha-1": "^1.0.0",
"vue": "^3.0.0", "vue": "^3.0.0",
"vue-router": "^4.0.2", "vue-router": "^4.0.2",
"webtorrent": "^0.112.0" "webtorrent": "^0.112.0"
@ -5192,11 +5193,6 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/crypto-js": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
},
"node_modules/css-color-names": { "node_modules/css-color-names": {
"version": "0.0.4", "version": "0.0.4",
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
@ -11956,6 +11952,14 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/parse-ms": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz",
"integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==",
"engines": {
"node": ">=6"
}
},
"node_modules/parse-torrent": { "node_modules/parse-torrent": {
"version": "9.1.0", "version": "9.1.0",
"resolved": "https://registry.npmjs.org/parse-torrent/-/parse-torrent-9.1.0.tgz", "resolved": "https://registry.npmjs.org/parse-torrent/-/parse-torrent-9.1.0.tgz",
@ -12971,6 +12975,20 @@
"renderkid": "^2.0.4" "renderkid": "^2.0.4"
} }
}, },
"node_modules/pretty-ms": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz",
"integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==",
"dependencies": {
"parse-ms": "^2.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/primeflex": { "node_modules/primeflex": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/primeflex/-/primeflex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-2.0.0.tgz",
@ -14530,6 +14548,11 @@
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
"dev": true "dev": true
}, },
"node_modules/sha-1": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/sha-1/-/sha-1-1.0.0.tgz",
"integrity": "sha512-qjFA/+LdT0Gvu/JcmYTGZMvVy6WXJOWv1KQuY7HvSr2oBrMxA8PnZu2mc1/ZS2EvLMokj7lIeQsNPjkRzXrImw=="
},
"node_modules/sha.js": { "node_modules/sha.js": {
"version": "2.4.11", "version": "2.4.11",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
@ -22489,11 +22512,6 @@
"randomfill": "^1.0.3" "randomfill": "^1.0.3"
} }
}, },
"crypto-js": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
},
"css-color-names": { "css-color-names": {
"version": "0.0.4", "version": "0.0.4",
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
@ -27797,6 +27815,11 @@
"json-parse-better-errors": "^1.0.1" "json-parse-better-errors": "^1.0.1"
} }
}, },
"parse-ms": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz",
"integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA=="
},
"parse-torrent": { "parse-torrent": {
"version": "9.1.0", "version": "9.1.0",
"resolved": "https://registry.npmjs.org/parse-torrent/-/parse-torrent-9.1.0.tgz", "resolved": "https://registry.npmjs.org/parse-torrent/-/parse-torrent-9.1.0.tgz",
@ -28649,6 +28672,14 @@
"renderkid": "^2.0.4" "renderkid": "^2.0.4"
} }
}, },
"pretty-ms": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz",
"integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==",
"requires": {
"parse-ms": "^2.1.0"
}
},
"primeflex": { "primeflex": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/primeflex/-/primeflex-2.0.0.tgz", "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-2.0.0.tgz",
@ -29883,6 +29914,11 @@
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
"dev": true "dev": true
}, },
"sha-1": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/sha-1/-/sha-1-1.0.0.tgz",
"integrity": "sha512-qjFA/+LdT0Gvu/JcmYTGZMvVy6WXJOWv1KQuY7HvSr2oBrMxA8PnZu2mc1/ZS2EvLMokj7lIeQsNPjkRzXrImw=="
},
"sha.js": { "sha.js": {
"version": "2.4.11", "version": "2.4.11",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",

View File

@ -9,12 +9,13 @@
}, },
"dependencies": { "dependencies": {
"core-js": "^3.6.5", "core-js": "^3.6.5",
"crypto-js": "^4.0.0",
"mitt": "^2.1.0", "mitt": "^2.1.0",
"pretty-bytes": "^5.5.0", "pretty-bytes": "^5.5.0",
"pretty-ms": "^7.0.1",
"primeflex": "^2.0.0", "primeflex": "^2.0.0",
"primeicons": "^4.1.0", "primeicons": "^4.1.0",
"primevue": "^3.1.1", "primevue": "^3.1.1",
"sha-1": "^1.0.0",
"vue": "^3.0.0", "vue": "^3.0.0",
"vue-router": "^4.0.2", "vue-router": "^4.0.2",
"webtorrent": "^0.112.0" "webtorrent": "^0.112.0"

View File

@ -1,8 +1,9 @@
<template> <template>
<MenuBar :model="items">
</MenuBar>
<router-view />
<Toast position="top-right" /> <Toast position="top-right" />
<MenuBar :model="items" />
<div class="p-p-6">
<router-view />
</div>
</template> </template>
<script> <script>
@ -32,4 +33,18 @@ export default {
a:focus { a:focus {
box-shadow: none !important; box-shadow: none !important;
} }
h1 {
font-size: 4em;
margin-top: 0;
margin-bottom: 0
}
video {
width: 100%
}
.p-card {
margin-bottom: 1.5rem
}
</style> </style>

View File

@ -5,7 +5,7 @@
<i class="pi pi-upload"></i> <i class="pi pi-upload"></i>
<h2>{{ message }}</h2> <h2>{{ message }}</h2>
</div> </div>
<input type="file" hidden="true" ref="fileInput" @change="selected" /> <input type="file" hidden="true" ref="fileInput" :disabled="disabled" @change="selected" />
</template> </template>
</Card> </Card>
</template> </template>
@ -16,7 +16,8 @@ export default {
'selected' 'selected'
], ],
props: [ props: [
'message' 'message',
'disabled'
], ],
setup (props, context) { setup (props, context) {
const selected = (event) => { const selected = (event) => {

View File

@ -4,7 +4,7 @@
Wire statistics Wire statistics
</template> </template>
<template #content> <template #content>
<DataTable :value="wires" :autoLayout="true"> <DataTable :value="wireStatistics" sortField="peerId" :autoLayout="true">
<Column field="peerId" header="Wire ID"></Column> <Column field="peerId" header="Wire ID"></Column>
<Column field="remoteAddress" header="Remote address"></Column> <Column field="remoteAddress" header="Remote address"></Column>
<Column field="uploadSpeed" header="Upload speed"></Column> <Column field="uploadSpeed" header="Upload speed"></Column>
@ -19,17 +19,10 @@
<script> <script>
export default { export default {
props: { props: {
wires: { wireStatistics: {
type: Object, type: Object,
required: true required: true
} }
},
setup (props) {
} }
} }
</script> </script>
<style>
</style>

View File

@ -0,0 +1,15 @@
import sha1 from 'sha-1'
import prettyBytes from 'pretty-bytes'
const generateWireStatistics = (wires) => {
return wires.map(wire => ({
peerId: sha1(wire.peerId).toString().substring(0, 100),
remoteAddress: wire.remoteAddress,
uploadSpeed: prettyBytes(wire.uploadSpeed()) + 'ps',
uploaded: prettyBytes(wire.uploaded),
downloadSpeed: prettyBytes(wire.downloadSpeed()) + 'ps',
downloaded: prettyBytes(wire.downloaded)
}))
}
export default generateWireStatistics

View File

@ -4,7 +4,7 @@ import Router from './router'
// Import global components. // Import global components.
import FileSelect from '@/components/FileSelect.vue' import FileSelect from '@/components/FileSelect.vue'
import HostWires from '@/components/HostWires.vue' import WireStatistics from '@/components/WireStatistics.vue'
// Import PrimeVue dependencies. // Import PrimeVue dependencies.
import PrimeVue from 'primevue/config' import PrimeVue from 'primevue/config'
@ -42,9 +42,12 @@ app.component('Toast', Toast)
// Register VCinemaApp components. // Register VCinemaApp components.
app.component('FileSelect', FileSelect) app.component('FileSelect', FileSelect)
app.component('HostWires', HostWires) app.component('WireStatistics', WireStatistics)
app.provide('trackers', [ app.provide('trackers', [
'wss://tracker.btorrent.xyz',
'wss://tracker.openwebtorrent.com',
'wss://tracker.fastcast.nz',
'wss://tracker.sloppyta.co:443/announce', 'wss://tracker.sloppyta.co:443/announce',
'wss://tracker.files.fm:7073/announce', 'wss://tracker.files.fm:7073/announce',
'wss://open.tube:443/tracker/socket', 'wss://open.tube:443/tracker/socket',

File diff suppressed because one or more lines are too long

View File

@ -1,58 +1,81 @@
<template> <template>
<div class="p-p-6"> <h1>Host</h1>
<h1 class="p-my-0">Host</h1> <FileSelect v-if="!video.file" message="Drag a H.264 encoded MP4 here to start a screen." :disabled="!!video.file" @selected="onFilesSelected" />
<Card class="p-mb-4" v-show="state.active"> <Card v-else-if="!state.active">
<template #content> <template #content>
<video id="player" controls="true"></video> <div class="p-text-center">
<ProgressSpinner />
<h2>Generating hash. Please wait...</h2>
</div>
</template>
</Card>
<div v-show="state.active && !!video.file">
<Card>
<template #content>
<video controls="true" ref="player"></video>
</template> </template>
</Card> </Card>
<div v-if="!state.active" key="host-pending"> <Card>
<FileSelect class="p-mb-4" v-if="!state.fileSelected" key="file-select" message="Drag a H.264 encoded MP4 here to start a screen." @selected="onFilesSelected" /> <template #title>
<Card class="p-mb-4" v-else key="file-selected"> Properties
<template #content> </template>
<div class="p-text-center"> <template #content>
<ProgressSpinner /> <div class="p-grid">
<h2>Generating hash. Please wait...</h2> <div class="p-col-12 p-md-4">
<h5>Filename</h5>
<span>{{video.name}}</span>
</div> </div>
</template> <div class="p-col-12 p-md-4">
</Card> <h5>Duration</h5>
</div> <span>{{video.hDuration}}</span>
<div v-else key="host-live"> </div>
<Card class="p-mb-4"> <div class="p-col-12 p-md-4">
<template #title> <h5>File size</h5>
Info hash <span>{{video.hSize}}</span>
</template> </div>
<template #content> <div class="p-col-12">
<InputText id="infoHash" type="text" v-model="state.infoHash" disabled /> <h5>ID</h5>
</template> <div class="p-fluid">
</Card> <InputText class="p-inputtext-sm" :value="state.infoHash" disabled />
<HostWires class="p-mb-4" :wires="wires"/> </div>
</div> </div>
</div>
</template>
</Card>
<WireStatistics :wireStatistics="wireStatistics"/>
</div> </div>
</template> </template>
<script> <script>
import { inject, onBeforeUnmount, onMounted, reactive } from 'vue' import { inject, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
import { useToast } from 'primevue/usetoast' import { useToast } from 'primevue/usetoast'
import prettyBytes from 'pretty-bytes' import prettyBytes from 'pretty-bytes'
import sha1 from 'crypto-js/sha1' import prettyMilliseconds from 'pretty-ms'
import WebTorrent from 'webtorrent/webtorrent.min.js' import WebTorrent from 'webtorrent/webtorrent.min.js'
import generateWireStatistics from '@/helpers/wire-statistics'
export default { export default {
setup () { setup () {
const toast = useToast() const toast = useToast()
const trackers = inject('trackers') const trackers = inject('trackers')
var webTorrent = null
const player = ref(null)
const video = reactive({
file: null,
name: '',
size: 0,
duration: 0,
hSize: '',
hDuration: ''
})
const state = reactive({ const state = reactive({
active: false, active: false,
fileSelected: false, torrent: null,
infoHash: '', infoHash: '',
size: 0,
uploaded: 0, uploaded: 0,
uploadSpeed: 0,
wires: [] wires: []
}) })
var webTorrent = null const wireStatistics = reactive([])
const wires = reactive([])
onMounted(() => { onMounted(() => {
webTorrent = new WebTorrent() webTorrent = new WebTorrent()
@ -68,67 +91,62 @@ export default {
if (f?.length !== 1 || !f[0].name.endsWith('.mp4') || f[0].type !== 'video/mp4') { if (f?.length !== 1 || !f[0].name.endsWith('.mp4') || f[0].type !== 'video/mp4') {
toast.add({ severity: 'error', summary: 'Bad file input', detail: 'You must select a single H.264 encoded MP4.', life: 3000 }) toast.add({ severity: 'error', summary: 'Bad file input', detail: 'You must select a single H.264 encoded MP4.', life: 3000 })
} else { } else {
state.fileSelected = true // Load video in DOM.
} player.value.src = URL.createObjectURL(f[0])
seedFile(f[0]) // Load video information such that it can be rendered.
video.file = f[0]
video.name = f[0].name
video.size = f[0].size
video.hSize = prettyBytes(f[0].size)
player.value.addEventListener('loadedmetadata', (e) => {
video.duration = e.target.duration
video.hDuration = prettyMilliseconds(e.target.duration * 1000)
})
// Seed the video.
seedVideo(f[0])
}
} }
const seedFile = (file) => { const seedVideo = (video) => {
webTorrent.seed(file, { announce: trackers }, (torrent) => { webTorrent.seed(video, { announce: trackers }, torrent => {
var file = torrent.files.find(file => {
return file.name.endsWith('.mp4')
})
state.size = file.size
state.active = true state.active = true
state.infoHash = torrent.infoHash state.infoHash = torrent.infoHash
state.wires = torrent.wires
var fileRendered = false
file.createReadStream().on('data', () => {
if (!fileRendered) {
file.renderTo('#player')
fileRendered = true
}
})
torrent.on('upload', bytes => { torrent.on('upload', bytes => {
state.uploaded += bytes state.uploaded += bytes
updateWireStatistics()
}) })
torrent.on('wire', wire => { torrent.on('wire', wire => {
state.wires.push(wire) updateWireStatistics()
toast.add({ severity: 'info', summary: 'New watcher', detail: 'Someone has joined your screen.', life: 3000 }) toast.add({ severity: 'info', summary: 'New watcher', detail: 'Someone has joined your screen.', life: 3000 })
}) })
toast.add({ severity: 'success', summary: 'File added', detail: `You are now sharing ${file.name}`, life: 5000 }) toast.add({ severity: 'success', summary: 'Video added', detail: `You are now sharing ${video.name}`, life: 5000 })
}) })
} }
setInterval(() => { const updateWireStatistics = () => {
const newWires = state.wires.map(wire => ({ var newWireStatistics = generateWireStatistics(state.wires)
peerId: sha1(wire.peerId).toString().substring(0, 100), wireStatistics.splice.apply(wireStatistics, [0, newWireStatistics.length].concat(newWireStatistics))
remoteAddress: wire.remoteAddress, }
uploadSpeed: prettyBytes(wire.uploadSpeed()) + 'ps',
uploaded: prettyBytes(wire.uploaded),
downloadSpeed: prettyBytes(wire.downloadSpeed()) + 'ps',
downloaded: prettyBytes(wire.downloaded)
}))
wires.splice.apply(wires, [0, newWires.length].concat(newWires))
}, 500)
return { return {
onFilesSelected, onFilesSelected,
player,
video,
state, state,
wires wireStatistics
} }
} }
} }
</script> </script>
<style> <style>
#infoHash, video#player { #infoHash {
width: 100%; width: 100%;
} }
</style> </style>

View File

@ -1,19 +1,17 @@
<template> <template>
<div class="p-p-6"> <h1>Join</h1>
<h1 class="p-my-0">Join</h1> <Card>
<Card> <template #content>
<template #content> <div class="p-fluid p-formgrid p-grid p-jc-center">
<div class="p-fluid p-formgrid p-grid p-jc-center"> <div class="p-field p-col-12 p-lg-8">
<div class="p-field p-col-12 p-lg-8"> <InputText :placeholder="defaultId" v-model="id" @keydown.enter="onIdSelected" />
<InputText :placeholder="defaultId" v-model="id" @keydown.enter="onIdSelected" />
</div>
<div class="p-field p-col-12 p-lg-2">
<Button label="Join" @click="onIdSelected" />
</div>
</div> </div>
</template> <div class="p-field p-col-12 p-lg-2">
</Card> <Button label="Join" @click="onIdSelected" />
</div> </div>
</div>
</template>
</Card>
</template> </template>
<script> <script>
@ -26,7 +24,7 @@ export default {
const router = useRouter() const router = useRouter()
const toast = useToast() const toast = useToast()
const id = ref() const id = ref()
const defaultId = '08ada5a7a6183aae1e09d831df6748d566095a10' const defaultId = '6a9759bffd5c0af65319979fb7832189f4f3c35d'
const onIdSelected = () => { const onIdSelected = () => {
if (!id.value) { if (!id.value) {
id.value = defaultId id.value = defaultId

View File

@ -1,27 +1,57 @@
<template> <template>
<div class="p-p-6"> <h1>Watch</h1>
<h1 class="p-my-0">Watch</h1> <Card class="p-text-center" v-if="!state.active">
<Card class="p-mb-4" v-if="state.wires.length === 0"> <template #content>
<ProgressSpinner />
<h2>Waiting for connections. Please wait...</h2>
</template>
</Card>
<div v-show="state.active">
<Card >
<template #content> <template #content>
<div class="p-text-center"> <ProgressBar class="p-mb-2" :value="progress" :showValue="false" />
<ProgressSpinner /> <video ref="player" controls="true"></video>
<h2>Waiting for connections. Please wait...</h2> </template>
</Card>
<Card>
<template #title>
Properties
</template>
<template #content>
<div class="p-grid">
<div class="p-col-12 p-md-4">
<h5>Filename</h5>
<span>{{video.name}}</span>
</div>
<div class="p-col-12 p-md-4">
<h5>Duration</h5>
<span>{{video.hDuration}}</span>
</div>
<div class="p-col-12 p-md-4">
<h5>File size</h5>
<span>{{video.hSize}}</span>
</div>
<div class="p-col-12">
<h5>ID</h5>
<div class="p-fluid">
<InputText class="p-inputtext-sm" :value="state.infoHash" disabled />
</div>
</div>
</div> </div>
</template> </template>
</Card> </Card>
<Card class="p-mb-4" v-show="state.wires.length !== 0"> <WireStatistics :wireStatistics="wireStatistics"/>
<template #content>
<video id="player" controls="true"></video>
</template>
</Card>
</div> </div>
</template> </template>
<script> <script>
import { inject, onBeforeMount, onBeforeUnmount, onMounted, reactive } from 'vue' import { inject, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useToast } from 'primevue/usetoast' import { useToast } from 'primevue/usetoast'
import prettyBytes from 'pretty-bytes'
import prettyMilliseconds from 'pretty-ms'
import WebTorrent from 'webtorrent/webtorrent.min.js' import WebTorrent from 'webtorrent/webtorrent.min.js'
import generateWireStatistics from '@/helpers/wire-statistics'
export default { export default {
props: { props: {
@ -34,10 +64,25 @@ export default {
const router = useRouter() const router = useRouter()
const toast = useToast() const toast = useToast()
const trackers = inject('trackers') const trackers = inject('trackers')
var webTorrent = null
const player = ref(null)
const progress = ref()
const video = reactive({
file: null,
name: '',
size: 0,
duration: 0,
hSize: '',
hDuration: ''
})
const state = reactive({ const state = reactive({
active: false,
torrent: null,
infoHash: '',
downloaded: 0,
wires: [] wires: []
}) })
var webTorrent = null const wireStatistics = reactive([])
onBeforeMount(() => { onBeforeMount(() => {
if (!/^([a-f0-9]{40})$/.test(props.id)) { if (!/^([a-f0-9]{40})$/.test(props.id)) {
@ -48,26 +93,7 @@ export default {
onMounted(() => { onMounted(() => {
webTorrent = new WebTorrent() webTorrent = new WebTorrent()
webTorrent.add(props.id, { announce: trackers }, function (torrent) { downloadVideo(props.id)
var file = torrent.files.find(function (file) {
return file.name.endsWith('.mp4')
})
var fileRendered = false
file.createReadStream().on('data', () => {
if (!fileRendered) {
file.renderTo('#player')
fileRendered = true
}
})
torrent.on('wire', wire => {
console.log(wire)
state.wires.push(wire)
toast.add({ severity: 'info', summary: 'New watcher', detail: 'Someone has joined your screen.', life: 3000 })
})
})
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
@ -76,8 +102,65 @@ export default {
} }
}) })
const downloadVideo = (infoHash) => {
webTorrent.add(infoHash, { announce: trackers }, torrent => {
torrent.on('wire', () => {
updateWireStatistics()
toast.add({ severity: 'info', summary: 'New watcher', detail: 'Someone has joined your screen.', life: 3000 })
})
torrent.on('noPeers', () => {
toast.add({ severity: 'warning', summary: 'No peers', detail: 'No one is sharing this video at the moment.', life: 3000 })
})
torrent.on('upload', bytes => {
updateWireStatistics()
})
torrent.on('download', bytes => {
if (!state.active) {
state.active = true
initialise(torrent)
}
state.downloaded += bytes
progress.value = state.downloaded / video.size * 100
updateWireStatistics()
})
})
}
const initialise = (torrent) => {
state.torrent = torrent
state.infoHash = torrent.infoHash
state.wires = torrent.wires
var file = torrent.files.find(file => {
return file.name.endsWith('.mp4')
})
video.file = file
video.name = file.name
video.size = file.length
video.hSize = prettyBytes(file.length)
player.value.addEventListener('loadedmetadata', (e) => {
video.duration = e.target.duration
video.hDuration = prettyMilliseconds(e.target.duration * 1000)
})
file.renderTo(player.value)
}
const updateWireStatistics = () => {
var newWireStatistics = generateWireStatistics(state.wires)
wireStatistics.splice.apply(wireStatistics, [0, newWireStatistics.length].concat(newWireStatistics))
}
return { return {
state player,
video,
state,
progress,
wireStatistics
} }
} }
} }