forked from platypush/platypush
[media UI] Added support for generic media providers.
This commit is contained in:
parent
be28965d84
commit
60fb7bba5f
9 changed files with 253 additions and 67 deletions
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="browser-container">
|
||||
<div class="browser">
|
||||
<Loading v-if="loading" />
|
||||
|
||||
<div class="nav" ref="nav">
|
||||
|
@ -140,9 +140,7 @@ export default {
|
|||
<style lang="scss" scoped>
|
||||
@import "src/style/items";
|
||||
|
||||
$nav-height: 2.5em;
|
||||
|
||||
.browser-container {
|
||||
.browser {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -154,33 +152,6 @@ $nav-height: 2.5em;
|
|||
}
|
||||
}
|
||||
|
||||
.nav {
|
||||
width: 100%;
|
||||
height: $nav-height;
|
||||
padding: 0.5em 1em;
|
||||
background: $tab-bg;
|
||||
box-shadow: $border-shadow-bottom;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
.path {
|
||||
cursor: pointer;
|
||||
|
||||
.token {
|
||||
&:hover {
|
||||
color: $default-hover-fg;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.separator {
|
||||
font-size: 1em;
|
||||
width: 1.2em;
|
||||
padding: 0 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.items {
|
||||
height: calc(100% - #{$nav-height});
|
||||
overflow: auto;
|
||||
|
|
|
@ -3,36 +3,45 @@
|
|||
<div class="media-browser">
|
||||
<Loading v-if="loading" />
|
||||
|
||||
<div class="media-index grid" v-else-if="!collection">
|
||||
<div class="item" @click="collection = 'files'">
|
||||
<div class="media-index grid" v-else-if="!mediaProvider">
|
||||
<div class="item"
|
||||
v-for="(provider, name) in mediaProviders"
|
||||
:key="name"
|
||||
@click="mediaProvider = provider">
|
||||
<div class="icon">
|
||||
<i class="fas fa-folder"></i>
|
||||
<i v-bind="providersMetadata[name].icon"
|
||||
:style="{ color: providersMetadata[name].icon?.color || 'inherit' }"
|
||||
v-if="providersMetadata[name].icon" />
|
||||
</div>
|
||||
<div class="name">
|
||||
Files
|
||||
{{ providersMetadata[name].name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="media-browser fade-in" v-else>
|
||||
<Browser :is-media="true"
|
||||
:filter="filter"
|
||||
:has-back="true"
|
||||
@back="collection = null"
|
||||
@path-change="$emit('path-change', $event)"
|
||||
@play="$emit('play', $event)"
|
||||
v-if="collection === 'files'" />
|
||||
<div class="media-browser-body" v-else-if="mediaProvider">
|
||||
<component
|
||||
:is="mediaProvider"
|
||||
:filter="filter"
|
||||
ref="mediaProvider"
|
||||
@back="mediaProvider = null"
|
||||
@path-change="$emit('path-change', $event)"
|
||||
@play="$emit('play', $event)" />
|
||||
</div>
|
||||
</div>
|
||||
</keep-alive>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent, shallowRef } from "vue";
|
||||
import Browser from "@/components/File/Browser";
|
||||
import Loading from "@/components/Loading";
|
||||
import Utils from "@/Utils";
|
||||
import providersMetadata from "./Providers/meta.json";
|
||||
|
||||
export default {
|
||||
emits: ['path-change', 'play'],
|
||||
mixins: [Utils],
|
||||
components: {
|
||||
Browser,
|
||||
Loading,
|
||||
|
@ -48,39 +57,46 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
collection: null,
|
||||
mediaProvider: null,
|
||||
mediaProviders: {},
|
||||
providersMetadata: providersMetadata,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
getMediaProviderComponent(type) {
|
||||
return shallowRef(
|
||||
defineAsyncComponent(
|
||||
() => import(`@/components/panels/Media/Providers/${type}`)
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
async refreshMediaProviders() {
|
||||
const config = await this.request('config.get')
|
||||
this.mediaProviders = {}
|
||||
// The local File provider is always enabled
|
||||
this.mediaProviders['File'] = this.getMediaProviderComponent('File')
|
||||
|
||||
if (config.youtube)
|
||||
this.mediaProviders['YouTube'] = this.getMediaProviderComponent('YouTube')
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.refreshMediaProviders()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
|
||||
.media-browser {
|
||||
height: 100%;
|
||||
|
||||
.item {
|
||||
height: 100px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: $default-border-2;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: $hover-bg;
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 60%;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
opacity: 0.5;
|
||||
|
||||
i {
|
||||
font-size: 40px;
|
||||
}
|
||||
}
|
||||
.media-browser-body {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
<span class="channel-name" v-text="item.channel" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="row creation-date" v-if="item.created_at">
|
||||
{{ formatDateTime(item.created_at, true) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -35,9 +39,11 @@ import Dropdown from "@/components/elements/Dropdown";
|
|||
import DropdownItem from "@/components/elements/DropdownItem";
|
||||
import Icons from "./icons.json";
|
||||
import MediaImage from "./MediaImage";
|
||||
import Utils from "@/Utils";
|
||||
|
||||
export default {
|
||||
components: {Dropdown, DropdownItem, MediaImage},
|
||||
mixins: [Utils],
|
||||
emits: ['play', 'select', 'view', 'download'],
|
||||
props: {
|
||||
item: {
|
||||
|
@ -189,5 +195,11 @@ export default {
|
|||
border-radius: 50%;
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
.creation-date {
|
||||
font-size: .85em;
|
||||
color: $default-fg-2;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<template>
|
||||
<div class="media-file-browser">
|
||||
<Loading v-if="loading" />
|
||||
<Browser :is-media="true"
|
||||
:filter="filter"
|
||||
:has-back="true"
|
||||
@back="$emit('back')"
|
||||
@path-change="$emit('path-change', $event)"
|
||||
@play="$emit('play', $event)"
|
||||
v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Browser from "@/components/File/Browser";
|
||||
import Loading from "@/components/Loading";
|
||||
import MediaProvider from "./Mixin";
|
||||
|
||||
export default {
|
||||
mixins: [MediaProvider],
|
||||
components: {
|
||||
Browser,
|
||||
Loading,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.media-file-browser {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,20 @@
|
|||
<script>
|
||||
import Utils from "@/Utils";
|
||||
|
||||
export default {
|
||||
emits: ['back', 'path-change', 'play'],
|
||||
mixins: [Utils],
|
||||
props: {
|
||||
filter: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<div class="nav">
|
||||
<span class="path">
|
||||
<span class="back token" title="Back" @click="$emit('back')">
|
||||
<i class="fas fa-home" />
|
||||
</span>
|
||||
|
||||
<span class="separator">
|
||||
<i class="fas fa-chevron-right" />
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="path" v-for="(token, index) in path" :key="index">
|
||||
<span class="token" :title="token.title" @click="onClick(token)">
|
||||
<i class="icon" :class="icon" v-if="icon = token.icon?.['class']" />
|
||||
<span v-if="token.title">{{ token.title }}</span>
|
||||
</span>
|
||||
|
||||
<span class="separator"
|
||||
v-if="(index > 0 || path.length > 1) && index < path.length - 1">
|
||||
<i class="fas fa-chevron-right" />
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
emit: ['back'],
|
||||
|
||||
props: {
|
||||
path: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
onClick(token) {
|
||||
if (token.click)
|
||||
token.click()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../style.scss";
|
||||
|
||||
.nav {
|
||||
.path .token .icon {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"File": {
|
||||
"name": "Files",
|
||||
"icon": {
|
||||
"class": "fas fa-folder",
|
||||
"color": "#888888"
|
||||
}
|
||||
},
|
||||
|
||||
"YouTube": {
|
||||
"name": "YouTube",
|
||||
"icon": {
|
||||
"class": "fab fa-youtube",
|
||||
"color": "#FF0000"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
.grid {
|
||||
:deep(.item) {
|
||||
height: 100px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: $default-border-2;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: $hover-bg;
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 60%;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
|
||||
i {
|
||||
font-size: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$media-nav-height: 2.5em;
|
||||
|
||||
:deep(.nav) {
|
||||
height: $media-nav-height;
|
||||
}
|
|
@ -71,3 +71,35 @@ body {
|
|||
scrollbar-color: $scrollbar-thumb-bg $scrollbar-track-bg;
|
||||
}
|
||||
|
||||
// Browser navigator layout
|
||||
$nav-height: 2.5em;
|
||||
|
||||
.browser {
|
||||
:deep(.nav) {
|
||||
width: 100%;
|
||||
height: $nav-height;
|
||||
padding: 0.5em 1em;
|
||||
background: $tab-bg;
|
||||
box-shadow: $border-shadow-bottom;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
.path {
|
||||
cursor: pointer;
|
||||
|
||||
.token {
|
||||
&:hover {
|
||||
color: $default-hover-fg;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.separator {
|
||||
font-size: 1em;
|
||||
width: 1.2em;
|
||||
padding: 0 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue