diff --git a/NUXT/components/CompactRenderers/compactChannelRenderer.vue b/NUXT/components/CompactRenderers/compactChannelRenderer.vue
index 643b968..7712300 100644
--- a/NUXT/components/CompactRenderers/compactChannelRenderer.vue
+++ b/NUXT/components/CompactRenderers/compactChannelRenderer.vue
@@ -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"
>
mdi-share-outline
diff --git a/NUXT/components/Player/legacy.vue b/NUXT/components/Player/legacy.vue
index c586822..8fe61b8 100644
--- a/NUXT/components/Player/legacy.vue
+++ b/NUXT/components/Player/legacy.vue
@@ -14,6 +14,7 @@
autoplay
width="100%"
:src="vidSrc"
+ style="filter: brightness(50%)"
@webkitfullscreenchange="handleFullscreenChange"
/>
+
-
+
+
+
+ {{ $vuetube.humanTime(value) }}
+
+
@@ -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 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);
+ },
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) {
diff --git a/NUXT/components/playlistCard.vue b/NUXT/components/playlistCard.vue
index 47e3709..66623c2 100644
--- a/NUXT/components/playlistCard.vue
+++ b/NUXT/components/playlistCard.vue
@@ -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',
}"
>
420
@@ -64,7 +64,7 @@
class="flex-grow-1"
style="width: 2rem !important"
>
-
mdi-playlist-plus
+
mdi-share-outline
- mdi-share-outline
+ mdi-playlist-plus
diff --git a/NUXT/components/topNavigation.vue b/NUXT/components/topNavigation.vue
index 56a8a96..c16dcbd 100644
--- a/NUXT/components/topNavigation.vue
+++ b/NUXT/components/topNavigation.vue
@@ -1,5 +1,7 @@
+
+
.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;
diff --git a/NUXT/pages/watch.vue b/NUXT/pages/watch.vue
index 973f068..80aad62 100644
--- a/NUXT/pages/watch.vue
+++ b/NUXT/pages/watch.vue
@@ -57,7 +57,7 @@
mdi-chevron-up
mdi-chevron-down
-