From 2134e82efe7fc7db522db630299ccc9c72945d50 Mon Sep 17 00:00:00 2001 From: vr10t <ai6@tuta.io> Date: Thu, 2 Mar 2023 21:55:23 +0000 Subject: [PATCH 1/4] add countdown until next video --- src/components/VideoPlayer.vue | 41 +--------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/src/components/VideoPlayer.vue b/src/components/VideoPlayer.vue index 148fb0db..21b5bec9 100644 --- a/src/components/VideoPlayer.vue +++ b/src/components/VideoPlayer.vue @@ -38,14 +38,6 @@ export default { return {}; }, }, - playlist: { - type: Object, - default: null, - }, - index: { - type: Number, - default: -1, - }, sponsors: { type: Object, default: () => { @@ -395,13 +387,7 @@ export default { }); videoEl.addEventListener("ended", () => { - if ( - !this.selectedAutoLoop && - this.selectedAutoPlay && - (this.playlist?.relatedStreams?.length > 0 || this.video.relatedStreams.length > 0) - ) { - this.navigateNext(); - } + this.$emit("ended"); }); } @@ -609,31 +595,6 @@ export default { this.$refs.videoEl.currentTime = time; } }, - navigateNext() { - const params = this.$route.query; - let url = this.playlist?.relatedStreams?.[this.index]?.url ?? this.video.relatedStreams[0].url; - const searchParams = new URLSearchParams(); - for (var param in params) - switch (param) { - case "v": - case "t": - break; - case "index": - if (this.index < this.playlist.relatedStreams.length) searchParams.set("index", this.index + 1); - break; - case "list": - if (this.index < this.playlist.relatedStreams.length) searchParams.set("list", params.list); - break; - default: - searchParams.set(param, params[param]); - break; - } - // save the fullscreen state - searchParams.set("fullscreen", this.$ui.getControls().isFullScreenEnabled()); - const paramStr = searchParams.toString(); - if (paramStr.length > 0) url += "&" + paramStr; - this.$router.push(url); - }, updateMarkers() { const markers = this.$refs.container.querySelector(".shaka-ad-markers"); const array = ["to right"]; From e46cbadc51cd5e620ef745ad475f895542f056db Mon Sep 17 00:00:00 2001 From: vr10t <ai6@tuta.io> Date: Thu, 2 Mar 2023 21:56:06 +0000 Subject: [PATCH 2/4] add countdown until next video --- src/components/PreferencesPage.vue | 13 +++++ src/components/ToastComponent.vue | 25 ++++++++++ src/components/WatchVideo.vue | 80 ++++++++++++++++++++++++++++-- src/locales/en.json | 7 ++- 4 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 src/components/ToastComponent.vue diff --git a/src/components/PreferencesPage.vue b/src/components/PreferencesPage.vue index 65a381f7..3db2d434 100644 --- a/src/components/PreferencesPage.vue +++ b/src/components/PreferencesPage.vue @@ -45,6 +45,16 @@ @change="onChange($event)" /> </label> + <label class="pref" for="chkAutoPlayNextCountdown"> + <strong v-t="'actions.autoplay_next_countdown'" /> + <input + id="chkAutoPlayNextCountdown" + v-model="autoPlayNextCountdown" + class="input w-24" + type="number" + @change="onChange($event)" + /> + </label> <label class="pref" for="chkAudioOnly"> <strong v-t="'actions.audio_only'" /> <input id="chkAudioOnly" v-model="listen" class="checkbox" type="checkbox" @change="onChange($event)" /> @@ -346,6 +356,7 @@ export default { minSegmentLength: 0, selectedTheme: "dark", autoPlayVideo: true, + autoPlayNextCountdown: 5, listen: false, resolutions: [144, 240, 360, 480, 720, 1080, 1440, 2160, 4320], defaultQuality: 0, @@ -461,6 +472,7 @@ export default { this.minSegmentLength = Math.max(this.getPreferenceNumber("minSegmentLength", 0), 0); this.selectedTheme = this.getPreferenceString("theme", "dark"); this.autoPlayVideo = this.getPreferenceBoolean("playerAutoPlay", true); + this.autoPlayNextCountdown = this.getPreferenceNumber("autoPlayNextCountdown", 5); this.listen = this.getPreferenceBoolean("listen", false); this.defaultQuality = Number(localStorage.getItem("quality")); this.bufferingGoal = Math.max(Number(localStorage.getItem("bufferGoal")), 10); @@ -515,6 +527,7 @@ export default { localStorage.setItem("minSegmentLength", this.minSegmentLength); localStorage.setItem("theme", this.selectedTheme); localStorage.setItem("playerAutoPlay", this.autoPlayVideo); + localStorage.setItem("autoPlayNextCountdown", this.autoPlayNextCountdown); localStorage.setItem("listen", this.listen); localStorage.setItem("quality", this.defaultQuality); localStorage.setItem("bufferGoal", this.bufferingGoal); diff --git a/src/components/ToastComponent.vue b/src/components/ToastComponent.vue new file mode 100644 index 00000000..dd436ce8 --- /dev/null +++ b/src/components/ToastComponent.vue @@ -0,0 +1,25 @@ +<template> + <div class="toast"> + <slot /> + <button @click="dismiss" v-t="'actions.dismiss'" /> + </div> +</template> + +<script> +export default { + methods: { + dismiss() { + this.$emit("dismissed"); + }, + }, +}; +</script> + +<style> +.toast { + @apply bg-dark-900/80 text-white flex flex-col justify-center fixed top-12 right-12 p-4 min-w-max shadow rounded duration-200 z-9999; +} +.toast button { + @apply underline; +} +</style> diff --git a/src/components/WatchVideo.vue b/src/components/WatchVideo.vue index db1a3e30..402355c8 100644 --- a/src/components/WatchVideo.vue +++ b/src/components/WatchVideo.vue @@ -4,8 +4,6 @@ ref="videoPlayer" :video="video" :sponsors="sponsors" - :playlist="playlist" - :index="index" :selected-auto-play="false" :selected-auto-loop="selectedAutoLoop" :is-embed="isEmbed" @@ -14,6 +12,11 @@ <div v-if="video && !isEmbed" class="w-full"> <ErrorHandler v-if="video && video.error" :message="video.message" :error="video.error" /> + <Transition> + <ToastComponent v-if="shouldShowToast" @dismissed="dismiss"> + <i18n-t keypath="info.next_video_countdown">{{ counter }}</i18n-t> + </ToastComponent> + </Transition> <div v-show="!video.error"> <div :class="isMobile ? 'flex-col' : 'flex'"> @@ -21,11 +24,10 @@ ref="videoPlayer" :video="video" :sponsors="sponsors" - :playlist="playlist" - :index="index" :selected-auto-play="selectedAutoPlay" :selected-auto-loop="selectedAutoLoop" @timeupdate="onTimeUpdate" + @ended="onVideoEnded" /> <ChaptersBar :mobileLayout="isMobile" @@ -230,6 +232,7 @@ import PlaylistAddModal from "./PlaylistAddModal.vue"; import ShareModal from "./ShareModal.vue"; import PlaylistVideos from "./PlaylistVideos.vue"; import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue"; +import ToastComponent from "./ToastComponent.vue"; export default { name: "App", @@ -243,6 +246,7 @@ export default { ShareModal, PlaylistVideos, WatchOnYouTubeButton, + ToastComponent, }, data() { const smallViewQuery = window.matchMedia("(max-width: 640px)"); @@ -270,6 +274,9 @@ export default { showShareModal: false, isMobile: true, currentTime: 0, + shouldShowToast: false, + timeoutCounter: null, + counter: 0, }; }, computed: { @@ -291,6 +298,9 @@ export default { year: "numeric", }); }, + defaultCounter(_this) { + return _this.getPreferenceNumber("autoPlayNextCountdown", 5); + }, }, mounted() { // check screen size @@ -560,6 +570,68 @@ export default { onTimeUpdate(time) { this.currentTime = time; }, + onVideoEnded() { + if ( + !this.selectedAutoLoop && + this.selectedAutoPlay && + (this.playlist?.relatedStreams?.length > 0 || this.video.relatedStreams.length > 0) + ) { + this.showToast(); + } + }, + showToast() { + this.counter = this.defaultCounter; + if (this.counter < 1) { + this.navigateNext(); + return; + } + if (this.timeoutCounter) clearInterval(this.timeoutCounter); + this.timeoutCounter = setInterval(() => { + this.counter--; + if (this.counter === 0) { + this.dismiss(); + this.navigateNext(); + } + }, 1000); + this.shouldShowToast = true; + }, + dismiss() { + clearInterval(this.timeoutCounter); + this.shouldShowToast = false; + }, + navigateNext() { + const params = this.$route.query; + let url = this.playlist?.relatedStreams?.[this.index]?.url ?? this.video.relatedStreams[0].url; + const searchParams = new URLSearchParams(); + for (var param in params) + switch (param) { + case "v": + case "t": + break; + case "index": + if (this.index < this.playlist.relatedStreams.length) searchParams.set("index", this.index + 1); + break; + case "list": + if (this.index < this.playlist.relatedStreams.length) searchParams.set("list", params.list); + break; + default: + searchParams.set(param, params[param]); + break; + } + // save the fullscreen state + searchParams.set("fullscreen", this.$refs.videoPlayer.$ui.getControls().isFullScreenEnabled()); + const paramStr = searchParams.toString(); + if (paramStr.length > 0) url += "&" + paramStr; + this.$router.push(url); + }, }, }; </script> + +<style> +.v-enter-from, +.v-leave-to { + opacity: 0; + transform: translateX(100%) scale(0.5); +} +</style> diff --git a/src/locales/en.json b/src/locales/en.json index 6ddfab97..0b0d5ac5 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -49,6 +49,7 @@ "dark": "Dark", "light": "Light", "autoplay_video": "Autoplay Video", + "autoplay_next_countdown": "Default Countdown until next video (in seconds)", "audio_only": "Audio Only", "default_quality": "Default Quality", "buffering_goal": "Buffering Goal (in seconds)", @@ -127,7 +128,8 @@ "no_valid_playlists": "The file doesn't contain valid playlists!", "with_playlist": "Share with playlist", "bookmark_playlist": "Bookmark", - "playlist_bookmarked": "Bookmarked" + "playlist_bookmarked": "Bookmarked", + "dismiss": "Dismiss" }, "comment": { "pinned_by": "Pinned by {author}", @@ -180,6 +182,7 @@ "copied": "Copied!", "cannot_copy": "Can't copy!", "local_storage": "This action requires localStorage, are cookies enabled?", - "register_no_email_note": "Using an e-mail as username is not recommended. Proceed anyways?" + "register_no_email_note": "Using an e-mail as username is not recommended. Proceed anyways?", + "next_video_countdown": "Playing next video in {0}s" } } From 39a64c89390560b722da94159bbb0fa6fd5af4b3 Mon Sep 17 00:00:00 2001 From: vr10t <ai6@tuta.io> Date: Sat, 11 Mar 2023 17:55:25 +0000 Subject: [PATCH 3/4] allow light mode --- src/components/ToastComponent.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/ToastComponent.vue b/src/components/ToastComponent.vue index dd436ce8..e80bed60 100644 --- a/src/components/ToastComponent.vue +++ b/src/components/ToastComponent.vue @@ -17,7 +17,10 @@ export default { <style> .toast { - @apply bg-dark-900/80 text-white flex flex-col justify-center fixed top-12 right-12 p-4 min-w-max shadow rounded duration-200 z-9999; + @apply bg-white/80 text-black flex flex-col justify-center fixed top-12 right-12 p-4 min-w-max shadow rounded duration-200 z-9999; +} +.dark .toast { + @apply bg-dark-900/80 text-white; } .toast button { @apply underline; From f1c1cb94f86f96113cf48d4c7a9a0263d2007bd3 Mon Sep 17 00:00:00 2001 From: Kavin <20838718+FireMasterK@users.noreply.github.com> Date: Sat, 29 Apr 2023 19:45:13 +0100 Subject: [PATCH 4/4] Dismiss autoplay on page unload. --- src/components/WatchVideo.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/WatchVideo.vue b/src/components/WatchVideo.vue index 248a6640..3a8e029a 100644 --- a/src/components/WatchVideo.vue +++ b/src/components/WatchVideo.vue @@ -372,10 +372,12 @@ export default { deactivated() { this.active = false; window.removeEventListener("scroll", this.handleScroll); + this.dismiss(); }, unmounted() { window.removeEventListener("scroll", this.handleScroll); window.removeEventListener("click", this.handleClick); + this.dismiss(); }, methods: { fetchVideo() {