Merge pull request #960 from TeamPiped/playwith

Implement play with playlists.
This commit is contained in:
Kavin 2022-04-08 22:38:37 +01:00 committed by GitHub
commit 128d39903d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 153 additions and 5 deletions

View File

@ -75,6 +75,7 @@ export default {
},
activated() {
window.addEventListener("scroll", this.handleScroll);
if (this.playlist) this.updateTitle();
},
deactivated() {
window.removeEventListener("scroll", this.handleScroll);
@ -86,7 +87,10 @@ export default {
async getPlaylistData() {
this.fetchPlaylist()
.then(data => (this.playlist = data))
.then(() => (document.title = this.playlist.name + " - Piped"));
.then(() => this.updateTitle());
},
async updateTitle() {
document.title = this.playlist.name + " - Piped";
},
handleScroll() {
if (this.loading || !this.playlist || !this.playlist.nextpage) return;

View File

@ -0,0 +1,57 @@
<template>
<div class="overflow-x-scroll h-screen-sm" ref="scrollable">
<VideoItem
v-for="(related, index) in playlist.relatedStreams"
:key="related.url"
:video="related"
:index="index"
:playlist-id="playlistId"
height="94"
width="168"
/>
</div>
</template>
<script>
import { nextTick } from "vue";
import VideoItem from "./VideoItem.vue";
export default {
components: { VideoItem },
props: {
playlist: {
type: Object,
required: true,
},
playlistId: {
type: String,
required: true,
},
selectedIndex: {
type: Number,
},
},
mounted() {
this.updateScroll();
},
methods: {
updateScroll() {
const elems = Array.from(this.$refs.scrollable.children).filter(elm => elm.matches("div"));
const index = this.selectedIndex - 1;
if (index < elems.length)
this.$refs.scrollable.scrollTop =
elems[this.selectedIndex - 1].offsetTop - this.$refs.scrollable.offsetTop;
},
},
watch: {
playlist: {
handler() {
if (this.selectedIndex - 1 < this.playlist.relatedStreams.length)
nextTick(() => {
this.updateScroll();
});
},
deep: true,
},
},
};
</script>

View File

@ -1,6 +1,15 @@
<template>
<div>
<router-link :to="video.url">
<router-link
:to="{
path: '/watch',
query: {
v: video.url.substr(-11),
...(playlistId && { list: playlistId }),
...(index >= 0 && { index: index + 1 }),
},
}"
>
<img :height="height" :width="width" class="w-full" :src="video.thumbnail" alt="" loading="lazy" />
<div class="relative text-sm">
<span
@ -26,7 +35,15 @@
<div class="float-right m-0 inline-block children:px-1">
<router-link
:to="video.url + '&listen=1'"
:to="{
path: '/watch',
query: {
v: video.url.substr(-11),
...(playlistId && { list: playlistId }),
...(index >= 0 && { index: index + 1 }),
listen: '1',
},
}"
:aria-label="'Listen to ' + video.title"
:title="'Listen to ' + video.title"
>

View File

@ -24,6 +24,14 @@ export default {
return {};
},
},
playlist: {
type: Object,
default: null,
},
index: {
type: Number,
default: -1,
},
sponsors: {
type: Object,
default: () => {
@ -38,6 +46,7 @@ export default {
return {
lastUpdate: new Date().getTime(),
initialSeekComplete: false,
destroying: false,
};
},
computed: {
@ -171,9 +180,11 @@ export default {
});
},
deactivated() {
this.destroying = true;
this.destroy(true);
},
unmounted() {
this.destroying = true;
this.destroy(true);
},
methods: {
@ -281,6 +292,7 @@ export default {
if (noPrevPlayer)
this.shakaPromise.then(() => {
if (this.destroying) return;
this.shaka.polyfill.installAll();
const localPlayer = new this.shaka.Player(videoEl);
@ -355,13 +367,23 @@ export default {
videoEl.addEventListener("ended", () => {
if (!this.selectedAutoLoop && this.selectedAutoPlay && this.video.relatedStreams.length > 0) {
const params = this.$route.query;
let url = this.video.relatedStreams[0].url;
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":
console.log(this.index);
console.log(this.playlist.relatedStreams.length);
if (this.index < this.playlist.relatedStreams.length)
searchParams.set("list", params.list);
break;
default:
searchParams.set(param, params[param]);
break;

View File

@ -7,7 +7,7 @@ export default {
activated() {
const videoId = this.$route.params.videoId;
if (videoId)
this.$router.push({
this.$router.replace({
path: "/watch",
query: { v: videoId },
});

View File

@ -4,6 +4,8 @@
ref="videoPlayer"
:video="video"
:sponsors="sponsors"
:playlist="playlist"
:index="index"
:selected-auto-play="false"
:selected-auto-loop="selectedAutoLoop"
:is-embed="isEmbed"
@ -18,6 +20,8 @@
ref="videoPlayer"
:video="video"
:sponsors="sponsors"
:playlist="playlist"
:index="index"
:selected-auto-play="selectedAutoPlay"
:selected-auto-loop="selectedAutoLoop"
/>
@ -128,6 +132,12 @@
</div>
<div v-if="video" class="order-first sm:order-last">
<PlaylistVideos
v-if="playlist"
:playlist-id="playlistId"
:playlist="playlist"
:selected-index="index"
/>
<a
class="btn mb-2 sm:hidden"
@click="showRecs = !showRecs"
@ -156,6 +166,7 @@ import ErrorHandler from "./ErrorHandler.vue";
import CommentItem from "./CommentItem.vue";
import Chapters from "./Chapters.vue";
import PlaylistAddModal from "./PlaylistAddModal.vue";
import PlaylistVideos from "./PlaylistVideos.vue";
export default {
name: "App",
@ -166,6 +177,7 @@ export default {
CommentItem,
Chapters,
PlaylistAddModal,
PlaylistVideos,
},
data() {
const smallViewQuery = window.matchMedia("(max-width: 640px)");
@ -173,6 +185,9 @@ export default {
video: {
title: "Loading...",
},
playlistId: null,
playlist: null,
index: null,
sponsors: null,
selectedAutoLoop: false,
selectedAutoPlay: null,
@ -237,6 +252,9 @@ export default {
})();
if (this.active) this.$refs.videoPlayer.loadVideo();
});
this.playlistId = this.$route.query.list;
this.index = Number(this.$route.query.index);
this.getPlaylistData();
this.getSponsors();
if (!this.isEmbed && this.getPreferenceBoolean("comments", true)) this.getComments();
window.addEventListener("resize", () => {
@ -307,6 +325,36 @@ export default {
}
});
},
async getPlaylistData() {
if (this.playlistId) {
await this.fetchJson(this.apiUrl() + "/playlists/" + this.playlistId).then(data => {
this.playlist = data;
});
await this.fetchPlaylistPages().then(() => {
if (!(this.index >= 0)) {
for (let i = 0; i < this.playlist.relatedStreams.length; i++)
if (this.playlist.relatedStreams[i].url.substr(-11) == this.getVideoId()) {
this.index = i + 1;
this.$router.replace({
query: { ...this.$route.query, index: this.index },
});
break;
}
}
});
}
},
async fetchPlaylistPages() {
if (this.playlist.nextpage) {
await this.fetchJson(this.apiUrl() + "/nextpage/playlists/" + this.playlistId, {
nextpage: this.playlist.nextpage,
}).then(json => {
this.playlist.relatedStreams = this.playlist.relatedStreams.concat(json.relatedStreams);
this.playlist.nextpage = json.nextpage;
});
await this.fetchPlaylistPages();
}
},
async getSponsors() {
if (this.getPreferenceBoolean("sponsorblock", true))
this.fetchSponsors().then(data => (this.sponsors = data));