background opacity, better scrubber preview thumbnail styling, mor copypasta

This commit is contained in:
Nikita Krupin 2022-05-23 16:03:03 -04:00
parent 54e516c2b5
commit 4fb7e764a6
5 changed files with 164 additions and 69 deletions

View File

@ -51,6 +51,8 @@
fab
text
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"
>
<v-icon>mdi-share-outline</v-icon>

View File

@ -14,6 +14,7 @@
autoplay
width="100%"
:src="vidSrc"
style="filter: brightness(50%)"
@webkitfullscreenchange="handleFullscreenChange"
/>
<video
@ -27,12 +28,12 @@
query
active
style="width: 100%"
background-color="primary"
background-opacity="0.5"
background-color="primary"
:buffer-value="buffered"
:value="percent"
color="primary"
height="4"
height="2"
/>
<!-- <v-btn
@ -129,35 +130,44 @@
</v-btn> -->
<v-slider
v-if="$refs.player"
dense
height="4"
hide-details
height="2"
dense
style="
position: absolute;
z-index: 69420;
width: 100%;
bottom: 0;
left: 0;
width: 100%;
z-index: 69420;
"
:value="progress"
:thumb-size="0"
:max="duration"
:value="progress"
@start="scrubbing = true"
@end="scrubbing = false"
@input="seek($event)"
@change="scrub($event)"
@input="seek($event)"
>
<!-- :thumb-size="$refs.player.clientHeight / 3" -->
<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>
<div style="transform: translateY(-50%)">
<canvas
ref="preview"
class="background"
:class="$vuetify.theme.dark ? 'lighten-4' : 'darken-4'"
:width="$refs.player.clientWidth / 3"
:height="$refs.player.clientHeight / 3"
style="border: 2px solid"
:style="{
borderRadius: $store.state.tweaks.roundWatch
? `${$store.state.tweaks.roundTweak / 3}rem`
: '0',
}"
></canvas>
<div class="text-center pb-5" style="font-size: 0.8rem">
<b>{{ $vuetube.humanTime(value) }}</b>
</div>
</div>
</template>
</v-slider>
</div>
@ -199,47 +209,126 @@ export default {
}
});
},
// 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: {
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 hasnt 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);
},
seek(e) {
console.log(`scrubbing ${e}`);
let vid = this.$refs.playerfake;
@ -251,8 +340,8 @@ export default {
vid,
0,
0,
this.$refs.player.clientWidth / 4,
this.$refs.player.clientHeight / 4
this.$refs.player.clientWidth / 3,
this.$refs.player.clientHeight / 3
);
},
scrub(e) {

View File

@ -25,19 +25,19 @@
:class="$vuetify.theme.dark ? 'lighten-3' : 'darken-3'"
:style="{
borderRadius: $store.state.tweaks.roundThumb
? `${$store.state.tweaks.roundTweak / 4}rem`
? `${$store.state.tweaks.roundTweak / 2}rem`
: '0',
}"
>
<div
class="background d-flex flex-column justify-center align-center"
class="d-flex flex-column justify-center align-center"
style="
position: absolute;
top: 0;
right: 0;
width: 40%;
width: 50%;
height: 100%;
opacity: 0.5;
background: linear-gradient(var(--v-background-base) -1000%, #00000000 1000%);
"
>
<div>420</div>
@ -64,7 +64,7 @@
class="flex-grow-1"
style="width: 2rem !important"
>
<v-icon>mdi-playlist-plus</v-icon>
<v-icon>mdi-share-outline</v-icon>
</v-btn>
<v-btn
text
@ -73,7 +73,7 @@
class="flex-grow-1"
style="width: 2rem !important"
>
<v-icon>mdi-share-outline</v-icon>
<v-icon>mdi-playlist-plus</v-icon>
</v-btn>
</div>
</v-card>

View File

@ -1,5 +1,7 @@
<template>
<v-card style="display: flex" class="rounded-0 pa-3 topNav background">
<!-- opacity with vue 😉 -->
<!-- :style="{ background: $vuetify.theme.currentTheme.primary + '55' }" -->
<h3
v-show="!search"
class="my-auto ml-4"
@ -112,7 +114,9 @@ export default {
<style scoped>
.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;
padding-top: env(safe-area-inset-top) !important;
box-shadow: none !important;

View File

@ -57,7 +57,7 @@
<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>
</div>
<div class="d-flex pl-2">
<div class="d-flex pl-6">
<v-btn
v-for="(item, index) in interactions"
:key="index"