mirror of https://github.com/VueTubeApp/VueTube
commit
da9df24812
|
@ -51,6 +51,8 @@
|
||||||
fab
|
fab
|
||||||
text
|
text
|
||||||
elevation="0"
|
elevation="0"
|
||||||
|
class="background--text"
|
||||||
|
:class="$vuetify.theme.dark ? 'text--lighten-4' : 'text--darken-4'"
|
||||||
style="width: 50px !important; height: 50px !important; z-index: 420"
|
style="width: 50px !important; height: 50px !important; z-index: 420"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-share-outline</v-icon>
|
<v-icon>mdi-share-outline</v-icon>
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<v-btn disabled fab text style="position: absolute; top: 0; right: 3.5rem">
|
||||||
|
<v-icon>mdi-closed-caption-outline</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<template>
|
||||||
|
<!-- TODO: change /home to $router.goBack() or $router.go(-1) -->
|
||||||
|
<v-btn
|
||||||
|
fab
|
||||||
|
text
|
||||||
|
style="position: absolute; top: 0; right: 0"
|
||||||
|
to="/home"
|
||||||
|
color="white"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-close</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
|
@ -1,116 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="controls" @click="toggleControls()">
|
|
||||||
<div ref="controlsWrap" class="controlsWrap">
|
|
||||||
<div class="centerVideoControls">
|
|
||||||
<v-btn
|
|
||||||
text
|
|
||||||
style="height: 5em; width: 5em"
|
|
||||||
color="white"
|
|
||||||
@click="togglePlaying()"
|
|
||||||
>
|
|
||||||
<v-icon
|
|
||||||
ref="pausePlayIndicator"
|
|
||||||
size="5rem"
|
|
||||||
v-text="playing ? 'mdi-pause' : 'mdi-play'"
|
|
||||||
/>
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bottomVideoControls white--text">
|
|
||||||
<div class="pl-4">
|
|
||||||
{{ watched }}
|
|
||||||
<span style="color: #999">
|
|
||||||
/ {{ $vuetube.humanTime(video.duration) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- <v-slider
|
|
||||||
id="slider"
|
|
||||||
v-model="video.currentTime"
|
|
||||||
:max="video.duration"
|
|
||||||
style="margin-bottom: -2rem !important"
|
|
||||||
/> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ["video"],
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
playing: true,
|
|
||||||
controls: true,
|
|
||||||
|
|
||||||
watched: 0,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
console.log("videovideovideo");
|
|
||||||
console.log(this.video);
|
|
||||||
this.video.ontimeupdate = () => {
|
|
||||||
console.log(this.video.currentTime);
|
|
||||||
this.watched = this.$vuetube.humanTime(this.video.currentTime);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
togglePlaying() {
|
|
||||||
if (this.video.paused) {
|
|
||||||
this.video.play();
|
|
||||||
this.playing = true;
|
|
||||||
} else {
|
|
||||||
this.video.pause();
|
|
||||||
this.playing = false;
|
|
||||||
}
|
|
||||||
this.toggleControls(); // Prevent Screen From Closing
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleControls() {
|
|
||||||
const setControls = this.controls ? "none" : "block";
|
|
||||||
this.$refs.controlsWrap.style.display = setControls;
|
|
||||||
this.controls = !this.controls;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
#slider * {
|
|
||||||
padding: 0 !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
|
||||||
.centerVideoControls {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottomVideoControls {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pausePlay {
|
|
||||||
min-height: 5em;
|
|
||||||
min-width: 5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controls {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlsWrap {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<template>
|
||||||
|
<v-btn
|
||||||
|
fab
|
||||||
|
text
|
||||||
|
color="white"
|
||||||
|
style="position: absolute; bottom: 0.25rem; right: 0"
|
||||||
|
@click.stop="$emit('fullscreen')"
|
||||||
|
>
|
||||||
|
<v-icon>{{ fullscreen ? "mdi-fullscreen-exit" : "mdi-fullscreen" }}</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
fullscreen: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ["fullscreen"],
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -1,58 +1,187 @@
|
||||||
<template>
|
<template>
|
||||||
<div style="position: relative">
|
<!-- TODO: down: () => minimize, -->
|
||||||
|
<div
|
||||||
|
ref="vidcontainer"
|
||||||
|
v-touch="{
|
||||||
|
up: () => (contain = false),
|
||||||
|
down: () => (contain = true),
|
||||||
|
}"
|
||||||
|
class="d-flex flex-column"
|
||||||
|
style="position: relative"
|
||||||
|
:style="{
|
||||||
|
borderRadius: $store.state.tweaks.roundWatch
|
||||||
|
? `${$store.state.tweaks.roundTweak / 3}rem`
|
||||||
|
: '0',
|
||||||
|
}"
|
||||||
|
@click="controls = !controls"
|
||||||
|
>
|
||||||
<video
|
<video
|
||||||
ref="player"
|
ref="player"
|
||||||
autoplay
|
autoplay
|
||||||
:src="vidSrc"
|
|
||||||
width="100%"
|
width="100%"
|
||||||
style="max-height: 50vh; display: block; overflow: hidden !important"
|
:src="vidSrc"
|
||||||
:style="{
|
style="transition: filter 0.15s ease-in-out; max-height: 100vh"
|
||||||
borderRadius: $store.state.tweaks.roundWatch
|
:class="controls ? 'dim' : ''"
|
||||||
? `${$store.state.tweaks.roundTweak / 4}rem`
|
:style="contain ? 'object-fit: contain;' : 'object-fit: cover;'"
|
||||||
: '0',
|
|
||||||
}"
|
|
||||||
@webkitfullscreenchange="handleFullscreenChange"
|
|
||||||
/>
|
/>
|
||||||
|
<!-- TODO: controls || seeking, take seeking out of <seekbar> component -->
|
||||||
|
|
||||||
<seekbar :video="$refs.player" v-if="$refs.player" />
|
<v-btn
|
||||||
<controls v-if="$refs.player" :video="$refs.player" />
|
v-if="controls"
|
||||||
|
text
|
||||||
|
tile
|
||||||
|
style="
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 50%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
"
|
||||||
|
@dbclick.stop="$refs.player.currentTime -= $refs.player.duration / 10"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-rewind</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
<!-- <v-slider v-model="value" step="0"></v-slider> -->
|
<v-btn
|
||||||
|
v-if="controls"
|
||||||
|
text
|
||||||
|
tile
|
||||||
|
style="
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
width: 50%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
"
|
||||||
|
@dbclick.stop="$refs.player.currentTime += $refs.player.duration / 10"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-fast-forward</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style="transition: opacity 0.15s ease-in-out"
|
||||||
|
:style="controls ? 'opacity: 1;' : 'opacity: 0; pointer-events: none'"
|
||||||
|
>
|
||||||
|
<minimize />
|
||||||
|
<loop />
|
||||||
|
<captions />
|
||||||
|
<close />
|
||||||
|
|
||||||
|
<playpause
|
||||||
|
v-if="$refs.player"
|
||||||
|
:video="$refs.player"
|
||||||
|
@close="controls = false"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<watchtime v-if="$refs.player" :video="$refs.player" />
|
||||||
|
<quality />
|
||||||
|
<speed />
|
||||||
|
<fullscreen
|
||||||
|
:fullscreen="isFullscreen"
|
||||||
|
@fullscreen="(controls = $refs.player.paused), handleFullscreenChange()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<!-- NOTE: breaks in fullscreen -->
|
||||||
|
<seekbar
|
||||||
|
v-if="$refs.player"
|
||||||
|
v-show="!isFullscreen || controls"
|
||||||
|
:fullscreen="isFullscreen"
|
||||||
|
:video="$refs.player"
|
||||||
|
:sources="sources"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import loop from "~/components/Player/loop.vue";
|
||||||
|
import close from "~/components/Player/close.vue";
|
||||||
|
import speed from "~/components/Player/speed.vue";
|
||||||
import seekbar from "~/components/Player/seekbar.vue";
|
import seekbar from "~/components/Player/seekbar.vue";
|
||||||
import controls from "~/components/Player/controls.vue";
|
import quality from "~/components/Player/quality.vue";
|
||||||
|
import minimize from "~/components/Player/minimize.vue";
|
||||||
|
import captions from "~/components/Player/captions.vue";
|
||||||
|
import playpause from "~/components/Player/playpause.vue";
|
||||||
|
import watchtime from "~/components/Player/watchtime.vue";
|
||||||
|
import fullscreen from "~/components/Player/fullscreen.vue";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
fullscreen,
|
||||||
|
watchtime,
|
||||||
|
playpause,
|
||||||
|
captions,
|
||||||
|
minimize,
|
||||||
|
quality,
|
||||||
seekbar,
|
seekbar,
|
||||||
controls,
|
speed,
|
||||||
|
close,
|
||||||
|
loop,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
sources: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
props: ["sources"],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
isFullscreen: false,
|
||||||
|
controls: false,
|
||||||
|
contain: true,
|
||||||
vidSrc: "",
|
vidSrc: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
console.log("sources", this.sources);
|
||||||
this.vidSrc = this.sources[this.sources.length - 1].url;
|
this.vidSrc = this.sources[this.sources.length - 1].url;
|
||||||
},
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.exitFullscreen();
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleFullscreenChange() {
|
handleFullscreenChange() {
|
||||||
if (document.fullscreenElement === this.$refs.player) {
|
if (document?.fullscreenElement === this.$refs.vidcontainer) {
|
||||||
this.$vuetube.statusBar.hide();
|
this.exitFullscreen();
|
||||||
this.$vuetube.navigationBar.hide();
|
|
||||||
} else {
|
} else {
|
||||||
this.$vuetube.statusBar.show();
|
this.openFullscreen();
|
||||||
this.$vuetube.navigationBar.show();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
exitFullscreen() {
|
||||||
|
const cancellFullScreen =
|
||||||
|
document.exitFullscreen ||
|
||||||
|
document.mozCancelFullScreen ||
|
||||||
|
document.webkitExitFullscreen ||
|
||||||
|
document.msExitFullscreen;
|
||||||
|
cancellFullScreen.call(document);
|
||||||
|
screen.orientation.lock("portrait");
|
||||||
|
screen.orientation.unlock();
|
||||||
|
this.$vuetube.navigationBar.show();
|
||||||
|
this.$vuetube.statusBar.show();
|
||||||
|
this.isFullscreen = false;
|
||||||
|
},
|
||||||
|
openFullscreen() {
|
||||||
|
const element = this.$refs.vidcontainer;
|
||||||
|
const requestFullScreen =
|
||||||
|
element.requestFullscreen ||
|
||||||
|
element.webkitRequestFullScreen ||
|
||||||
|
element.mozRequestFullScreen ||
|
||||||
|
element.msRequestFullScreen;
|
||||||
|
requestFullScreen.call(element);
|
||||||
|
screen.orientation.lock("landscape");
|
||||||
|
this.$vuetube.navigationBar.hide();
|
||||||
|
this.$vuetube.statusBar.hide();
|
||||||
|
this.isFullscreen = true;
|
||||||
|
},
|
||||||
getPlayer() {
|
getPlayer() {
|
||||||
return this.$refs.player;
|
return this.$refs.player;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.dim {
|
||||||
|
filter: brightness(50%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,272 +0,0 @@
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
class="d-flex flex-column"
|
|
||||||
style="position: relative"
|
|
||||||
:style="{
|
|
||||||
borderRadius: $store.state.tweaks.roundWatch
|
|
||||||
? `${$store.state.tweaks.roundTweak / 3}rem`
|
|
||||||
: '0',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<video
|
|
||||||
ref="player"
|
|
||||||
controls
|
|
||||||
autoplay
|
|
||||||
width="100%"
|
|
||||||
:src="vidSrc"
|
|
||||||
@webkitfullscreenchange="handleFullscreenChange"
|
|
||||||
/>
|
|
||||||
<video
|
|
||||||
ref="playerfake"
|
|
||||||
muted
|
|
||||||
autoplay
|
|
||||||
style="display: none"
|
|
||||||
:src="vidSrc"
|
|
||||||
/>
|
|
||||||
<v-progress-linear
|
|
||||||
query
|
|
||||||
active
|
|
||||||
style="width: 100%"
|
|
||||||
background-color="primary"
|
|
||||||
background-opacity="0.5"
|
|
||||||
:buffer-value="buffered"
|
|
||||||
:value="percent"
|
|
||||||
color="primary"
|
|
||||||
height="4"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- <v-btn
|
|
||||||
text
|
|
||||||
tile
|
|
||||||
class="debug"
|
|
||||||
style="position: absolute; top: 0; left: 0; width: 50%; height: 100%"
|
|
||||||
>
|
|
||||||
<v-icon>mdi-rewind</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
|
|
||||||
<v-btn
|
|
||||||
text
|
|
||||||
tile
|
|
||||||
class="debug"
|
|
||||||
style="position: absolute; top: 0; left: 50%; width: 50%; height: 100%"
|
|
||||||
>
|
|
||||||
<v-icon>mdi-fast-forward</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
|
|
||||||
<v-btn
|
|
||||||
text
|
|
||||||
class="debug"
|
|
||||||
style="position: absolute; top: 1rem; left: 1rem; border-radius: 2rem"
|
|
||||||
to="home"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<v-icon>mdi-chevron-down</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
|
|
||||||
<v-btn
|
|
||||||
text
|
|
||||||
class="debug"
|
|
||||||
style="position: absolute; top: 1rem; right: 1rem; border-radius: 2rem"
|
|
||||||
to="home"
|
|
||||||
>
|
|
||||||
<v-icon>mdi-close</v-icon>
|
|
||||||
</v-btn> -->
|
|
||||||
|
|
||||||
<!-- <v-btn
|
|
||||||
v-if="$refs.player"
|
|
||||||
fab
|
|
||||||
text
|
|
||||||
style="
|
|
||||||
height: 5rem;
|
|
||||||
width: 5rem;
|
|
||||||
position: absolute;
|
|
||||||
top: calc(50% - 2.5rem);
|
|
||||||
left: calc(50% - 2.5rem);
|
|
||||||
"
|
|
||||||
class="debug"
|
|
||||||
color="white"
|
|
||||||
@click="$refs.player.paused ? $refs.player.play() : $refs.player.pause()"
|
|
||||||
>
|
|
||||||
<v-icon
|
|
||||||
ref="pausePlayIndicator"
|
|
||||||
size="5rem"
|
|
||||||
v-text="$refs.player.paused ? 'mdi-play' : 'mdi-pause'"
|
|
||||||
/>
|
|
||||||
</v-btn> -->
|
|
||||||
|
|
||||||
<!-- <div
|
|
||||||
v-if="$vuetify"
|
|
||||||
class="debug px-4 rounded-xl"
|
|
||||||
style="position: absolute; bottom: 2rem; left: 1rem"
|
|
||||||
>
|
|
||||||
{{ watched }}
|
|
||||||
<span style="color: #999"> / {{ total }} </span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<v-btn
|
|
||||||
text
|
|
||||||
class="debug"
|
|
||||||
style="
|
|
||||||
position: absolute;
|
|
||||||
bottom: 2rem;
|
|
||||||
right: 1rem;
|
|
||||||
border-radius: 0 2rem 2rem 0;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
HD
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
text
|
|
||||||
class="debug"
|
|
||||||
style="
|
|
||||||
position: absolute;
|
|
||||||
bottom: 2rem;
|
|
||||||
right: 5rem;
|
|
||||||
border-radius: 2rem 0 0 2rem;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
1X
|
|
||||||
</v-btn> -->
|
|
||||||
<v-slider
|
|
||||||
v-if="$refs.player"
|
|
||||||
dense
|
|
||||||
height="4"
|
|
||||||
hide-details
|
|
||||||
style="
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 69420;
|
|
||||||
"
|
|
||||||
:value="progress"
|
|
||||||
:max="duration"
|
|
||||||
@start="scrubbing = true"
|
|
||||||
@end="scrubbing = false"
|
|
||||||
@input="seek($event)"
|
|
||||||
@change="scrub($event)"
|
|
||||||
>
|
|
||||||
<template #thumb-label="{ value }">
|
|
||||||
<canvas
|
|
||||||
ref="preview"
|
|
||||||
class="rounded-lg mb-8"
|
|
||||||
style="
|
|
||||||
border: 4px solid var(--v-primary-base);
|
|
||||||
margin-top: -20px !important;
|
|
||||||
top: -20px !important;
|
|
||||||
"
|
|
||||||
:width="$refs.player.clientWidth / 4"
|
|
||||||
:height="$refs.player.clientHeight / 4"
|
|
||||||
></canvas>
|
|
||||||
</template>
|
|
||||||
</v-slider>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ["vidSrc"],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
scrubbing: false,
|
|
||||||
percent: 0,
|
|
||||||
progress: 0,
|
|
||||||
buffered: 0,
|
|
||||||
duration: 0,
|
|
||||||
watched: 0,
|
|
||||||
total: 0,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
let vid = this.$refs.player;
|
|
||||||
vid.addEventListener("loadeddata", (e) => {
|
|
||||||
console.log("%c loadeddata", "color: #00ff00");
|
|
||||||
console.log(e);
|
|
||||||
//Video should now be loaded but we can add a second check
|
|
||||||
if (vid.readyState >= 3) {
|
|
||||||
vid.ontimeupdate = () => {
|
|
||||||
console.log("%c timeupdate", "color: #aaaaff");
|
|
||||||
this.duration = vid.duration;
|
|
||||||
if (!this.scrubbing) this.progress = vid.currentTime;
|
|
||||||
this.percent = (vid.currentTime / vid.duration) * 100;
|
|
||||||
this.watched = this.$vuetube.humanTime(vid.currentTime);
|
|
||||||
this.total = this.$vuetube.humanTime(vid.duration);
|
|
||||||
};
|
|
||||||
vid.onprogress = () => {
|
|
||||||
console.log("%c progress", "color: #ff00ff");
|
|
||||||
this.buffered = (vid.buffered.end(0) / vid.duration) * 100;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// TODO: screenshot-based faster dynamic thumbnail preview
|
|
||||||
// var video = document.getElementById("thumb");
|
|
||||||
// video.addEventListener("loadedmetadata", initScreenshot);
|
|
||||||
// video.addEventListener("playing", startScreenshot);
|
|
||||||
// video.addEventListener("pause", stopScreenshot);
|
|
||||||
// video.addEventListener("ended", stopScreenshot);
|
|
||||||
|
|
||||||
// var canvas = document.getElementById("canvas");
|
|
||||||
// var ctx = canvas.getContext("2d");
|
|
||||||
// var ssContainer = document.getElementById("screenShots");
|
|
||||||
// var videoHeight, videoWidth;
|
|
||||||
// var drawTimer = null;
|
|
||||||
|
|
||||||
// function initScreenshot() {
|
|
||||||
// videoHeight = video.videoHeight;
|
|
||||||
// videoWidth = video.videoWidth;
|
|
||||||
// canvas.width = videoWidth;
|
|
||||||
// canvas.height = videoHeight;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function startScreenshot() {
|
|
||||||
// if (drawTimer == null) {
|
|
||||||
// drawTimer = setInterval(grabScreenshot, 1000);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function stopScreenshot() {
|
|
||||||
// if (drawTimer) {
|
|
||||||
// clearInterval(drawTimer);
|
|
||||||
// drawTimer = null;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function grabScreenshot() {
|
|
||||||
// ctx.drawImage(video, 0, 0, videoWidth, videoHeight);
|
|
||||||
// var img = new Image();
|
|
||||||
// img.src = canvas.toDataURL("image/png");
|
|
||||||
// img.width = 120;
|
|
||||||
// ssContainer.appendChild(img);
|
|
||||||
// }
|
|
||||||
methods: {
|
|
||||||
seek(e) {
|
|
||||||
console.log(`scrubbing ${e}`);
|
|
||||||
let vid = this.$refs.playerfake;
|
|
||||||
let canvas = this.$refs.preview;
|
|
||||||
this.$refs.playerfake.currentTime = e;
|
|
||||||
canvas
|
|
||||||
.getContext("2d")
|
|
||||||
.drawImage(
|
|
||||||
vid,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
this.$refs.player.clientWidth / 4,
|
|
||||||
this.$refs.player.clientHeight / 4
|
|
||||||
);
|
|
||||||
},
|
|
||||||
scrub(e) {
|
|
||||||
this.$refs.player.currentTime = e;
|
|
||||||
},
|
|
||||||
handleFullscreenChange() {
|
|
||||||
if (document.fullscreenElement === this.$refs.player) {
|
|
||||||
this.$vuetube.statusBar.hide();
|
|
||||||
this.$vuetube.navigationBar.hide();
|
|
||||||
} else {
|
|
||||||
this.$vuetube.statusBar.show();
|
|
||||||
this.$vuetube.navigationBar.show();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<v-btn fab text disabled style="position: absolute; top: 0; right: 7rem">
|
||||||
|
<v-icon>mdi-sync</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<v-btn fab text disabled style="position: absolute; top: 0; left: 0">
|
||||||
|
<v-icon>mdi-chevron-down</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
|
@ -0,0 +1,32 @@
|
||||||
|
<template>
|
||||||
|
<v-btn
|
||||||
|
fab
|
||||||
|
text
|
||||||
|
style="
|
||||||
|
width: 5rem;
|
||||||
|
height: 5rem;
|
||||||
|
position: absolute;
|
||||||
|
top: calc(50% - 2.5rem);
|
||||||
|
left: calc(50% - 2.5rem);
|
||||||
|
"
|
||||||
|
color="white"
|
||||||
|
@click.stop="
|
||||||
|
(paused = !video.paused),
|
||||||
|
video.paused ? (video.play(), $emit('close')) : video.pause()
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<v-icon size="5rem">
|
||||||
|
{{ paused ? "mdi-play" : "mdi-pause" }}
|
||||||
|
</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ["video"],
|
||||||
|
emits: ["close"],
|
||||||
|
data: () => ({
|
||||||
|
paused: false,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<template>
|
||||||
|
<v-btn
|
||||||
|
fab
|
||||||
|
text
|
||||||
|
disabled
|
||||||
|
style="position: absolute; bottom: 0.25rem; right: 3.5rem"
|
||||||
|
>
|
||||||
|
HD
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
|
@ -1,34 +1,243 @@
|
||||||
<template>
|
<template>
|
||||||
<v-progress-linear
|
<div>
|
||||||
active
|
<video
|
||||||
background-color="primary"
|
ref="playerfake"
|
||||||
background-opacity="0.5"
|
muted
|
||||||
:buffer-value="buffered"
|
autoplay
|
||||||
color="primary"
|
style="display: none"
|
||||||
height="3"
|
:src="vidWrs"
|
||||||
query
|
/>
|
||||||
:value="percentage"
|
<v-progress-linear
|
||||||
/>
|
query
|
||||||
|
active
|
||||||
|
style="width: 100%"
|
||||||
|
background-opacity="0.5"
|
||||||
|
background-color="primary"
|
||||||
|
:buffer-value="buffered"
|
||||||
|
:value="percent"
|
||||||
|
color="primary"
|
||||||
|
height="2"
|
||||||
|
/>
|
||||||
|
<!-- Scrubber -->
|
||||||
|
<v-slider
|
||||||
|
hide-details
|
||||||
|
height="2"
|
||||||
|
dense
|
||||||
|
style="position: absolute; z-index: 69420"
|
||||||
|
:style="
|
||||||
|
fullscreen
|
||||||
|
? 'width: calc(100% - 2rem); left: 1rem; bottom: 4rem;'
|
||||||
|
: 'width: 100%; left: 0; bottom: 0;'
|
||||||
|
"
|
||||||
|
:thumb-size="0"
|
||||||
|
:max="duration"
|
||||||
|
:value="progress"
|
||||||
|
@start="scrubbing = true"
|
||||||
|
@end="scrubbing = false"
|
||||||
|
@change="scrub($event)"
|
||||||
|
@input="scrubbing ? seek($event) : null"
|
||||||
|
>
|
||||||
|
<template #thumb-label="{ value }">
|
||||||
|
<div style="transform: translateY(-50%)">
|
||||||
|
<canvas
|
||||||
|
ref="preview"
|
||||||
|
class="white"
|
||||||
|
:width="video.clientWidth / 3"
|
||||||
|
:height="video.clientHeight / 3"
|
||||||
|
style="border: 2px solid white"
|
||||||
|
:style="{
|
||||||
|
borderRadius: $store.state.tweaks.roundWatch
|
||||||
|
? `${$store.state.tweaks.roundTweak / 3}rem`
|
||||||
|
: '0',
|
||||||
|
}"
|
||||||
|
></canvas>
|
||||||
|
<div class="text-center pb-4" style="font-size: 0.8rem">
|
||||||
|
<b>{{ $vuetube.humanTime(value) }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</v-slider>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: ["video"],
|
props: ["sources", "video", "fullscreen"],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
percentage: 0,
|
scrubbing: false,
|
||||||
|
percent: 0,
|
||||||
|
progress: 0,
|
||||||
buffered: 0,
|
buffered: 0,
|
||||||
|
duration: 0,
|
||||||
|
vidSrc: "",
|
||||||
|
vidWrs: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.video.ontimeupdate = () => {
|
console.log("sources", this.sources);
|
||||||
this.percentage = (this.video.currentTime / this.video.duration) * 100;
|
this.vidSrc = this.sources[this.sources.length - 1].url;
|
||||||
};
|
this.vidWrs = this.sources[1].url;
|
||||||
this.video.addEventListener("progress", () => {
|
let vid = this.video;
|
||||||
this.buffered = (this.video.buffered.end(0) / this.video.duration) * 100;
|
vid.addEventListener("loadeddata", (e) => {
|
||||||
|
// console.log("%c loadeddata", "color: #00ff00");
|
||||||
|
console.log(e);
|
||||||
|
//Video should now be loaded but we can add a second check
|
||||||
|
if (vid.readyState >= 3) {
|
||||||
|
vid.ontimeupdate = () => {
|
||||||
|
// console.log("%c timeupdate", "color: #aaaaff");
|
||||||
|
this.duration = vid.duration;
|
||||||
|
if (!this.scrubbing) this.progress = vid.currentTime;
|
||||||
|
this.percent = (vid.currentTime / vid.duration) * 100;
|
||||||
|
};
|
||||||
|
vid.onprogress = () => {
|
||||||
|
// console.log("%c progress", "color: #ff00ff");
|
||||||
|
this.buffered = (vid.buffered.end(0) / vid.duration) * 100;
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
// TODO: better scrubbing preview
|
||||||
|
loadVideoFrames() {
|
||||||
|
// Exit loop if desired number of frames have been extracted
|
||||||
|
if (this.frames.length >= frameCount) {
|
||||||
|
this.visibleFrame = 0;
|
||||||
|
|
||||||
|
// Append all canvases to container div
|
||||||
|
this.frames.forEach((frame) => {
|
||||||
|
this.frameContainerElement.appendChild(frame);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If extraction hasn’t started, set desired time for first frame
|
||||||
|
if (this.frames.length === 0) {
|
||||||
|
this.requestedTime = 0;
|
||||||
|
} else {
|
||||||
|
this.requestedTime = this.requestedTime + this.frameTimestep;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send seek request to video player for the next frame.
|
||||||
|
this.videoElement.currentTime = this.requestedTime;
|
||||||
|
},
|
||||||
|
extractFrame(videoWidth, videoHeight) {
|
||||||
|
// Create DOM canvas object
|
||||||
|
var canvas = document.createElement("canvas");
|
||||||
|
canvas.className = "video-scrubber-frame";
|
||||||
|
canvas.height = videoHeight;
|
||||||
|
canvas.width = videoWidth;
|
||||||
|
|
||||||
|
// Copy current frame to canvas
|
||||||
|
var context = canvas.getContext("2d");
|
||||||
|
context.drawImage(this.videoElement, 0, 0, videoWidth, videoHeight);
|
||||||
|
this.frames.push(canvas);
|
||||||
|
|
||||||
|
// Load the next frame
|
||||||
|
loadVideoFrames();
|
||||||
|
},
|
||||||
|
prefetch_file(url, fetched_callback, progress_callback, error_callback) {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", url, true);
|
||||||
|
xhr.responseType = "blob";
|
||||||
|
|
||||||
|
xhr.addEventListener(
|
||||||
|
"load",
|
||||||
|
function () {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
var URL = window.URL || window.webkitURL;
|
||||||
|
var blob_url = URL.createObjectURL(xhr.response);
|
||||||
|
fetched_callback(blob_url);
|
||||||
|
} else {
|
||||||
|
error_callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
var prev_pc = 0;
|
||||||
|
xhr.addEventListener("progress", function (event) {
|
||||||
|
if (event.lengthComputable) {
|
||||||
|
var pc = Math.round((event.loaded / event.total) * 100);
|
||||||
|
if (pc != prev_pc) {
|
||||||
|
prev_pc = pc;
|
||||||
|
progress_callback(pc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
xhr.send();
|
||||||
|
},
|
||||||
|
async extractFramesFromVideo(videoUrl, fps = 25) {
|
||||||
|
// fully download it first (no buffering):
|
||||||
|
console.log(videoUrl);
|
||||||
|
console.log(fps);
|
||||||
|
let videoBlob = await fetch(videoUrl, {
|
||||||
|
headers: { range: "bytes=0-567139" },
|
||||||
|
}).then((r) => r.blob());
|
||||||
|
console.log(videoBlob);
|
||||||
|
let videoObjectUrl = URL.createObjectURL(videoBlob);
|
||||||
|
let video = document.createElement("video");
|
||||||
|
|
||||||
|
let seekResolve;
|
||||||
|
video.addEventListener("seeked", async function () {
|
||||||
|
if (seekResolve) seekResolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
video.src = videoObjectUrl;
|
||||||
|
|
||||||
|
// workaround chromium metadata bug (https://stackoverflow.com/q/38062864/993683)
|
||||||
|
while (
|
||||||
|
(video.duration === Infinity || isNaN(video.duration)) &&
|
||||||
|
video.readyState < 2
|
||||||
|
) {
|
||||||
|
await new Promise((r) => setTimeout(r, 1000));
|
||||||
|
video.currentTime = 10000000 * Math.random();
|
||||||
|
}
|
||||||
|
let duration = video.duration;
|
||||||
|
|
||||||
|
let canvas = document.createElement("canvas");
|
||||||
|
let context = canvas.getContext("2d");
|
||||||
|
let [w, h] = [video.videoWidth, video.videoHeight];
|
||||||
|
canvas.width = w;
|
||||||
|
canvas.height = h;
|
||||||
|
|
||||||
|
let interval = 1;
|
||||||
|
let currentTime = 0;
|
||||||
|
|
||||||
|
while (currentTime < duration) {
|
||||||
|
video.currentTime = currentTime;
|
||||||
|
await new Promise((r) => (seekResolve = r));
|
||||||
|
|
||||||
|
context.drawImage(video, 0, 0, w, h);
|
||||||
|
let base64ImageData = canvas.toDataURL();
|
||||||
|
console.log(base64ImageData);
|
||||||
|
this.frames.push(base64ImageData);
|
||||||
|
|
||||||
|
currentTime += interval;
|
||||||
|
}
|
||||||
|
console.log("%c frames", "color: #00ff00");
|
||||||
|
console.log(this.frames);
|
||||||
|
},
|
||||||
|
// TODO: scrubbing preview end
|
||||||
|
seek(e) {
|
||||||
|
// console.log(`scrubbing ${e}`);
|
||||||
|
let vid = this.$refs.playerfake;
|
||||||
|
let canvas = this.$refs.preview;
|
||||||
|
this.$refs.playerfake.currentTime = e;
|
||||||
|
canvas
|
||||||
|
.getContext("2d")
|
||||||
|
.drawImage(
|
||||||
|
vid,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
this.video.clientWidth / 3,
|
||||||
|
this.video.clientHeight / 3
|
||||||
|
);
|
||||||
|
},
|
||||||
|
scrub(e) {
|
||||||
|
this.video.currentTime = e;
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<template>
|
||||||
|
<v-btn
|
||||||
|
fab
|
||||||
|
text
|
||||||
|
disabled
|
||||||
|
style="position: absolute; bottom: 0.25rem; right: 7rem"
|
||||||
|
>
|
||||||
|
1X
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
|
@ -0,0 +1,32 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
color: #fff;
|
||||||
|
left: 1.25rem;
|
||||||
|
bottom: 1.25rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
position: absolute;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ watched }}
|
||||||
|
<span style="color: #999"> / {{ duration }} </span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ["video"],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
watched: 0,
|
||||||
|
duration: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.video.addEventListener("timeupdate", () => {
|
||||||
|
this.duration = this.$vuetube.humanTime(this.video.duration);
|
||||||
|
this.watched = this.$vuetube.humanTime(this.video.currentTime);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -25,19 +25,19 @@
|
||||||
:class="$vuetify.theme.dark ? 'lighten-3' : 'darken-3'"
|
:class="$vuetify.theme.dark ? 'lighten-3' : 'darken-3'"
|
||||||
:style="{
|
:style="{
|
||||||
borderRadius: $store.state.tweaks.roundThumb
|
borderRadius: $store.state.tweaks.roundThumb
|
||||||
? `${$store.state.tweaks.roundTweak / 4}rem`
|
? `${$store.state.tweaks.roundTweak / 2}rem`
|
||||||
: '0',
|
: '0',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="background d-flex flex-column justify-center align-center"
|
class="d-flex flex-column justify-center align-center"
|
||||||
style="
|
style="
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
width: 40%;
|
width: 50%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
opacity: 0.5;
|
background: linear-gradient(var(--v-background-base) -1000%, #00000000 1000%);
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div>420</div>
|
<div>420</div>
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
class="flex-grow-1"
|
class="flex-grow-1"
|
||||||
style="width: 2rem !important"
|
style="width: 2rem !important"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-playlist-plus</v-icon>
|
<v-icon>mdi-share-outline</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
text
|
text
|
||||||
|
@ -73,7 +73,7 @@
|
||||||
class="flex-grow-1"
|
class="flex-grow-1"
|
||||||
style="width: 2rem !important"
|
style="width: 2rem !important"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-share-outline</v-icon>
|
<v-icon>mdi-playlist-plus</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<v-card style="display: flex" class="rounded-0 pa-3 topNav background">
|
<v-card style="display: flex" class="rounded-0 pa-3 topNav background">
|
||||||
|
<!-- opacity with vue 😉 -->
|
||||||
|
<!-- :style="{ background: $vuetify.theme.currentTheme.primary + '55' }" -->
|
||||||
<h3
|
<h3
|
||||||
v-show="!search"
|
v-show="!search"
|
||||||
class="my-auto ml-4"
|
class="my-auto ml-4"
|
||||||
|
@ -112,7 +114,9 @@ export default {
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.topNav {
|
.topNav {
|
||||||
/* box-shadow: inset 0 1rem 10rem var(--v-background-base) !important; */
|
/* opacity with hex, wow 😉 */
|
||||||
|
/* background: linear-gradient(var(--v-background-base) -1000%, #00000000 1000%); */
|
||||||
|
/* box-shadow: inset 0 0 5rem var(--v-background-base) !important; */
|
||||||
height: calc(4rem + env(safe-area-inset-top)) !important;
|
height: calc(4rem + env(safe-area-inset-top)) !important;
|
||||||
padding-top: env(safe-area-inset-top) !important;
|
padding-top: env(safe-area-inset-top) !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
|
|
|
@ -1,20 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="background" id="watch-body">
|
<div class="background" id="watch-body">
|
||||||
<div id="player-container">
|
<div id="player-container">
|
||||||
<!-- VueTube Player V1 -->
|
<!-- TODO: move component to default.vue -->
|
||||||
<vuetubePlayer
|
<!-- TODO: pass sources through vuex instead of props -->
|
||||||
v-if="useBetaPlayer === 'true' && sources.length > 0"
|
<player v-if="sources.length > 0" ref="player" :sources="sources" />
|
||||||
:sources="sources"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Stock Player -->
|
|
||||||
<legacyPlayer
|
|
||||||
v-if="useBetaPlayer !== 'true'"
|
|
||||||
id="player"
|
|
||||||
ref="player"
|
|
||||||
v-touch="{ down: () => $router.push('/home') }"
|
|
||||||
:vid-src="vidSrc"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -57,12 +46,13 @@
|
||||||
<v-icon class="ml-4" v-if="showMore">mdi-chevron-up</v-icon>
|
<v-icon class="ml-4" v-if="showMore">mdi-chevron-up</v-icon>
|
||||||
<v-icon class="ml-4" v-else>mdi-chevron-down</v-icon>
|
<v-icon class="ml-4" v-else>mdi-chevron-down</v-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex pl-2">
|
<div class="d-flex pl-6">
|
||||||
<v-btn
|
<v-btn
|
||||||
v-for="(item, index) in interactions"
|
v-for="(item, index) in interactions"
|
||||||
:key="index"
|
:key="index"
|
||||||
text
|
text
|
||||||
fab
|
fab
|
||||||
|
no-caps
|
||||||
class="vertical-button mx-1"
|
class="vertical-button mx-1"
|
||||||
elevation="0"
|
elevation="0"
|
||||||
style="width: 4.2rem !important; height: 4.2rem !important"
|
style="width: 4.2rem !important; height: 4.2rem !important"
|
||||||
|
@ -262,29 +252,27 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import player from "~/components/Player/index.vue";
|
||||||
import { Share } from "@capacitor/share";
|
import { Share } from "@capacitor/share";
|
||||||
import VidLoadRenderer from "~/components/vidLoadRenderer.vue";
|
|
||||||
import { getCpn } from "~/plugins/utils";
|
import { getCpn } from "~/plugins/utils";
|
||||||
import SlimVideoDescriptionRenderer from "~/components/UtilRenderers/slimVideoDescriptionRenderer.vue";
|
|
||||||
import ItemSectionRenderer from "~/components/SectionRenderers/itemSectionRenderer.vue";
|
|
||||||
import legacyPlayer from "~/components/Player/legacy.vue";
|
|
||||||
import vuetubePlayer from "~/components/Player/index.vue";
|
|
||||||
import ShelfRenderer from "~/components/SectionRenderers/shelfRenderer.vue";
|
import ShelfRenderer from "~/components/SectionRenderers/shelfRenderer.vue";
|
||||||
|
import VidLoadRenderer from "~/components/vidLoadRenderer.vue";
|
||||||
|
import ItemSectionRenderer from "~/components/SectionRenderers/itemSectionRenderer.vue";
|
||||||
import mainCommentRenderer from "~/components/Comments/mainCommentRenderer.vue";
|
import mainCommentRenderer from "~/components/Comments/mainCommentRenderer.vue";
|
||||||
import SwipeableBottomSheet from "~/components/ExtendedComponents/swipeableBottomSheet";
|
import SwipeableBottomSheet from "~/components/ExtendedComponents/swipeableBottomSheet";
|
||||||
|
import SlimVideoDescriptionRenderer from "~/components/UtilRenderers/slimVideoDescriptionRenderer.vue";
|
||||||
|
|
||||||
import backType from "~/plugins/classes/backType";
|
import backType from "~/plugins/classes/backType";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
player,
|
||||||
ShelfRenderer,
|
ShelfRenderer,
|
||||||
VidLoadRenderer,
|
VidLoadRenderer,
|
||||||
SlimVideoDescriptionRenderer,
|
|
||||||
vuetubePlayer,
|
|
||||||
legacyPlayer,
|
|
||||||
ItemSectionRenderer,
|
ItemSectionRenderer,
|
||||||
SwipeableBottomSheet,
|
|
||||||
mainCommentRenderer,
|
mainCommentRenderer,
|
||||||
|
SwipeableBottomSheet,
|
||||||
|
SlimVideoDescriptionRenderer,
|
||||||
},
|
},
|
||||||
layout: "empty",
|
layout: "empty",
|
||||||
// transition(to) { // TODO: fix layout switching
|
// transition(to) { // TODO: fix layout switching
|
||||||
|
@ -304,7 +292,6 @@ export default {
|
||||||
// Exit fullscreen if currently in fullscreen
|
// Exit fullscreen if currently in fullscreen
|
||||||
// if (this.$refs.player) this.$refs.player.webkitExitFullscreen();
|
// if (this.$refs.player) this.$refs.player.webkitExitFullscreen();
|
||||||
// Reset player and run getVideo function again
|
// Reset player and run getVideo function again
|
||||||
// this.vidSrc = "";
|
|
||||||
// this.startTime = Math.floor(Date.now() / 1000);
|
// this.startTime = Math.floor(Date.now() / 1000);
|
||||||
// this.getVideo();
|
// this.getVideo();
|
||||||
clearInterval(this.interval);
|
clearInterval(this.interval);
|
||||||
|
@ -329,18 +316,9 @@ export default {
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
|
|
||||||
this.$youtube.getVid(this.$route.query.v).then((result) => {
|
this.$youtube.getVid(this.$route.query.v).then((result) => {
|
||||||
this.video = result;
|
|
||||||
console.log("Video info data", result);
|
|
||||||
console.log(result.availableResolutions);
|
|
||||||
|
|
||||||
//--- VueTube Player v1 ---//
|
|
||||||
this.sources = result.availableResolutions;
|
this.sources = result.availableResolutions;
|
||||||
|
console.log("Video info data", result);
|
||||||
//--- Legacy Player ---//
|
this.video = result;
|
||||||
this.vidSrc =
|
|
||||||
result.availableResolutions[
|
|
||||||
result.availableResolutions.length - 1
|
|
||||||
].url; // Takes the highest available resolution with both video and Audio. Note this will be lower than the actual highest resolution
|
|
||||||
|
|
||||||
//--- Content Stuff ---//
|
//--- Content Stuff ---//
|
||||||
this.likes = result.metadata.likes.toLocaleString();
|
this.likes = result.metadata.likes.toLocaleString();
|
||||||
|
@ -463,13 +441,11 @@ export default {
|
||||||
showMore: false,
|
showMore: false,
|
||||||
showComments: false,
|
showComments: false,
|
||||||
// share: false,
|
// share: false,
|
||||||
vidSrc: null,
|
|
||||||
sources: [],
|
sources: [],
|
||||||
recommends: null,
|
recommends: null,
|
||||||
loaded: false,
|
loaded: false,
|
||||||
interval: null,
|
interval: null,
|
||||||
video: null,
|
video: null,
|
||||||
useBetaPlayer: false,
|
|
||||||
backHierarchy: [],
|
backHierarchy: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -477,7 +453,6 @@ export default {
|
||||||
mountedInit() {
|
mountedInit() {
|
||||||
this.startTime = Math.floor(Date.now() / 1000);
|
this.startTime = Math.floor(Date.now() / 1000);
|
||||||
this.getVideo();
|
this.getVideo();
|
||||||
this.useBetaPlayer = localStorage.getItem("debug.BetaPlayer");
|
|
||||||
|
|
||||||
// Reset vertical scrolling
|
// Reset vertical scrolling
|
||||||
const scrollableList = document.querySelectorAll(".overflow-y-auto");
|
const scrollableList = document.querySelectorAll(".overflow-y-auto");
|
||||||
|
|
|
@ -143,12 +143,18 @@ const module = {
|
||||||
|
|
||||||
let returntext = new String();
|
let returntext = new String();
|
||||||
for (const i in levels) {
|
for (const i in levels) {
|
||||||
const num = levels[i].toString().length == 1 ? "0" + levels[i] : levels[i]; // If Number Is Single Digit, Add 0 In Front
|
const num =
|
||||||
|
levels[i].toString().length == 1 ? "0" + levels[i] : levels[i]; // If Number Is Single Digit, Add 0 In Front
|
||||||
returntext += ":" + num;
|
returntext += ":" + num;
|
||||||
}
|
}
|
||||||
while (returntext.startsWith(":00")) { returntext = returntext.substring(3); } // Remove Prepending 0s (eg. 00:00:00:01:00)
|
while (returntext.startsWith(":00")) {
|
||||||
if (returntext.startsWith(":0")) { returntext = returntext.substring(2); } else { returntext = returntext.substring(1); } // Prevent Time Starting With 0 (eg. 01:00)
|
returntext = returntext.substring(3);
|
||||||
|
} // Remove Prepending 0s (eg. 00:00:00:01:00)
|
||||||
|
if (returntext.startsWith(":0")) {
|
||||||
|
returntext = returntext.substring(2);
|
||||||
|
} else {
|
||||||
|
returntext = returntext.substring(1);
|
||||||
|
} // Prevent Time Starting With 0 (eg. 01:00)
|
||||||
// console.log("Human Time:", returntext);
|
// console.log("Human Time:", returntext);
|
||||||
return returntext;
|
return returntext;
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const mutations = {
|
||||||
// NOTE: localStorage is not reactive, so it will only be used on first load
|
// NOTE: localStorage is not reactive, so it will only be used on first load
|
||||||
// currently called on mounted() in pages/index.vue
|
// currently called on mounted() in pages/index.vue
|
||||||
if (process.client) {
|
if (process.client) {
|
||||||
state.roundTweak = localStorage.getItem("roundTweak") || 0;
|
state.roundTweak = JSON.parse(localStorage.getItem("roundTweak")) || 0;
|
||||||
state.roundThumb =
|
state.roundThumb =
|
||||||
JSON.parse(localStorage.getItem("roundThumb")) === true;
|
JSON.parse(localStorage.getItem("roundThumb")) === true;
|
||||||
state.roundWatch =
|
state.roundWatch =
|
||||||
|
|
|
@ -2,5 +2,9 @@
|
||||||
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||||
<access origin="*" />
|
<access origin="*" />
|
||||||
|
|
||||||
|
<feature name="CDVOrientation">
|
||||||
|
<param name="android-package" value="cordova.plugins.screenorientation.CDVOrientation"/>
|
||||||
|
</feature>
|
||||||
|
|
||||||
|
|
||||||
</widget>
|
</widget>
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cordova.plugins.screenorientation;
|
||||||
|
|
||||||
|
import org.apache.cordova.CallbackContext;
|
||||||
|
import org.apache.cordova.CordovaPlugin;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class CDVOrientation extends CordovaPlugin {
|
||||||
|
|
||||||
|
private static final String TAG = "YoikScreenOrientation";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Screen Orientation Constants
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static final String ANY = "any";
|
||||||
|
private static final String PORTRAIT_PRIMARY = "portrait-primary";
|
||||||
|
private static final String PORTRAIT_SECONDARY = "portrait-secondary";
|
||||||
|
private static final String LANDSCAPE_PRIMARY = "landscape-primary";
|
||||||
|
private static final String LANDSCAPE_SECONDARY = "landscape-secondary";
|
||||||
|
private static final String PORTRAIT = "portrait";
|
||||||
|
private static final String LANDSCAPE = "landscape";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||||
|
|
||||||
|
Log.d(TAG, "execute action: " + action);
|
||||||
|
|
||||||
|
// Route the Action
|
||||||
|
if (action.equals("screenOrientation")) {
|
||||||
|
return routeScreenOrientation(args, callbackContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action not found
|
||||||
|
callbackContext.error("action not recognised");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean routeScreenOrientation(JSONArray args, CallbackContext callbackContext) {
|
||||||
|
|
||||||
|
String action = args.optString(0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String orientation = args.optString(1);
|
||||||
|
|
||||||
|
Log.d(TAG, "Requested ScreenOrientation: " + orientation);
|
||||||
|
|
||||||
|
Activity activity = cordova.getActivity();
|
||||||
|
|
||||||
|
if (orientation.equals(ANY)) {
|
||||||
|
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||||
|
} else if (orientation.equals(LANDSCAPE_PRIMARY)) {
|
||||||
|
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||||
|
} else if (orientation.equals(PORTRAIT_PRIMARY)) {
|
||||||
|
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||||
|
} else if (orientation.equals(LANDSCAPE)) {
|
||||||
|
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
||||||
|
} else if (orientation.equals(PORTRAIT)) {
|
||||||
|
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
|
||||||
|
} else if (orientation.equals(LANDSCAPE_SECONDARY)) {
|
||||||
|
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
|
||||||
|
} else if (orientation.equals(PORTRAIT_SECONDARY)) {
|
||||||
|
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackContext.success();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,5 +2,9 @@
|
||||||
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||||
<access origin="*" />
|
<access origin="*" />
|
||||||
|
|
||||||
|
<feature name="CDVOrientation">
|
||||||
|
<param name="ios-package" value="CDVOrientation"/>
|
||||||
|
</feature>
|
||||||
|
|
||||||
|
|
||||||
</widget>
|
</widget>
|
|
@ -20,6 +20,7 @@ def capacitor_pods
|
||||||
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
|
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
|
||||||
pod 'CapacitorToast', :path => '../../node_modules/@capacitor/toast'
|
pod 'CapacitorToast', :path => '../../node_modules/@capacitor/toast'
|
||||||
pod 'HugotomaziCapacitorNavigationBar', :path => '../../node_modules/@hugotomazi/capacitor-navigation-bar'
|
pod 'HugotomaziCapacitorNavigationBar', :path => '../../node_modules/@hugotomazi/capacitor-navigation-bar'
|
||||||
|
pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'
|
||||||
end
|
end
|
||||||
|
|
||||||
target 'App' do
|
target 'App' do
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
"@capacitor/status-bar": "^1.0.8",
|
"@capacitor/status-bar": "^1.0.8",
|
||||||
"@capacitor/toast": "^1.0.8",
|
"@capacitor/toast": "^1.0.8",
|
||||||
"@hugotomazi/capacitor-navigation-bar": "^1.1.1",
|
"@hugotomazi/capacitor-navigation-bar": "^1.1.1",
|
||||||
|
"cordova-plugin-screen-orientation": "^3.0.2",
|
||||||
|
"es6-promise-plugin": "^4.2.2",
|
||||||
"iconv-lite": "^0.6.3"
|
"iconv-lite": "^0.6.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue