Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve playlist display aside #3763

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
84 changes: 75 additions & 9 deletions src/components/PlaylistVideos.vue
Original file line number Diff line number Diff line change
@@ -1,24 +1,90 @@
<template>
<div ref="scrollable" class="h-screen-sm overflow-x-scroll">
<VideoItem
<div>
<router-link :to="{ path: '/playlist', query: { list: playlistId } }"
><h1 class="font-bold !text-lg hover:underline" v-text="playlist.name"
/></router-link>
<span class="text-sm">
<template v-if="playlist.uploader">
<router-link class="link-secondary" :to="playlist.uploaderUrl" :title="playlist.uploader">
{{ playlist.uploader }}
</router-link>
-
</template>
{{ selectedIndex }} / {{ playlist.videos }}
</span>
</div>
<div ref="scrollable" class="mt-4 max-h-screen-sm overflow-y-auto">
<div
v-for="(related, index) in playlist.relatedStreams"
:key="related.url"
:item="related"
:index="index"
:playlist-id="playlistId"
:prefer-listen="preferListen"
height="94"
width="168"
/>
>
<router-link
class="flex hover:bg-gray-50 .dark:hover:bg-neutral-800"
:class="{ 'bg-gray-200 .dark:bg-neutral-700': index === selectedIndex - 1 }"
:to="{
path: '/watch',
query: {
v: related.url.substr(-11),
...(playlistId && { list: playlistId }),
...(index >= 0 && { index: index + 1 }),
...(preferListen && { listen: 1 }),
},
}"
>
<span class="min-w-5 flex-none text-xs" v-text="index + 1" />
<div class="w-24 flex-none">
<VideoThumbnail :item="related" />

<div class="relative text-xs">
<!-- shorts thumbnail -->
<span v-if="related.isShort" v-t="'video.shorts'" class="thumbnail-overlay thumbnail-left" />
<span
v-if="related.duration > 0 || (related.duration == 0 && related.isShort)"
class="thumbnail-overlay thumbnail-right px-0.5"
v-text="timeFormat(related.duration)"
/>
<i18n-t
v-else
keypath="video.live"
class="thumbnail-overlay thumbnail-right !bg-red-600"
tag="div"
>
<i class="i-fa6-solid:tower-broadcast w-3" />
</i18n-t>
<span
v-if="related.watched"
v-t="'video.watched'"
class="thumbnail-overlay bottom-5px left-5px px-0.5"
/>
</div>
</div>
<div class="ml-2 flex flex-col">
<span class="text-sm font-semibold leading-4" :title="related.title" v-text="related.title" />
<div class="flex-1">
<router-link
v-if="related.uploaderUrl && related.uploaderName && !hideChannel"
class="link-secondary block overflow-hidden text-xs"
:to="related.uploaderUrl"
:title="related.uploaderName"
>
<span v-text="related.uploaderName" />
<i v-if="related.uploaderVerified" class="i-fa6-solid:check ml-1.5" />
</router-link>
</div>
</div>
</router-link>
</div>
</div>
</template>

<script>
import { nextTick } from "vue";
import VideoItem from "./VideoItem.vue";

import VideoThumbnail from "./VideoThumbnail.vue";
export default {
components: { VideoItem },
components: { VideoThumbnail },
props: {
playlist: {
type: Object,
Expand Down
36 changes: 5 additions & 31 deletions src/components/VideoItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,14 @@
},
}"
>
<div class="w-full">
<img
loading="lazy"
class="aspect-video w-full object-contain"
:src="thumbnail"
:alt="title"
:class="{ 'shorts-img': item.isShort, 'opacity-75': item.watched }"
/>
<!-- progress bar -->
<div class="relative h-1 w-full">
<div
v-if="item.watched && item.duration > 0"
class="absolute bottom-0 left-0 h-1 bg-red-600"
:style="{ width: `clamp(0%, ${(item.currentTime / item.duration) * 100}%, 100%` }"
/>
</div>
</div>
<VideoThumbnail :item="item" />

<div class="relative text-sm">
<span
v-if="item.duration > 0"
class="thumbnail-overlay thumbnail-right"
v-text="timeFormat(item.duration)"
/>
<!-- shorts thumbnail -->
<span v-if="item.isShort" v-t="'video.shorts'" class="thumbnail-overlay thumbnail-left" />
<span
v-else-if="item.duration >= 0"
class="thumbnail-overlay thumbnail-right"
v-if="item.duration > 0 || (item.duration == 0 && item.isShort)"
class="thumbnail-overlay thumbnail-right px-0.5"
v-text="timeFormat(item.duration)"
/>
<i18n-t v-else keypath="video.live" class="thumbnail-overlay thumbnail-right !bg-red-600" tag="div">
Expand Down Expand Up @@ -149,9 +128,10 @@
import PlaylistAddModal from "./PlaylistAddModal.vue";
import ShareModal from "./ShareModal.vue";
import ConfirmModal from "./ConfirmModal.vue";
import VideoThumbnail from "./VideoThumbnail.vue";

export default {
components: { PlaylistAddModal, ConfirmModal, ShareModal },
components: { PlaylistAddModal, ConfirmModal, ShareModal, VideoThumbnail },
props: {
item: {
type: Object,
Expand Down Expand Up @@ -215,9 +195,3 @@ export default {
},
};
</script>

<style>
.shorts-img {
@apply w-full object-contain;
}
</style>
45 changes: 45 additions & 0 deletions src/components/VideoThumbnail.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<template>
<div class="w-full">
<img
loading="lazy"
class="aspect-video w-full object-contain"
:src="item.thumbnail"
:alt="item.title"
:class="{ 'shorts-img': item.isShort, 'opacity-75': item.watched }"
/>
<!-- progress bar -->
<div class="relative h-1 w-full">
<div
v-if="item.watched && item.duration > 0"
class="absolute bottom-0 left-0 h-1 bg-red-600"
:style="{ width: `clamp(0%, ${(item.currentTime / item.duration) * 100}%, 100%` }"
/>
</div>
</div>
</template>
<script>
export default {
props: {
item: {
type: Object,
default: () => {
return {};
},
},
},
computed: {
title() {
return this.item.dearrow?.titles[0]?.title ?? this.item.title;
},
thumbnail() {
return this.item.dearrow?.thumbnails[0]?.thumbnail ?? this.item.thumbnail;
},
},
};
</script>

<style>
.shorts-img {
@apply w-full object-contain;
}
</style>
Loading