Merge pull request #2160 from vr10t/toast

Add countdown until next video
This commit is contained in:
Kavin 2023-04-29 19:47:30 +01:00 committed by GitHub
commit 9eff395da6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 126 additions and 47 deletions

View file

@ -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,
@ -462,6 +473,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);
@ -516,6 +528,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);

View file

@ -0,0 +1,28 @@
<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-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;
}
</style>

View file

@ -39,14 +39,6 @@ export default {
return {};
},
},
playlist: {
type: Object,
default: null,
},
index: {
type: Number,
default: -1,
},
sponsors: {
type: Object,
default: () => {
@ -409,13 +401,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");
});
}
@ -625,31 +611,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"];

View file

@ -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 @@
<LoadingIndicatorPage :show-content="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'">
@ -22,11 +25,10 @@
ref="videoPlayer"
:video="video"
:sponsors="sponsors"
:playlist="playlist"
:index="index"
:selected-auto-play="selectedAutoPlay"
:selected-auto-loop="selectedAutoLoop"
@timeupdate="onTimeUpdate"
@ended="onVideoEnded"
/>
</keep-alive>
<ChaptersBar
@ -233,6 +235,7 @@ import ShareModal from "./ShareModal.vue";
import PlaylistVideos from "./PlaylistVideos.vue";
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
import ToastComponent from "./ToastComponent.vue";
export default {
name: "App",
@ -247,6 +250,7 @@ export default {
PlaylistVideos,
WatchOnYouTubeButton,
LoadingIndicatorPage,
ToastComponent,
},
data() {
const smallViewQuery = window.matchMedia("(max-width: 640px)");
@ -272,6 +276,9 @@ export default {
showShareModal: false,
isMobile: true,
currentTime: 0,
shouldShowToast: false,
timeoutCounter: null,
counter: 0,
};
},
computed: {
@ -293,6 +300,9 @@ export default {
year: "numeric",
});
},
defaultCounter(_this) {
return _this.getPreferenceNumber("autoPlayNextCountdown", 5);
},
},
mounted() {
// check screen size
@ -362,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() {
@ -562,6 +574,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>

View file

@ -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)",
@ -128,8 +129,9 @@
"with_playlist": "Share with playlist",
"bookmark_playlist": "Bookmark",
"playlist_bookmarked": "Bookmarked",
"show_more": "Show more",
"show_less": "Show less"
"dismiss": "Dismiss",
"show_more": "Show more",
"show_less": "Show less"
},
"comment": {
"pinned_by": "Pinned by {author}",
@ -182,6 +184,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"
}
}