Improve playback
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
9b967c2dde
commit
201ee9813f
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
21
src/App.vue
21
src/App.vue
|
@ -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>
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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>
|
|
|
@ -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
|
|
@ -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
|
@ -1,14 +1,7 @@
|
||||||
<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>
|
|
||||||
<video id="player" controls="true"></video>
|
|
||||||
</template>
|
|
||||||
</Card>
|
|
||||||
<div v-if="!state.active" key="host-pending">
|
|
||||||
<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" />
|
|
||||||
<Card class="p-mb-4" v-else key="file-selected">
|
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="p-text-center">
|
<div class="p-text-center">
|
||||||
<ProgressSpinner />
|
<ProgressSpinner />
|
||||||
|
@ -16,43 +9,73 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
<div v-show="state.active && !!video.file">
|
||||||
<div v-else key="host-live">
|
<Card>
|
||||||
<Card class="p-mb-4">
|
|
||||||
<template #title>
|
|
||||||
Info hash
|
|
||||||
</template>
|
|
||||||
<template #content>
|
<template #content>
|
||||||
<InputText id="infoHash" type="text" v-model="state.infoHash" disabled />
|
<video controls="true" ref="player"></video>
|
||||||
</template>
|
</template>
|
||||||
</Card>
|
</Card>
|
||||||
<HostWires class="p-mb-4" :wires="wires"/>
|
<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>
|
||||||
|
<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>
|
||||||
|
</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
|
||||||
const seedFile = (file) => {
|
video.size = f[0].size
|
||||||
webTorrent.seed(file, { announce: trackers }, (torrent) => {
|
video.hSize = prettyBytes(f[0].size)
|
||||||
var file = torrent.files.find(file => {
|
player.value.addEventListener('loadedmetadata', (e) => {
|
||||||
return file.name.endsWith('.mp4')
|
video.duration = e.target.duration
|
||||||
|
video.hDuration = prettyMilliseconds(e.target.duration * 1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
state.size = file.size
|
// Seed the video.
|
||||||
|
seedVideo(f[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const seedVideo = (video) => {
|
||||||
|
webTorrent.seed(video, { announce: trackers }, torrent => {
|
||||||
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>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<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">
|
||||||
|
@ -13,7 +12,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
|
||||||
</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
|
||||||
|
|
|
@ -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>
|
<template #content>
|
||||||
<div class="p-text-center">
|
|
||||||
<ProgressSpinner />
|
<ProgressSpinner />
|
||||||
<h2>Waiting for connections. Please wait...</h2>
|
<h2>Waiting for connections. Please wait...</h2>
|
||||||
|
</template>
|
||||||
|
</Card>
|
||||||
|
<div v-show="state.active">
|
||||||
|
<Card >
|
||||||
|
<template #content>
|
||||||
|
<ProgressBar class="p-mb-2" :value="progress" :showValue="false" />
|
||||||
|
<video ref="player" controls="true"></video>
|
||||||
|
</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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue