mirror of
https://github.com/TeamPiped/Piped.git
synced 2024-12-24 22:43:35 +00:00
Merge pull request #960 from TeamPiped/playwith
Implement play with playlists.
This commit is contained in:
commit
128d39903d
@ -75,6 +75,7 @@ export default {
|
|||||||
},
|
},
|
||||||
activated() {
|
activated() {
|
||||||
window.addEventListener("scroll", this.handleScroll);
|
window.addEventListener("scroll", this.handleScroll);
|
||||||
|
if (this.playlist) this.updateTitle();
|
||||||
},
|
},
|
||||||
deactivated() {
|
deactivated() {
|
||||||
window.removeEventListener("scroll", this.handleScroll);
|
window.removeEventListener("scroll", this.handleScroll);
|
||||||
@ -86,7 +87,10 @@ export default {
|
|||||||
async getPlaylistData() {
|
async getPlaylistData() {
|
||||||
this.fetchPlaylist()
|
this.fetchPlaylist()
|
||||||
.then(data => (this.playlist = data))
|
.then(data => (this.playlist = data))
|
||||||
.then(() => (document.title = this.playlist.name + " - Piped"));
|
.then(() => this.updateTitle());
|
||||||
|
},
|
||||||
|
async updateTitle() {
|
||||||
|
document.title = this.playlist.name + " - Piped";
|
||||||
},
|
},
|
||||||
handleScroll() {
|
handleScroll() {
|
||||||
if (this.loading || !this.playlist || !this.playlist.nextpage) return;
|
if (this.loading || !this.playlist || !this.playlist.nextpage) return;
|
||||||
|
57
src/components/PlaylistVideos.vue
Normal file
57
src/components/PlaylistVideos.vue
Normal 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>
|
@ -1,6 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<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" />
|
<img :height="height" :width="width" class="w-full" :src="video.thumbnail" alt="" loading="lazy" />
|
||||||
<div class="relative text-sm">
|
<div class="relative text-sm">
|
||||||
<span
|
<span
|
||||||
@ -26,7 +35,15 @@
|
|||||||
|
|
||||||
<div class="float-right m-0 inline-block children:px-1">
|
<div class="float-right m-0 inline-block children:px-1">
|
||||||
<router-link
|
<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"
|
:aria-label="'Listen to ' + video.title"
|
||||||
:title="'Listen to ' + video.title"
|
:title="'Listen to ' + video.title"
|
||||||
>
|
>
|
||||||
|
@ -24,6 +24,14 @@ export default {
|
|||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
playlist: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
default: -1,
|
||||||
|
},
|
||||||
sponsors: {
|
sponsors: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {
|
default: () => {
|
||||||
@ -38,6 +46,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
lastUpdate: new Date().getTime(),
|
lastUpdate: new Date().getTime(),
|
||||||
initialSeekComplete: false,
|
initialSeekComplete: false,
|
||||||
|
destroying: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -171,9 +180,11 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
deactivated() {
|
deactivated() {
|
||||||
|
this.destroying = true;
|
||||||
this.destroy(true);
|
this.destroy(true);
|
||||||
},
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
|
this.destroying = true;
|
||||||
this.destroy(true);
|
this.destroy(true);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -281,6 +292,7 @@ export default {
|
|||||||
|
|
||||||
if (noPrevPlayer)
|
if (noPrevPlayer)
|
||||||
this.shakaPromise.then(() => {
|
this.shakaPromise.then(() => {
|
||||||
|
if (this.destroying) return;
|
||||||
this.shaka.polyfill.installAll();
|
this.shaka.polyfill.installAll();
|
||||||
|
|
||||||
const localPlayer = new this.shaka.Player(videoEl);
|
const localPlayer = new this.shaka.Player(videoEl);
|
||||||
@ -355,13 +367,23 @@ export default {
|
|||||||
videoEl.addEventListener("ended", () => {
|
videoEl.addEventListener("ended", () => {
|
||||||
if (!this.selectedAutoLoop && this.selectedAutoPlay && this.video.relatedStreams.length > 0) {
|
if (!this.selectedAutoLoop && this.selectedAutoPlay && this.video.relatedStreams.length > 0) {
|
||||||
const params = this.$route.query;
|
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();
|
const searchParams = new URLSearchParams();
|
||||||
for (var param in params)
|
for (var param in params)
|
||||||
switch (param) {
|
switch (param) {
|
||||||
case "v":
|
case "v":
|
||||||
case "t":
|
case "t":
|
||||||
break;
|
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:
|
default:
|
||||||
searchParams.set(param, params[param]);
|
searchParams.set(param, params[param]);
|
||||||
break;
|
break;
|
||||||
|
@ -7,7 +7,7 @@ export default {
|
|||||||
activated() {
|
activated() {
|
||||||
const videoId = this.$route.params.videoId;
|
const videoId = this.$route.params.videoId;
|
||||||
if (videoId)
|
if (videoId)
|
||||||
this.$router.push({
|
this.$router.replace({
|
||||||
path: "/watch",
|
path: "/watch",
|
||||||
query: { v: videoId },
|
query: { v: videoId },
|
||||||
});
|
});
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
ref="videoPlayer"
|
ref="videoPlayer"
|
||||||
:video="video"
|
:video="video"
|
||||||
:sponsors="sponsors"
|
:sponsors="sponsors"
|
||||||
|
:playlist="playlist"
|
||||||
|
:index="index"
|
||||||
:selected-auto-play="false"
|
:selected-auto-play="false"
|
||||||
:selected-auto-loop="selectedAutoLoop"
|
:selected-auto-loop="selectedAutoLoop"
|
||||||
:is-embed="isEmbed"
|
:is-embed="isEmbed"
|
||||||
@ -18,6 +20,8 @@
|
|||||||
ref="videoPlayer"
|
ref="videoPlayer"
|
||||||
:video="video"
|
:video="video"
|
||||||
:sponsors="sponsors"
|
:sponsors="sponsors"
|
||||||
|
:playlist="playlist"
|
||||||
|
:index="index"
|
||||||
:selected-auto-play="selectedAutoPlay"
|
:selected-auto-play="selectedAutoPlay"
|
||||||
:selected-auto-loop="selectedAutoLoop"
|
:selected-auto-loop="selectedAutoLoop"
|
||||||
/>
|
/>
|
||||||
@ -128,6 +132,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="video" class="order-first sm:order-last">
|
<div v-if="video" class="order-first sm:order-last">
|
||||||
|
<PlaylistVideos
|
||||||
|
v-if="playlist"
|
||||||
|
:playlist-id="playlistId"
|
||||||
|
:playlist="playlist"
|
||||||
|
:selected-index="index"
|
||||||
|
/>
|
||||||
<a
|
<a
|
||||||
class="btn mb-2 sm:hidden"
|
class="btn mb-2 sm:hidden"
|
||||||
@click="showRecs = !showRecs"
|
@click="showRecs = !showRecs"
|
||||||
@ -156,6 +166,7 @@ import ErrorHandler from "./ErrorHandler.vue";
|
|||||||
import CommentItem from "./CommentItem.vue";
|
import CommentItem from "./CommentItem.vue";
|
||||||
import Chapters from "./Chapters.vue";
|
import Chapters from "./Chapters.vue";
|
||||||
import PlaylistAddModal from "./PlaylistAddModal.vue";
|
import PlaylistAddModal from "./PlaylistAddModal.vue";
|
||||||
|
import PlaylistVideos from "./PlaylistVideos.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
@ -166,6 +177,7 @@ export default {
|
|||||||
CommentItem,
|
CommentItem,
|
||||||
Chapters,
|
Chapters,
|
||||||
PlaylistAddModal,
|
PlaylistAddModal,
|
||||||
|
PlaylistVideos,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const smallViewQuery = window.matchMedia("(max-width: 640px)");
|
const smallViewQuery = window.matchMedia("(max-width: 640px)");
|
||||||
@ -173,6 +185,9 @@ export default {
|
|||||||
video: {
|
video: {
|
||||||
title: "Loading...",
|
title: "Loading...",
|
||||||
},
|
},
|
||||||
|
playlistId: null,
|
||||||
|
playlist: null,
|
||||||
|
index: null,
|
||||||
sponsors: null,
|
sponsors: null,
|
||||||
selectedAutoLoop: false,
|
selectedAutoLoop: false,
|
||||||
selectedAutoPlay: null,
|
selectedAutoPlay: null,
|
||||||
@ -237,6 +252,9 @@ export default {
|
|||||||
})();
|
})();
|
||||||
if (this.active) this.$refs.videoPlayer.loadVideo();
|
if (this.active) this.$refs.videoPlayer.loadVideo();
|
||||||
});
|
});
|
||||||
|
this.playlistId = this.$route.query.list;
|
||||||
|
this.index = Number(this.$route.query.index);
|
||||||
|
this.getPlaylistData();
|
||||||
this.getSponsors();
|
this.getSponsors();
|
||||||
if (!this.isEmbed && this.getPreferenceBoolean("comments", true)) this.getComments();
|
if (!this.isEmbed && this.getPreferenceBoolean("comments", true)) this.getComments();
|
||||||
window.addEventListener("resize", () => {
|
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() {
|
async getSponsors() {
|
||||||
if (this.getPreferenceBoolean("sponsorblock", true))
|
if (this.getPreferenceBoolean("sponsorblock", true))
|
||||||
this.fetchSponsors().then(data => (this.sponsors = data));
|
this.fetchSponsors().then(data => (this.sponsors = data));
|
||||||
|
Loading…
Reference in New Issue
Block a user