[media UI] Improved media UI.

This commit is contained in:
Fabio Manganiello 2023-11-07 00:45:16 +01:00
parent e7bd61e0d4
commit 3d2dc22ed3
Signed by: blacklight
GPG key ID: D90FBA7F76362774
7 changed files with 354 additions and 157 deletions

View file

@ -1,7 +1,7 @@
<template>
<div class="header" :class="{'with-filter': filterVisible}">
<div class="row">
<div class="col-7 left side" v-if="selectedView === 'search'">
<div class="col-s-8 col-m-7 left side" v-if="selectedView === 'search'">
<button title="Filter" class="filter-btn" :class="{selected: filterVisible}"
@click="filterVisible = !filterVisible">
<i class="fa fa-filter" />
@ -14,7 +14,7 @@
</form>
</div>
<div class="col-7 left side" v-else-if="selectedView === 'torrents'">
<div class="col-s-8 col-m-7 left side" v-else-if="selectedView === 'torrents'">
<form @submit.prevent="$emit('torrent-add', torrentURL)">
<label class="search-box">
<input type="search" placeholder="Add torrent URL" v-model="torrentURL">
@ -22,14 +22,18 @@
</form>
</div>
<div class="col-7 left side" v-else-if="selectedView === 'browser'">
<div class="col-s-8 col-m-7 left side" v-else-if="selectedView === 'browser'">
<label class="search-box">
<input type="search" placeholder="Filter" :value="browserFilter" @change="$emit('filter', $event.target.value)"
@keyup="$emit('filter', $event.target.value)">
</label>
</div>
<div class="col-5 right side">
<div class="col-s-4 col-m-5 right side">
<button class="mobile" title="Menu" @click="$emit('toggle-nav')" v-if="showNavButton">
<i class="fas fa-bars" />
</button>
<button title="Select subtitles" class="captions-btn" :class="{selected: selectedSubtitles != null}"
@click="$emit('show-subtitles')" v-if="hasSubtitlesPlugin && selectedItem &&
(selectedItem.type === 'file' || (selectedItem.url || '').startsWith('file://'))">
@ -59,8 +63,17 @@ import Players from "@/components/panels/Media/Players";
export default {
name: "Header",
components: {Players},
emits: ['search', 'select-player', 'player-status', 'torrent-add', 'show-subtitles', 'play-url', 'filter',
'source-toggle'],
emits: [
'filter',
'play-url',
'player-status',
'search',
'select-player',
'show-subtitles',
'source-toggle',
'toggle-nav',
'torrent-add',
],
props: {
pluginName: {
@ -86,6 +99,10 @@ export default {
default: false,
},
showNavButton: {
type: Boolean,
},
browserFilter: {
type: String,
default: '',
@ -165,9 +182,8 @@ export default {
:deep(button) {
background: none;
padding: 0 .25em;
padding: 0 .5em;
border: 0;
margin-right: .25em;
&:hover {
color: $default-hover-fg-2;

View file

@ -7,19 +7,30 @@
:buttons="mediaButtons" @play="pause" @pause="pause" @stop="stop" @set-volume="setVolume"
@seek="seek" @search="search" @mute="toggleMute" @unmute="toggleMute">
<main>
<div class="nav-container">
<Nav :selected-view="selectedView" @input="selectedView = $event" />
<div class="nav-container from tablet" :style="navContainerStyle">
<Nav :selected-view="selectedView" @input="selectedView = $event"
@toggle="forceShowNav = !forceShowNav" />
</div>
<div class="view-container">
<Header :plugin-name="pluginName" :selected-view="selectedView" :has-subtitles-plugin="hasSubtitlesPlugin"
ref="header" :sources="sources" :selected-item="selectedPlayer && selectedPlayer.status &&
(selectedPlayer.status.state === 'play' || selectedPlayer.status.state === 'pause')
? selectedPlayer.status : results[selectedResult]" :selected-subtitles="selectedSubtitles"
:browser-filter="browserFilter" @search="search" @select-player="selectedPlayer = $event"
@player-status="onStatusUpdate" @torrent-add="downloadTorrent($event)"
@show-subtitles="showSubtitlesModal = !showSubtitlesModal" @play-url="showPlayUrlModal"
@filter="browserFilter = $event" @source-toggle="sources[$event] = !sources[$event]" />
<Header :plugin-name="pluginName"
:selected-view="selectedView"
:has-subtitles-plugin="hasSubtitlesPlugin"
:sources="sources"
:selected-item="selectedItem"
:selected-subtitles="selectedSubtitles"
:browser-filter="browserFilter"
:show-nav-button="!forceShowNav"
ref="header"
@search="search"
@select-player="selectedPlayer = $event"
@player-status="onStatusUpdate"
@torrent-add="downloadTorrent($event)"
@show-subtitles="showSubtitlesModal = !showSubtitlesModal"
@play-url="showPlayUrlModal"
@filter="browserFilter = $event"
@toggle-nav="forceShowNav = !forceShowNav"
@source-toggle="sources[$event] = !sources[$event]" />
<div class="body-container" :class="{'expanded-header': $refs.header?.filterVisible}">
<Results :results="results" :selected-result="selectedResult" @select="onResultSelect($event)"
@ -120,6 +131,7 @@ export default {
selectedView: 'search',
selectedSubtitles: null,
showSubtitlesModal: false,
forceShowNav: false,
awaitingPlayTorrent: null,
urlPlay: null,
browserFilter: null,
@ -141,6 +153,28 @@ export default {
hasSubtitlesPlugin() {
return 'media.subtitles' in this.$root.config
},
navContainerStyle() {
if (this.forceShowNav)
return {
display: 'flex !important',
}
return {}
},
selectedItem() {
if (
this.selectedPlayer && this.selectedPlayer.status &&
(
this.selectedPlayer.status.state === 'play' ||
this.selectedPlayer.status.state === 'pause'
)
)
return this.selectedPlayer.status
return this.results[this.selectedResult]
},
},
methods: {
@ -420,12 +454,9 @@ export default {
:deep(.media-info-container) {
.modal-container {
.content {
max-width: 75%;
}
.body {
padding: 1em .5em;
max-width: calc(100vw - 2px);
padding: 0;
overflow: auto;
}
}

View file

@ -1,157 +1,149 @@
<template>
<div class="row" v-if="item?.title">
<div class="left side">Title</div>
<div class="right side">
<a :href="`https://www.imdb.com/title/${item.imdb_id}`" target="_blank" v-if="item.imdb_id"
v-text="item.title" />
<span v-else v-text="item.title" />
<div class="media-info">
<div class="row header">
<MediaImage :item="item" />
<div class="title">
<a :href="item.url" target="_blank" v-if="item.url" v-text="item.title" />
<span v-else v-text="item.title" />
</div>
</div>
</div>
<div class="row" v-if="item?.series">
<div class="left side">TV Series</div>
<div class="right side" v-text="item.series" />
</div>
<div class="row" v-if="item?.season">
<div class="left side">Season</div>
<div class="right side" v-text="item.season" />
</div>
<div class="row" v-if="item?.episode">
<div class="left side">Episode</div>
<div class="right side" v-text="item.episode" />
</div>
<div class="row" v-if="item?.num_seasons">
<div class="left side">Number of seasons</div>
<div class="right side" v-text="item.num_seasons" />
</div>
<div class="row" v-if="item?.synopsis">
<div class="left side">Synopsis</div>
<div class="right side" v-text="item.synopsis" />
</div>
<div class="row" v-if="item?.description">
<div class="left side">Description</div>
<div class="right side" v-text="item.description" />
</div>
<div class="row" v-if="item?.summary">
<div class="left side">Summary</div>
<div class="right side" v-text="item.summary" />
</div>
<div class="row" v-if="item?.overview">
<div class="left side">Overview</div>
<div class="right side" v-text="item.overview" />
</div>
<div class="row" v-if="item?.duration">
<div class="left side">Duration</div>
<div class="right side" v-text="convertTime(item.duration)" />
</div>
<div class="row" v-if="item?.country">
<div class="left side">Country</div>
<div class="right side" v-text="item.country" />
</div>
<div class="row" v-if="item?.network">
<div class="left side">Network</div>
<div class="right side" v-text="item.network" />
</div>
<div class="row" v-if="item?.status">
<div class="left side">Status</div>
<div class="right side" v-text="item.status" />
</div>
<div class="row" v-if="item?.rating">
<div class="left side">Rating</div>
<div class="right side" v-text="item.rating.percentage" />
</div>
<div class="row" v-if="item?.rating">
<div class="left side">Votes</div>
<div class="right side" v-text="item.rating.votes" />
</div>
<div class="row" v-if="item?.genres">
<div class="left side">Genres</div>
<div class="right side" v-text="item.genres.join(', ')" />
</div>
<div class="row" v-if="item?.channelId">
<div class="left side">Channel</div>
<div class="right side">
<a :href="`https://www.youtube.com/channel/${item.channelId}`" target="_blank"
v-text="item.channelTitle || `https://www.youtube.com/channel/${item.channelId}`" />
<div class="row" v-if="item?.series">
<div class="left side">TV Series</div>
<div class="right side" v-text="item.series" />
</div>
</div>
<div class="row" v-if="item?.year">
<div class="left side">Year</div>
<div class="right side" v-text="item.year" />
</div>
<div class="row" v-if="item?.publishedAt">
<div class="left side">Published at</div>
<div class="right side" v-text="formatDate(item.publishedAt, true)" />
</div>
<div class="row" v-if="item?.file">
<div class="left side">File</div>
<div class="right side" v-text="item.file" />
</div>
<div class="row" v-if="item?.url">
<div class="left side">URL</div>
<div class="right side url">
<a :href="item.url" target="_blank" v-text="item.url" />
<div class="row" v-if="item?.season">
<div class="left side">Season</div>
<div class="right side" v-text="item.season" />
</div>
</div>
<div class="row" v-if="item?.trailer">
<div class="left side">Trailer</div>
<div class="right side url">
<a :href="item.trailer" target="_blank" v-text="item.trailer" />
<div class="row" v-if="item?.episode">
<div class="left side">Episode</div>
<div class="right side" v-text="item.episode" />
</div>
</div>
<div class="row" v-if="item?.size">
<div class="left side">Size</div>
<div class="right side" v-text="convertSize(item.size)" />
</div>
<div class="row" v-if="item?.num_seasons">
<div class="left side">Number of seasons</div>
<div class="right side" v-text="item.num_seasons" />
</div>
<div class="row" v-if="item?.quality">
<div class="left side">Quality</div>
<div class="right side" v-text="item.quality" />
</div>
<div class="row" v-if="item?.synopsis">
<div class="left side">Synopsis</div>
<div class="right side" v-text="item.synopsis" />
</div>
<div class="row" v-if="item?.seeds">
<div class="left side">Seeds</div>
<div class="right side" v-text="item.seeds" />
</div>
<div class="row" v-if="item?.description">
<div class="left side">Description</div>
<div class="right side" v-text="item.description" />
</div>
<div class="row" v-if="item?.peers">
<div class="left side">Peers</div>
<div class="right side" v-text="item.peers" />
</div>
<div class="row" v-if="item?.summary">
<div class="left side">Summary</div>
<div class="right side" v-text="item.summary" />
</div>
<div class="row" v-if="item?.language">
<div class="left side">Language</div>
<div class="right side" v-text="item.language" />
<div class="row" v-if="item?.overview">
<div class="left side">Overview</div>
<div class="right side" v-text="item.overview" />
</div>
<div class="row" v-if="item?.country">
<div class="left side">Country</div>
<div class="right side" v-text="item.country" />
</div>
<div class="row" v-if="item?.network">
<div class="left side">Network</div>
<div class="right side" v-text="item.network" />
</div>
<div class="row" v-if="item?.status">
<div class="left side">Status</div>
<div class="right side" v-text="item.status" />
</div>
<div class="row" v-if="item?.rating">
<div class="left side">Rating</div>
<div class="right side" v-text="item.rating.percentage" />
</div>
<div class="row" v-if="item?.rating">
<div class="left side">Votes</div>
<div class="right side" v-text="item.rating.votes" />
</div>
<div class="row" v-if="item?.genres">
<div class="left side">Genres</div>
<div class="right side" v-text="item.genres.join(', ')" />
</div>
<div class="row" v-if="item?.channelId">
<div class="left side">Channel</div>
<div class="right side">
<a :href="`https://www.youtube.com/channel/${item.channelId}`" target="_blank"
v-text="item.channelTitle || `https://www.youtube.com/channel/${item.channelId}`" />
</div>
</div>
<div class="row" v-if="item?.year">
<div class="left side">Year</div>
<div class="right side" v-text="item.year" />
</div>
<div class="row" v-if="item?.publishedAt">
<div class="left side">Published at</div>
<div class="right side" v-text="formatDate(item.publishedAt, true)" />
</div>
<div class="row" v-if="item?.file">
<div class="left side">File</div>
<div class="right side" v-text="item.file" />
</div>
<div class="row" v-if="item?.trailer">
<div class="left side">Trailer</div>
<div class="right side url">
<a :href="item.trailer" target="_blank" v-text="item.trailer" />
</div>
</div>
<div class="row" v-if="item?.size">
<div class="left side">Size</div>
<div class="right side" v-text="convertSize(item.size)" />
</div>
<div class="row" v-if="item?.quality">
<div class="left side">Quality</div>
<div class="right side" v-text="item.quality" />
</div>
<div class="row" v-if="item?.seeds">
<div class="left side">Seeds</div>
<div class="right side" v-text="item.seeds" />
</div>
<div class="row" v-if="item?.peers">
<div class="left side">Peers</div>
<div class="right side" v-text="item.peers" />
</div>
<div class="row" v-if="item?.language">
<div class="left side">Language</div>
<div class="right side" v-text="item.language" />
</div>
</div>
</template>
<script>
import Utils from "@/Utils";
import MediaUtils from "@/components/Media/Utils";
import MediaImage from "./MediaImage";
export default {
name: "Info",
components: {MediaImage},
mixins: [Utils, MediaUtils],
props: {
item: {
@ -163,6 +155,16 @@ export default {
</script>
<style lang="scss" scoped>
@import "vars";
.media-info {
width: 100%;
@include from($tablet) {
width: 640px;
}
}
.row {
display: flex;
min-height: 3em;
@ -222,4 +224,24 @@ export default {
}
}
}
.header {
width: 100%;
display: flex;
flex-direction: column;
position: relative;
.title {
width: 100%;
font-size: 1.5em;
font-weight: bold;
margin-top: 0.5em;
text-align: center;
overflow-wrap: break-word;
}
&:hover {
background: initial;
}
}
</style>

View file

@ -0,0 +1,97 @@
<template>
<div class="image-container">
<img class="image" :src="item.image" :alt="item.title" v-if="item?.image" />
<div class="image" v-else>
<div class="inner">
<i class="fas fa-play" />
</div>
</div>
<span class="imdb-link" v-if="item?.imdb_id">
<a :href="`https://www.imdb.com/title/${item.imdb_id}`" target="_blank">
<i class="fab fa-imdb" />
</a>
</span>
<span class="duration" v-if="item?.duration != null"
v-text="convertTime(item.duration)" />
</div>
</template>
<script>
import MediaUtils from "@/components/Media/Utils";
export default {
mixins: [MediaUtils],
props: {
item: {
type: Object,
default: () => {},
}
}
}
</script>
<style lang="scss" scoped>
@import "vars";
.imdb-link {
position: absolute;
top: 0;
right: 0;
height: 1em;
font-size: 2em;
&:hover {
color: $default-hover-fg;
}
i {
background: #ffff00;
border-radius: 0.25em;
}
}
.duration {
position: absolute;
bottom: 0;
right: 0;
font-size: 0.9em;
background: rgba(0, 0, 0, 0.5);
color: white;
padding: 0.25em 0.5em;
border-radius: 0.25em;
}
.image-container {
width: 100%;
min-height: 240px;
position: relative;
img {
@include from($tablet) {
height: 480px;
}
}
}
div.image {
height: 400px;
color: $default-media-img-fg;
font-size: 5em;
display: flex;
align-items: center;
justify-content: center;
.inner {
width: calc(100% - 20px);
height: calc(100% - 20px);
min-height: 240px;
background: $default-media-img-bg;
display: flex;
align-items: center;
justify-content: center;
border-radius: 1em;
}
}
</style>

View file

@ -1,5 +1,9 @@
<template>
<nav>
<button class="menu-button mobile" @click="$emit('toggle')">
<i class="fa fa-bars" />
</button>
<li v-for="(view, name) in views" :key="name" :title="view.displayName"
:class="{selected: name === selectedView}" @click="$emit('input', name)">
<i :class="view.iconClass" />
@ -9,8 +13,7 @@
<script>
export default {
name: "Nav",
emits: ['input'],
emits: ['input', 'toggle'],
props: {
selectedView: {
type: String,
@ -57,10 +60,31 @@ nav {
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
box-shadow: 2.5px 0 4.5px 2px $nav-collapsed-fg;
margin-left: 2.5px;
overflow: hidden;
.menu-button {
position: absolute;
top: 0.75em;
left: 0;
width: 100%;
display: inline-flex;
justify-content: center;
padding: 0;
margin: 0;
font-size: 1.2em;
border: 0;
background: none;
cursor: pointer;
z-index: 1;
&:hover {
color: $default-hover-fg;
}
}
li {
display: flex;
align-items: center;

View file

@ -87,6 +87,11 @@ export default {
display: inline-flex;
align-items: center;
&.left {
overflow: hidden;
text-overflow: " [...]";
}
&.right {
justify-content: flex-end;
margin-right: .5em;

View file

@ -1,3 +1,5 @@
$media-header-height: 3.3em;
$media-nav-width: 2.8em;
$filter-header-height: 3em;
$default-media-img-bg: #d0dad8;
$default-media-img-fg: white;