forked from platypush/platypush
[media
UI] Improved media UI.
This commit is contained in:
parent
e7bd61e0d4
commit
3d2dc22ed3
7 changed files with 354 additions and 157 deletions
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="header" :class="{'with-filter': filterVisible}">
|
<div class="header" :class="{'with-filter': filterVisible}">
|
||||||
<div class="row">
|
<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}"
|
<button title="Filter" class="filter-btn" :class="{selected: filterVisible}"
|
||||||
@click="filterVisible = !filterVisible">
|
@click="filterVisible = !filterVisible">
|
||||||
<i class="fa fa-filter" />
|
<i class="fa fa-filter" />
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</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)">
|
<form @submit.prevent="$emit('torrent-add', torrentURL)">
|
||||||
<label class="search-box">
|
<label class="search-box">
|
||||||
<input type="search" placeholder="Add torrent URL" v-model="torrentURL">
|
<input type="search" placeholder="Add torrent URL" v-model="torrentURL">
|
||||||
|
@ -22,14 +22,18 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</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">
|
<label class="search-box">
|
||||||
<input type="search" placeholder="Filter" :value="browserFilter" @change="$emit('filter', $event.target.value)"
|
<input type="search" placeholder="Filter" :value="browserFilter" @change="$emit('filter', $event.target.value)"
|
||||||
@keyup="$emit('filter', $event.target.value)">
|
@keyup="$emit('filter', $event.target.value)">
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</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}"
|
<button title="Select subtitles" class="captions-btn" :class="{selected: selectedSubtitles != null}"
|
||||||
@click="$emit('show-subtitles')" v-if="hasSubtitlesPlugin && selectedItem &&
|
@click="$emit('show-subtitles')" v-if="hasSubtitlesPlugin && selectedItem &&
|
||||||
(selectedItem.type === 'file' || (selectedItem.url || '').startsWith('file://'))">
|
(selectedItem.type === 'file' || (selectedItem.url || '').startsWith('file://'))">
|
||||||
|
@ -59,8 +63,17 @@ import Players from "@/components/panels/Media/Players";
|
||||||
export default {
|
export default {
|
||||||
name: "Header",
|
name: "Header",
|
||||||
components: {Players},
|
components: {Players},
|
||||||
emits: ['search', 'select-player', 'player-status', 'torrent-add', 'show-subtitles', 'play-url', 'filter',
|
emits: [
|
||||||
'source-toggle'],
|
'filter',
|
||||||
|
'play-url',
|
||||||
|
'player-status',
|
||||||
|
'search',
|
||||||
|
'select-player',
|
||||||
|
'show-subtitles',
|
||||||
|
'source-toggle',
|
||||||
|
'toggle-nav',
|
||||||
|
'torrent-add',
|
||||||
|
],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
pluginName: {
|
pluginName: {
|
||||||
|
@ -86,6 +99,10 @@ export default {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showNavButton: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
|
||||||
browserFilter: {
|
browserFilter: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
|
@ -165,9 +182,8 @@ export default {
|
||||||
|
|
||||||
:deep(button) {
|
:deep(button) {
|
||||||
background: none;
|
background: none;
|
||||||
padding: 0 .25em;
|
padding: 0 .5em;
|
||||||
border: 0;
|
border: 0;
|
||||||
margin-right: .25em;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $default-hover-fg-2;
|
color: $default-hover-fg-2;
|
||||||
|
|
|
@ -7,19 +7,30 @@
|
||||||
:buttons="mediaButtons" @play="pause" @pause="pause" @stop="stop" @set-volume="setVolume"
|
:buttons="mediaButtons" @play="pause" @pause="pause" @stop="stop" @set-volume="setVolume"
|
||||||
@seek="seek" @search="search" @mute="toggleMute" @unmute="toggleMute">
|
@seek="seek" @search="search" @mute="toggleMute" @unmute="toggleMute">
|
||||||
<main>
|
<main>
|
||||||
<div class="nav-container">
|
<div class="nav-container from tablet" :style="navContainerStyle">
|
||||||
<Nav :selected-view="selectedView" @input="selectedView = $event" />
|
<Nav :selected-view="selectedView" @input="selectedView = $event"
|
||||||
|
@toggle="forceShowNav = !forceShowNav" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="view-container">
|
<div class="view-container">
|
||||||
<Header :plugin-name="pluginName" :selected-view="selectedView" :has-subtitles-plugin="hasSubtitlesPlugin"
|
<Header :plugin-name="pluginName"
|
||||||
ref="header" :sources="sources" :selected-item="selectedPlayer && selectedPlayer.status &&
|
:selected-view="selectedView"
|
||||||
(selectedPlayer.status.state === 'play' || selectedPlayer.status.state === 'pause')
|
:has-subtitles-plugin="hasSubtitlesPlugin"
|
||||||
? selectedPlayer.status : results[selectedResult]" :selected-subtitles="selectedSubtitles"
|
:sources="sources"
|
||||||
:browser-filter="browserFilter" @search="search" @select-player="selectedPlayer = $event"
|
:selected-item="selectedItem"
|
||||||
@player-status="onStatusUpdate" @torrent-add="downloadTorrent($event)"
|
:selected-subtitles="selectedSubtitles"
|
||||||
@show-subtitles="showSubtitlesModal = !showSubtitlesModal" @play-url="showPlayUrlModal"
|
:browser-filter="browserFilter"
|
||||||
@filter="browserFilter = $event" @source-toggle="sources[$event] = !sources[$event]" />
|
: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}">
|
<div class="body-container" :class="{'expanded-header': $refs.header?.filterVisible}">
|
||||||
<Results :results="results" :selected-result="selectedResult" @select="onResultSelect($event)"
|
<Results :results="results" :selected-result="selectedResult" @select="onResultSelect($event)"
|
||||||
|
@ -120,6 +131,7 @@ export default {
|
||||||
selectedView: 'search',
|
selectedView: 'search',
|
||||||
selectedSubtitles: null,
|
selectedSubtitles: null,
|
||||||
showSubtitlesModal: false,
|
showSubtitlesModal: false,
|
||||||
|
forceShowNav: false,
|
||||||
awaitingPlayTorrent: null,
|
awaitingPlayTorrent: null,
|
||||||
urlPlay: null,
|
urlPlay: null,
|
||||||
browserFilter: null,
|
browserFilter: null,
|
||||||
|
@ -141,6 +153,28 @@ export default {
|
||||||
hasSubtitlesPlugin() {
|
hasSubtitlesPlugin() {
|
||||||
return 'media.subtitles' in this.$root.config
|
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: {
|
methods: {
|
||||||
|
@ -420,12 +454,9 @@ export default {
|
||||||
|
|
||||||
:deep(.media-info-container) {
|
:deep(.media-info-container) {
|
||||||
.modal-container {
|
.modal-container {
|
||||||
.content {
|
|
||||||
max-width: 75%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
padding: 1em .5em;
|
max-width: calc(100vw - 2px);
|
||||||
|
padding: 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="row" v-if="item?.title">
|
<div class="media-info">
|
||||||
<div class="left side">Title</div>
|
<div class="row header">
|
||||||
<div class="right side">
|
<MediaImage :item="item" />
|
||||||
<a :href="`https://www.imdb.com/title/${item.imdb_id}`" target="_blank" v-if="item.imdb_id"
|
|
||||||
v-text="item.title" />
|
<div class="title">
|
||||||
|
<a :href="item.url" target="_blank" v-if="item.url" v-text="item.title" />
|
||||||
<span v-else v-text="item.title" />
|
<span v-else v-text="item.title" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,11 +49,6 @@
|
||||||
<div class="right side" v-text="item.overview" />
|
<div class="right side" v-text="item.overview" />
|
||||||
</div>
|
</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="row" v-if="item?.country">
|
||||||
<div class="left side">Country</div>
|
<div class="left side">Country</div>
|
||||||
<div class="right side" v-text="item.country" />
|
<div class="right side" v-text="item.country" />
|
||||||
|
@ -106,13 +102,6 @@
|
||||||
<div class="right side" v-text="item.file" />
|
<div class="right side" v-text="item.file" />
|
||||||
</div>
|
</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>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row" v-if="item?.trailer">
|
<div class="row" v-if="item?.trailer">
|
||||||
<div class="left side">Trailer</div>
|
<div class="left side">Trailer</div>
|
||||||
<div class="right side url">
|
<div class="right side url">
|
||||||
|
@ -144,14 +133,17 @@
|
||||||
<div class="left side">Language</div>
|
<div class="left side">Language</div>
|
||||||
<div class="right side" v-text="item.language" />
|
<div class="right side" v-text="item.language" />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Utils from "@/Utils";
|
import Utils from "@/Utils";
|
||||||
import MediaUtils from "@/components/Media/Utils";
|
import MediaUtils from "@/components/Media/Utils";
|
||||||
|
import MediaImage from "./MediaImage";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Info",
|
name: "Info",
|
||||||
|
components: {MediaImage},
|
||||||
mixins: [Utils, MediaUtils],
|
mixins: [Utils, MediaUtils],
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
|
@ -163,6 +155,16 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@import "vars";
|
||||||
|
|
||||||
|
.media-info {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@include from($tablet) {
|
||||||
|
width: 640px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 3em;
|
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>
|
</style>
|
||||||
|
|
|
@ -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>
|
|
@ -1,5 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<nav>
|
<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"
|
<li v-for="(view, name) in views" :key="name" :title="view.displayName"
|
||||||
:class="{selected: name === selectedView}" @click="$emit('input', name)">
|
:class="{selected: name === selectedView}" @click="$emit('input', name)">
|
||||||
<i :class="view.iconClass" />
|
<i :class="view.iconClass" />
|
||||||
|
@ -9,8 +13,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "Nav",
|
emits: ['input', 'toggle'],
|
||||||
emits: ['input'],
|
|
||||||
props: {
|
props: {
|
||||||
selectedView: {
|
selectedView: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -57,10 +60,31 @@ nav {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
box-shadow: 2.5px 0 4.5px 2px $nav-collapsed-fg;
|
box-shadow: 2.5px 0 4.5px 2px $nav-collapsed-fg;
|
||||||
margin-left: 2.5px;
|
margin-left: 2.5px;
|
||||||
overflow: hidden;
|
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 {
|
li {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -87,6 +87,11 @@ export default {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: " [...]";
|
||||||
|
}
|
||||||
|
|
||||||
&.right {
|
&.right {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-right: .5em;
|
margin-right: .5em;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
$media-header-height: 3.3em;
|
$media-header-height: 3.3em;
|
||||||
$media-nav-width: 2.8em;
|
$media-nav-width: 2.8em;
|
||||||
$filter-header-height: 3em;
|
$filter-header-height: 3em;
|
||||||
|
$default-media-img-bg: #d0dad8;
|
||||||
|
$default-media-img-fg: white;
|
||||||
|
|
Loading…
Reference in a new issue