mirror of
https://github.com/jellyfin/jellyfin-vue.git
synced 2026-09-03 05:10:26 +03:00
feat(details): redesign item details
This commit is contained in:
@@ -2,3 +2,8 @@
|
||||
//
|
||||
// The variables you want to modify
|
||||
// $font-size-root: 20px;
|
||||
|
||||
// Globals
|
||||
$body-font-family: 'Noto Sans',
|
||||
'Roboto',
|
||||
sans-serif !default;
|
||||
|
||||
+27
-8
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<nuxt-link :to="itemLink" style="text-decoration: none; color: inherit">
|
||||
<div class="card-box">
|
||||
<div class="card-box" :class="{ 'card-margin': !noMargin }">
|
||||
<div :class="shape || cardType" class="elevation-3">
|
||||
<div
|
||||
class="card-content card-content-button d-flex justify-center align-center primary darken-4"
|
||||
@@ -46,15 +46,18 @@
|
||||
class="align-self-end"
|
||||
/>
|
||||
</div>
|
||||
<div class="card-overlay d-flex justify-center align-center">
|
||||
<div
|
||||
v-if="overlay"
|
||||
class="card-overlay d-flex justify-center align-center"
|
||||
>
|
||||
<v-btn fab color="primary" :to="`/item/${item.Id}/play`">
|
||||
<v-icon size="36">mdi-play</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-text">
|
||||
<div v-if="!noText" class="card-text">
|
||||
<div class="card-title mt-1">{{ item.Name }}</div>
|
||||
<div class="card-subtitle grey--text">{{ cardSubtitle }}</div>
|
||||
<div class="card-subtitle text--secondary">{{ cardSubtitle }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</nuxt-link>
|
||||
@@ -74,14 +77,30 @@ export default Vue.extend({
|
||||
},
|
||||
shape: {
|
||||
type: [String, Boolean],
|
||||
required: false,
|
||||
default: () => {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
episode: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: () => {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
overlay: {
|
||||
type: Boolean,
|
||||
default: () => {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
noText: {
|
||||
type: Boolean,
|
||||
default: () => {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
noMargin: {
|
||||
type: Boolean,
|
||||
default: () => {
|
||||
return false;
|
||||
}
|
||||
@@ -187,8 +206,9 @@ export default Vue.extend({
|
||||
.card-box {
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
.card-margin {
|
||||
margin: 0.6em;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.portrait-card {
|
||||
position: relative;
|
||||
@@ -235,7 +255,6 @@ export default Vue.extend({
|
||||
right: 1em;
|
||||
}
|
||||
.card-overlay {
|
||||
border-radius: 0.3em;
|
||||
position: absolute;
|
||||
background: radial-gradient(
|
||||
farthest-corner at 50% 50%,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<v-col v-show="items.length > 0" class="home-section">
|
||||
<h1 class="text-h5">
|
||||
<h1 class="text-h5 font-weight-light header">
|
||||
<span>{{ section.name }}</span>
|
||||
</h1>
|
||||
|
||||
@@ -132,6 +132,20 @@ h1 {
|
||||
margin-left: 0.4em;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
||||
.home-section .header span {
|
||||
padding-left: 0.25em;
|
||||
}
|
||||
.home-section .header::before {
|
||||
background-color: white;
|
||||
content: '';
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: 1px;
|
||||
bottom: 0.3em;
|
||||
left: 0;
|
||||
width: 1.25em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="text--secondary">
|
||||
<span v-if="item.ProductionYear && year">{{ item.ProductionYear }}</span>
|
||||
<span v-if="item.OfficialRating && rating">{{ item.OfficialRating }}</span>
|
||||
<span v-if="item.CommunityRating && rating"
|
||||
><v-icon class="rating-icon" size="16">mdi-star</v-icon>
|
||||
{{ item.CommunityRating }}</span
|
||||
>
|
||||
<span v-if="item.RunTimeTicks && runtime">{{ runtimeValue }}</span>
|
||||
<span v-if="item.RunTimeTicks && endsAt">{{
|
||||
$t('endsAt', {
|
||||
time: endsAtValue
|
||||
})
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { formatDuration, intervalToDuration } from 'date-fns';
|
||||
import { BaseItemDto } from '~/api';
|
||||
import timeUtils from '~/mixins/timeUtils';
|
||||
|
||||
export default Vue.extend({
|
||||
mixins: [timeUtils],
|
||||
props: {
|
||||
item: {
|
||||
type: Object as () => BaseItemDto,
|
||||
required: true
|
||||
},
|
||||
year: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
rating: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
runtime: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
endsAt: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
runtimeValue: {
|
||||
get() {
|
||||
const seconds = this.ticksToMs(this.item.RunTimeTicks);
|
||||
return formatDuration(intervalToDuration({ start: 0, end: seconds }), {
|
||||
format: ['hours', 'minutes']
|
||||
});
|
||||
}
|
||||
},
|
||||
endsAtValue: {
|
||||
get() {
|
||||
const seconds = this.ticksToMs(this.item.RunTimeTicks);
|
||||
return this.$dateFns.format(Date.now() + seconds, 'p');
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getEndsAtTime(ticks: number): string {
|
||||
const ms = this.ticksToMs(ticks);
|
||||
const endTimeLong = new Date(Date.now() + ms);
|
||||
// TODO: Respect user locale when rendering time
|
||||
const endTimeShort = endTimeLong.toLocaleString(this.$i18n.locale, {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric'
|
||||
});
|
||||
|
||||
// TODO: Use a Date object
|
||||
return this.$t('endsAt', {
|
||||
time: endTimeShort
|
||||
}).toString();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
span {
|
||||
margin-left: 0.6em;
|
||||
margin-right: 0.6em;
|
||||
}
|
||||
span:first-of-type {
|
||||
margin-left: 0;
|
||||
}
|
||||
span:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
.rating-icon {
|
||||
opacity: 0.6;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<v-list color="transparent" two-line>
|
||||
<div v-if="items.length > 0">
|
||||
<v-list-item
|
||||
v-for="item in items"
|
||||
:key="item.Id"
|
||||
nuxt
|
||||
:to="`/person/${item.Id}`"
|
||||
>
|
||||
<v-list-item-avatar>
|
||||
<v-img
|
||||
v-if="item.PrimaryImageTag"
|
||||
:src="`${$axios.defaults.baseURL}/Items/${item.Id}/Images/Primary`"
|
||||
/>
|
||||
<v-icon class="grey darken-3">mdi-account</v-icon>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{ item.Name }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{
|
||||
item.Role || item.Type
|
||||
}}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</div>
|
||||
<div
|
||||
v-for="index in skeletonLength"
|
||||
v-else
|
||||
:key="index"
|
||||
class="d-flex align-center mt-5 mb-5"
|
||||
>
|
||||
<v-skeleton-loader type="avatar" class="ml-3 mr-3" />
|
||||
<v-skeleton-loader type="sentences" width="10em" class="pr-5" />
|
||||
</div>
|
||||
</v-list>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
skeletonLength: {
|
||||
type: Number,
|
||||
default: 3
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
+92
-32
@@ -1,36 +1,72 @@
|
||||
<template>
|
||||
<v-col v-if="relatedItems" class="related-items">
|
||||
<h1 class="text-h5">
|
||||
<span>{{ $t('moreLikeThis') }}</span>
|
||||
</h1>
|
||||
<vueper-slides
|
||||
:bullets="false"
|
||||
:bullets-outside="false"
|
||||
:arrows-outside="false"
|
||||
:visible-slides="8"
|
||||
:slide-multiple="true"
|
||||
:breakpoints="breakpoints"
|
||||
fixed-height="true"
|
||||
>
|
||||
<vueper-slide v-for="item in relatedItems" :key="item.Id">
|
||||
<template v-slot:content>
|
||||
<card :item="item" />
|
||||
<div v-if="relatedItems.length > 0">
|
||||
<div v-if="!vertical" class="related-items">
|
||||
<h1 class="text-h5 mb-2 ml-2 header">
|
||||
<span>{{ $t('youMayAlsoLike') }}</span>
|
||||
</h1>
|
||||
<vueper-slides
|
||||
:bullets="false"
|
||||
:bullets-outside="false"
|
||||
:arrows-outside="false"
|
||||
:visible-slides="6"
|
||||
:slide-multiple="true"
|
||||
:breakpoints="breakpoints"
|
||||
fixed-height="true"
|
||||
>
|
||||
<vueper-slide v-for="item in relatedItems" :key="item.Id">
|
||||
<template v-slot:content>
|
||||
<card :item="item" />
|
||||
</template>
|
||||
</vueper-slide>
|
||||
|
||||
<template v-slot:arrow-left>
|
||||
<v-btn icon large>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</vueper-slide>
|
||||
|
||||
<template v-slot:arrow-left>
|
||||
<v-btn icon large>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<template v-slot:arrow-right>
|
||||
<v-btn icon large>
|
||||
<v-icon>mdi-arrow-right</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</vueper-slides>
|
||||
</v-col>
|
||||
<template v-slot:arrow-right>
|
||||
<v-btn icon large>
|
||||
<v-icon>mdi-arrow-right</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</vueper-slides>
|
||||
</div>
|
||||
<div v-if="vertical">
|
||||
<h2 v-if="relatedItems.length > 0">{{ $t('youMayAlsoLike') }}</h2>
|
||||
<v-skeleton-loader v-else type="heading" />
|
||||
<v-list color="transparent" two-line>
|
||||
<div v-if="relatedItems.length > 0">
|
||||
<v-list-item
|
||||
v-for="relatedItem in relatedItems"
|
||||
:key="relatedItem.Id"
|
||||
nuxt
|
||||
:to="`/item/${relatedItem.Id}`"
|
||||
>
|
||||
<v-list-item-avatar>
|
||||
<v-img
|
||||
:src="`${$axios.defaults.baseURL}/Items/${relatedItem.Id}/Images/Primary`"
|
||||
/>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{ relatedItem.Name }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{
|
||||
relatedItem.ProductionYear
|
||||
}}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</div>
|
||||
<div
|
||||
v-for="index in skeletonLength"
|
||||
:key="index"
|
||||
class="d-flex align-center mt-5 mb-5"
|
||||
>
|
||||
<v-skeleton-loader type="avatar" class="ml-3 mr-3" />
|
||||
<v-skeleton-loader type="sentences" width="10em" class="pr-5" />
|
||||
</div>
|
||||
</v-list>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -47,6 +83,14 @@ export default Vue.extend({
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
vertical: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
skeletonLength: {
|
||||
type: Number,
|
||||
default: 5
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -67,7 +111,7 @@ export default Vue.extend({
|
||||
visibleSlides: 6
|
||||
},
|
||||
1904: {
|
||||
visibleSlides: 8
|
||||
visibleSlides: 6
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -81,7 +125,7 @@ export default Vue.extend({
|
||||
await this.$api.library.getSimilarItems({
|
||||
itemId: this.id,
|
||||
userId: this.$auth.user.Id,
|
||||
limit: 16
|
||||
limit: this.vertical ? 5 : 12
|
||||
})
|
||||
).data.Items as BaseItemDto[];
|
||||
|
||||
@@ -93,6 +137,22 @@ export default Vue.extend({
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header span {
|
||||
padding-left: 0.25em;
|
||||
}
|
||||
.header::before {
|
||||
background-color: white;
|
||||
content: '';
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: 1px;
|
||||
bottom: 0.3em;
|
||||
left: 0;
|
||||
width: 1.25em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.related-items .vueperslides__track {
|
||||
position: relative;
|
||||
|
||||
+30
-29
@@ -1,29 +1,33 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-tabs v-model="seasonTabs" class="mb-3">
|
||||
<v-tabs v-model="currentTab" class="mb-3" background-color="transparent">
|
||||
<v-tab v-for="season in seasons" :key="season.Id">
|
||||
{{ season.Name }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<v-tabs-items v-model="seasonTabs">
|
||||
<v-tabs-items v-model="currentTab" class="transparent">
|
||||
<v-tab-item v-for="season in seasons" :key="season.Id">
|
||||
<vueper-slides
|
||||
:visible-slides="3"
|
||||
:arrows-outside="false"
|
||||
slide-multiple
|
||||
:gap="2"
|
||||
:infinite="false"
|
||||
:disable-arrows-on-edges="true"
|
||||
:bullets="false"
|
||||
:breakpoints="breakpoints"
|
||||
:dragging-distance="200"
|
||||
>
|
||||
<vueper-slide v-for="episode in season.Episodes" :key="episode.Id">
|
||||
<template v-slot:content>
|
||||
<card :item="episode" episode />
|
||||
</template>
|
||||
</vueper-slide>
|
||||
</vueper-slides>
|
||||
<v-list two-line color="transparent">
|
||||
<v-list-item
|
||||
v-for="episode in seasonEpisodes[currentTab]"
|
||||
:key="episode.Id"
|
||||
nuxt
|
||||
:to="`/item/${episode.Id}/play`"
|
||||
>
|
||||
<v-list-item-avatar tile width="20em" height="12em">
|
||||
<blurhash-image
|
||||
v-if="episode.ImageTags && episode.ImageTags.Primary"
|
||||
:item="episode"
|
||||
/>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{ episode.Name }} </v-list-item-title>
|
||||
<v-list-item-subtitle>{{
|
||||
episode.Overview
|
||||
}}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</div>
|
||||
@@ -33,10 +37,6 @@
|
||||
import Vue from 'vue';
|
||||
import { BaseItemDto } from '~/api';
|
||||
|
||||
interface Season extends BaseItemDto {
|
||||
Episodes?: Array<BaseItemDto>;
|
||||
}
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
item: {
|
||||
@@ -46,8 +46,9 @@ export default Vue.extend({
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
seasons: [] as Season[],
|
||||
seasonTabs: 1,
|
||||
currentTab: 0,
|
||||
seasons: [] as BaseItemDto[],
|
||||
seasonEpisodes: [] as Array<BaseItemDto[]>,
|
||||
breakpoints: {
|
||||
600: {
|
||||
visibleSlides: 2
|
||||
@@ -74,19 +75,19 @@ export default Vue.extend({
|
||||
|
||||
this.seasons = seasons;
|
||||
|
||||
// TODO: Lazy load season episodes when clicking on a tab
|
||||
for (const season of this.seasons) {
|
||||
const episodes = (
|
||||
await this.$api.items.getItems({
|
||||
uId: this.$auth.user.Id,
|
||||
userId: this.$auth.user.Id,
|
||||
parentId: season.Id
|
||||
parentId: season.Id,
|
||||
fields: 'Overview'
|
||||
})
|
||||
).data.Items as BaseItemDto[];
|
||||
|
||||
season.Episodes = episodes;
|
||||
this.seasonEpisodes.push(episodes);
|
||||
}
|
||||
|
||||
this.seasonTabs = 0;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<template>
|
||||
<div class="skeleton-card">
|
||||
<v-skeleton-loader type="image" :boilerplate="boilerplate" />
|
||||
<v-skeleton-loader type="heading" class="mt-1" :boilerplate="boilerplate" />
|
||||
<v-skeleton-loader
|
||||
v-if="!noText"
|
||||
type="heading"
|
||||
class="mt-1"
|
||||
:boilerplate="boilerplate"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -13,6 +18,10 @@ export default Vue.extend({
|
||||
boilerplate: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
noText: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<v-img
|
||||
v-if="userImage"
|
||||
:src="userImage"
|
||||
:alt="$auth.user.name"
|
||||
:alt="$auth.user.Name"
|
||||
></v-img>
|
||||
<v-icon v-else dark>mdi-account</v-icon>
|
||||
</v-avatar>
|
||||
|
||||
+60
-14
@@ -1,7 +1,26 @@
|
||||
<template>
|
||||
<v-app ref="app">
|
||||
<backdrop />
|
||||
<v-navigation-drawer v-model="drawer" :clipped="clipped" fixed app>
|
||||
<v-navigation-drawer
|
||||
v-model="drawer"
|
||||
:temporary="$vuetify.breakpoint.mobile"
|
||||
app
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<div class="d-flex align-center full-width pa-6">
|
||||
<v-avatar size="48" color="primary" class="mr-4">
|
||||
<img
|
||||
v-if="$auth.user.PrimaryImageTag"
|
||||
:src="`${$axios.defaults.baseURL}/Users/${$auth.user.Id}/Images/Primary/?tag=${$auth.user.PrimaryImageTag}&maxWidth=64`"
|
||||
/>
|
||||
<span class="white--text">{{ $auth.user.Name.charAt(0) }}</span>
|
||||
</v-avatar>
|
||||
<h1 class="font-weight-light">
|
||||
{{ $auth.user.Name }}
|
||||
</h1>
|
||||
</div>
|
||||
<v-divider></v-divider>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-for="(item, i) in items"
|
||||
@@ -54,20 +73,37 @@
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
<v-app-bar
|
||||
:clipped-left="clipped"
|
||||
:hide-on-scroll="$vuetify.breakpoint.mobile"
|
||||
:color="$vuetify.theme.dark ? '#202020' : undefined"
|
||||
fixed
|
||||
:clipped-left="$vuetify.breakpoint.mobile"
|
||||
class="pl-2 pr-2"
|
||||
flat
|
||||
app
|
||||
>
|
||||
<v-app-bar-nav-icon @click.stop="drawer = !drawer" />
|
||||
<v-btn v-if="$route.name !== 'index'" icon @click="$router.back()">
|
||||
<v-app-bar-nav-icon
|
||||
v-if="$vuetify.breakpoint.mobile"
|
||||
@click.stop="drawer = !drawer"
|
||||
/>
|
||||
<v-btn
|
||||
v-if="$route.name !== 'index'"
|
||||
class="mr-2"
|
||||
icon
|
||||
@click="$router.back()"
|
||||
>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
<v-toolbar-title v-text="title" />
|
||||
<v-text-field
|
||||
class="search-input"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
placeholder="Search"
|
||||
max-width="15em"
|
||||
dense
|
||||
outlined
|
||||
filled
|
||||
flat
|
||||
hide-details
|
||||
single-line
|
||||
/>
|
||||
<v-spacer />
|
||||
<locale-switcher />
|
||||
<locale-switcher class="mr-2" />
|
||||
<user-button v-if="$auth.loggedIn" />
|
||||
</v-app-bar>
|
||||
<v-main>
|
||||
@@ -83,15 +119,11 @@ import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
clipped: true,
|
||||
drawer: true,
|
||||
miniVariant: false
|
||||
opacity: 0
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.$store.state.page.title;
|
||||
},
|
||||
items() {
|
||||
return [
|
||||
{
|
||||
@@ -124,4 +156,18 @@ export default Vue.extend({
|
||||
.v-application {
|
||||
background-color: var(--v-background-base) !important;
|
||||
}
|
||||
|
||||
.v-app-bar:not(.v-app-bar--is-scrolled) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.v-app-bar .v-app-bar--is-scrolled {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
background-color: rgba(32, 32, 32, 1) !important;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
max-width: 15em;
|
||||
}
|
||||
</style>
|
||||
|
||||
+34
-32
@@ -1,34 +1,36 @@
|
||||
{
|
||||
"badRequest": "Špatný požadavek. Zkuste to znovu",
|
||||
"continueListening": "Pokračovat v poslechu",
|
||||
"continueWatching": "Pokračovat ve sledování",
|
||||
"episodeNumber": "Epizoda {episodeNumber}",
|
||||
"home": "Domů",
|
||||
"incorrectUsernameOrPassword": "Nesprávné uživatelské jméno nebo heslo",
|
||||
"login": "Přihlášení",
|
||||
"logout": "Odhlášení",
|
||||
"more": "Více",
|
||||
"play": "Přehrát",
|
||||
"present": "Prezentovat",
|
||||
"serverAddressMustBeUrl": "Adresa serveru musí být platné URL",
|
||||
"serverAddressRequired": "Adresa serveru je povinná",
|
||||
"serverNotFound": "Server nebyl nalezen",
|
||||
"settings": "Nastavení",
|
||||
"unexpectedError": "Neočekávaná chyba",
|
||||
"usernameRequired": "Uživatelské jméno je povinné",
|
||||
"serverAddress": "Adresa serveru",
|
||||
"username": "Uživatelské jméno",
|
||||
"password": "Heslo",
|
||||
"playType": "Přehrát {mediaType}",
|
||||
"signIn": "Přihlásit se",
|
||||
"upNext": "Následující",
|
||||
"libraryNotFound": "Knihovna nebyla nalezena",
|
||||
"itemNotFound": "Položka nebyla nalezena",
|
||||
"endsAt": "Končí v {time}",
|
||||
"moreLikeThis": "Podobné",
|
||||
"libraryEmpty": "Knihovna je prázdná",
|
||||
"rating": "Hodnocení",
|
||||
"endDate": "Datum skončení",
|
||||
"releaseDate": "Datum vydání",
|
||||
"alphabetically": "Abecedně"
|
||||
"alphabetically": "Abecedně",
|
||||
"badRequest": "Špatný požadavek. Zkuste to znovu",
|
||||
"continueListening": "Pokračovat v poslechu",
|
||||
"continueWatching": "Pokračovat ve sledování",
|
||||
"endDate": "Datum skončení",
|
||||
"endsAt": "Končí v {time}",
|
||||
"episodeNumber": "Epizoda {episodeNumber}",
|
||||
"home": "Domů",
|
||||
"incorrectUsernameOrPassword": "Nesprávné uživatelské jméno nebo heslo",
|
||||
"itemNotFound": "Položka nebyla nalezena",
|
||||
"libraryEmpty": "Knihovna je prázdná",
|
||||
"libraryNotFound": "Knihovna nebyla nalezena",
|
||||
"login": "Přihlášení",
|
||||
"logout": "Odhlášení",
|
||||
"more": "Více",
|
||||
"movies": "",
|
||||
"password": "Heslo",
|
||||
"play": "Přehrát",
|
||||
"playType": "Přehrát {mediaType}",
|
||||
"present": "Prezentovat",
|
||||
"rating": "Hodnocení",
|
||||
"releaseDate": "Datum vydání",
|
||||
"serverAddress": "Adresa serveru",
|
||||
"serverAddressMustBeUrl": "Adresa serveru musí být platné URL",
|
||||
"serverAddressRequired": "Adresa serveru je povinná",
|
||||
"serverNotFound": "Server nebyl nalezen",
|
||||
"settings": "Nastavení",
|
||||
"signIn": "Přihlásit se",
|
||||
"unexpectedError": "Neočekávaná chyba",
|
||||
"upNext": "Následující",
|
||||
"username": "Uživatelské jméno",
|
||||
"usernameRequired": "Uživatelské jméno je povinné",
|
||||
"youMayAlsoLike": "",
|
||||
"youMightLike": ""
|
||||
}
|
||||
|
||||
+34
-32
@@ -1,34 +1,36 @@
|
||||
{
|
||||
"badRequest": "Fehlerhafte Anfrage. Erneut versuchen",
|
||||
"continueListening": "Weiter zuhören",
|
||||
"continueWatching": "Weiter anschauen",
|
||||
"home": "Startseite",
|
||||
"incorrectUsernameOrPassword": "Ungültiger Benutzername oder Passwort",
|
||||
"login": "Anmelden",
|
||||
"logout": "Abmelden",
|
||||
"more": "Mehr",
|
||||
"play": "Abspielen",
|
||||
"serverAddressMustBeUrl": "Serveradresse muss eine gültige URL sein",
|
||||
"serverAddressRequired": "Serveradresse ist erforderlich",
|
||||
"serverNotFound": "Server nicht gefunden",
|
||||
"settings": "Einstellungen",
|
||||
"unexpectedError": "Unerwarteter Fehler",
|
||||
"upNext": "Als nächstes",
|
||||
"usernameRequired": "Benutzername erforderlich",
|
||||
"signIn": "Anmelden",
|
||||
"password": "Passwort",
|
||||
"username": "Benutzername",
|
||||
"serverAddress": "Serveradresse",
|
||||
"itemNotFound": "Element nicht gefunden",
|
||||
"libraryNotFound": "Bibliothek nicht gefunden",
|
||||
"episodeNumber": "Episode {episodeNumber}",
|
||||
"playType": "{mediaType} abspielen",
|
||||
"moreLikeThis": "Ähnliches",
|
||||
"present": "Jetzt",
|
||||
"endsAt": "Endet um {time}",
|
||||
"libraryEmpty": "Diese Bibliothek ist leer",
|
||||
"rating": "Bewertung",
|
||||
"endDate": "Enddatum",
|
||||
"releaseDate": "Erscheinungsdatum",
|
||||
"alphabetically": "Alphabetisch"
|
||||
"alphabetically": "Alphabetisch",
|
||||
"badRequest": "Fehlerhafte Anfrage. Erneut versuchen",
|
||||
"continueListening": "Weiter zuhören",
|
||||
"continueWatching": "Weiter anschauen",
|
||||
"endDate": "Enddatum",
|
||||
"endsAt": "Endet um {time}",
|
||||
"episodeNumber": "Episode {episodeNumber}",
|
||||
"home": "Startseite",
|
||||
"incorrectUsernameOrPassword": "Ungültiger Benutzername oder Passwort",
|
||||
"itemNotFound": "Element nicht gefunden",
|
||||
"libraryEmpty": "Diese Bibliothek ist leer",
|
||||
"libraryNotFound": "Bibliothek nicht gefunden",
|
||||
"login": "Anmelden",
|
||||
"logout": "Abmelden",
|
||||
"more": "Mehr",
|
||||
"movies": "",
|
||||
"password": "Passwort",
|
||||
"play": "Abspielen",
|
||||
"playType": "{mediaType} abspielen",
|
||||
"present": "Jetzt",
|
||||
"rating": "Bewertung",
|
||||
"releaseDate": "Erscheinungsdatum",
|
||||
"serverAddress": "Serveradresse",
|
||||
"serverAddressMustBeUrl": "Serveradresse muss eine gültige URL sein",
|
||||
"serverAddressRequired": "Serveradresse ist erforderlich",
|
||||
"serverNotFound": "Server nicht gefunden",
|
||||
"settings": "Einstellungen",
|
||||
"signIn": "Anmelden",
|
||||
"unexpectedError": "Unerwarteter Fehler",
|
||||
"upNext": "Als nächstes",
|
||||
"username": "Benutzername",
|
||||
"usernameRequired": "Benutzername erforderlich",
|
||||
"youMayAlsoLike": "",
|
||||
"youMightLike": ""
|
||||
}
|
||||
|
||||
+4
-1
@@ -16,6 +16,7 @@
|
||||
"logout": "Logout",
|
||||
"more": "More",
|
||||
"moreLikeThis": "More like this",
|
||||
"movies": "Movies",
|
||||
"password": "Password",
|
||||
"play": "Play",
|
||||
"playType": "Play {mediaType}",
|
||||
@@ -27,9 +28,11 @@
|
||||
"serverAddressRequired": "Server address is required",
|
||||
"serverNotFound": "Server not found",
|
||||
"settings": "Settings",
|
||||
"shows": "Shows",
|
||||
"signIn": "Sign in",
|
||||
"unexpectedError": "Unexpected error",
|
||||
"upNext": "Up next",
|
||||
"username": "Username",
|
||||
"usernameRequired": "Username is required"
|
||||
"usernameRequired": "Username is required",
|
||||
"youMayAlsoLike": "You may also like"
|
||||
}
|
||||
|
||||
+32
-32
@@ -1,34 +1,34 @@
|
||||
{
|
||||
"alphabetically": "Alfabéticamente",
|
||||
"releaseDate": "Fecha de lanzamiento",
|
||||
"endDate": "Fecha de finalización",
|
||||
"rating": "Valoración",
|
||||
"badRequest": "Solicitud incorrecta. Intente denuevo",
|
||||
"continueListening": "Seguir escuchando",
|
||||
"continueWatching": "Seguir viendo",
|
||||
"episodeNumber": "Episodio {episodeNumber}",
|
||||
"endsAt": "Termina a las {time}",
|
||||
"home": "Inicio",
|
||||
"incorrectUsernameOrPassword": "Nombre de usuario o contraseña incorrectos",
|
||||
"login": "Iniciar sesión",
|
||||
"logout": "Cerrar sesión",
|
||||
"more": "Más",
|
||||
"moreLikeThis": "Similar a esto",
|
||||
"play": "Reproducir",
|
||||
"present": "Actualidad",
|
||||
"serverAddressMustBeUrl": "La dirección del servidor debe ser una URL válida",
|
||||
"serverAddressRequired": "Se requiere dirección del servidor",
|
||||
"serverNotFound": "No se ha podido encontrar el servidor",
|
||||
"settings": "Ajustes",
|
||||
"unexpectedError": "Error desconocido",
|
||||
"usernameRequired": "Se requiere nombre de usuario",
|
||||
"serverAddress": "Dirección del servidor",
|
||||
"username": "Nombre de usuario",
|
||||
"password": "Contraseña",
|
||||
"playType": "Reproducir {mediaType}",
|
||||
"signIn": "Iniciar sesión",
|
||||
"upNext": "A continuación",
|
||||
"libraryNotFound": "Librería no encontrada",
|
||||
"itemNotFound": "Articulo no encontrado",
|
||||
"libraryEmpty": "Esta librería esta vacía"
|
||||
"alphabetically": "Alfabéticamente",
|
||||
"releaseDate": "Fecha de lanzamiento",
|
||||
"endDate": "Fecha de finalización",
|
||||
"rating": "Valoración",
|
||||
"badRequest": "Solicitud incorrecta. Intente denuevo",
|
||||
"continueListening": "Seguir escuchando",
|
||||
"continueWatching": "Seguir viendo",
|
||||
"episodeNumber": "Episodio {episodeNumber}",
|
||||
"endsAt": "Termina a las {time}",
|
||||
"home": "Inicio",
|
||||
"incorrectUsernameOrPassword": "Nombre de usuario o contraseña incorrectos",
|
||||
"login": "Iniciar sesión",
|
||||
"logout": "Cerrar sesión",
|
||||
"more": "Más",
|
||||
"moreLikeThis": "Similar a esto",
|
||||
"play": "Reproducir",
|
||||
"present": "Actualidad",
|
||||
"serverAddressMustBeUrl": "La dirección del servidor debe ser una URL válida",
|
||||
"serverAddressRequired": "Se requiere dirección del servidor",
|
||||
"serverNotFound": "No se ha podido encontrar el servidor",
|
||||
"settings": "Ajustes",
|
||||
"unexpectedError": "Error desconocido",
|
||||
"usernameRequired": "Se requiere nombre de usuario",
|
||||
"serverAddress": "Dirección del servidor",
|
||||
"username": "Nombre de usuario",
|
||||
"password": "Contraseña",
|
||||
"playType": "Reproducir {mediaType}",
|
||||
"signIn": "Iniciar sesión",
|
||||
"upNext": "A continuación",
|
||||
"libraryNotFound": "Librería no encontrada",
|
||||
"itemNotFound": "Articulo no encontrado",
|
||||
"libraryEmpty": "Esta librería esta vacía"
|
||||
}
|
||||
|
||||
+32
-32
@@ -1,34 +1,34 @@
|
||||
{
|
||||
"badRequest": "Solicitud incorrecta. Reintente",
|
||||
"continueListening": "Continuar escuchando",
|
||||
"continueWatching": "Continuar viendo",
|
||||
"episodeNumber": "Episodio {episodeNumber}",
|
||||
"home": "Inicio",
|
||||
"incorrectUsernameOrPassword": "Usuario o contraseña incorrecta",
|
||||
"login": "Ingresar",
|
||||
"logout": "Salir",
|
||||
"more": "Más",
|
||||
"play": "Reproducir",
|
||||
"serverAddressMustBeUrl": "Dirección del servidor debe ser una URL valida",
|
||||
"serverAddressRequired": "Dirección del servidor requerida",
|
||||
"serverNotFound": "Servidor no encontrado",
|
||||
"settings": "Configuración",
|
||||
"unexpectedError": "Error inesperado",
|
||||
"usernameRequired": "Se requiere nombre de usuario",
|
||||
"serverAddress": "Dirección del servidor",
|
||||
"username": "Usuario",
|
||||
"password": "Contraseña",
|
||||
"playType": "Reproducir {mediaType}",
|
||||
"signIn": "Ingresar",
|
||||
"upNext": "Siguiente",
|
||||
"libraryNotFound": "Librería no encontrada",
|
||||
"itemNotFound": "Item no encontrado",
|
||||
"present": "Presente",
|
||||
"libraryEmpty": "La biblioteca está vacía",
|
||||
"moreLikeThis": "Más como esto",
|
||||
"endsAt": "Termina {time}",
|
||||
"rating": "Calificación",
|
||||
"endDate": "Fecha de finalización",
|
||||
"releaseDate": "Fecha de estreno",
|
||||
"alphabetically": "Alfabéticamente"
|
||||
"badRequest": "Solicitud incorrecta. Reintente",
|
||||
"continueListening": "Continuar escuchando",
|
||||
"continueWatching": "Continuar viendo",
|
||||
"episodeNumber": "Episodio {episodeNumber}",
|
||||
"home": "Inicio",
|
||||
"incorrectUsernameOrPassword": "Usuario o contraseña incorrecta",
|
||||
"login": "Ingresar",
|
||||
"logout": "Salir",
|
||||
"more": "Más",
|
||||
"play": "Reproducir",
|
||||
"serverAddressMustBeUrl": "Dirección del servidor debe ser una URL valida",
|
||||
"serverAddressRequired": "Dirección del servidor requerida",
|
||||
"serverNotFound": "Servidor no encontrado",
|
||||
"settings": "Configuración",
|
||||
"unexpectedError": "Error inesperado",
|
||||
"usernameRequired": "Se requiere nombre de usuario",
|
||||
"serverAddress": "Dirección del servidor",
|
||||
"username": "Usuario",
|
||||
"password": "Contraseña",
|
||||
"playType": "Reproducir {mediaType}",
|
||||
"signIn": "Ingresar",
|
||||
"upNext": "Siguiente",
|
||||
"libraryNotFound": "Librería no encontrada",
|
||||
"itemNotFound": "Item no encontrado",
|
||||
"present": "Presente",
|
||||
"libraryEmpty": "La biblioteca está vacía",
|
||||
"moreLikeThis": "Más como esto",
|
||||
"endsAt": "Termina {time}",
|
||||
"rating": "Calificación",
|
||||
"endDate": "Fecha de finalización",
|
||||
"releaseDate": "Fecha de estreno",
|
||||
"alphabetically": "Alfabéticamente"
|
||||
}
|
||||
|
||||
+3
-1
@@ -15,6 +15,7 @@
|
||||
"logout": "Kirjaudu ulos",
|
||||
"more": "Lisää",
|
||||
"moreLikeThis": "Lisää samanlaisia",
|
||||
"movies": "",
|
||||
"password": "Salasana",
|
||||
"play": "Toista",
|
||||
"playType": "Toista {mediaType}",
|
||||
@@ -30,5 +31,6 @@
|
||||
"unexpectedError": "Odottamaton virhe",
|
||||
"upNext": "Seuraava",
|
||||
"username": "Käyttäjätunnus",
|
||||
"usernameRequired": "Käyttäjänimi vaaditaan"
|
||||
"usernameRequired": "Käyttäjänimi vaaditaan",
|
||||
"youMayAlsoLike": ""
|
||||
}
|
||||
|
||||
+34
-32
@@ -1,34 +1,36 @@
|
||||
{
|
||||
"badRequest": "Mauvaise requête. Réessayez",
|
||||
"continueListening": "Poursuivre l'écoute",
|
||||
"continueWatching": "Poursuivre le visionnage",
|
||||
"home": "Accueil",
|
||||
"incorrectUsernameOrPassword": "Nom d'utilisateur ou mot de passe incorrect",
|
||||
"login": "Connexion",
|
||||
"logout": "Se déconnecter",
|
||||
"more": "Plus",
|
||||
"play": "Lire",
|
||||
"serverAddressMustBeUrl": "L'adresse du serveur doit être une URL valide",
|
||||
"serverAddressRequired": "L'adresse du serveur est requise",
|
||||
"serverNotFound": "Serveur introuvable",
|
||||
"settings": "Paramètres",
|
||||
"unexpectedError": "Erreur inattendue",
|
||||
"usernameRequired": "Nom d'utilisateur requis",
|
||||
"serverAddress": "Adresse du serveur",
|
||||
"username": "Nom d'utilisateur",
|
||||
"password": "Mot de passe",
|
||||
"signIn": "Se connecter",
|
||||
"upNext": "À suivre",
|
||||
"libraryNotFound": "Médiathèque introuvable",
|
||||
"itemNotFound": "Élément introuvable",
|
||||
"episodeNumber": "Épisode {episodeNumber}",
|
||||
"playType": "Lire {mediaType}",
|
||||
"present": "Maintenant",
|
||||
"endsAt": "Se termine à {time}",
|
||||
"moreLikeThis": "Similaires",
|
||||
"libraryEmpty": "Cette médiathèque est vide",
|
||||
"rating": "Critique",
|
||||
"endDate": "Date de fin",
|
||||
"releaseDate": "Date de sortie",
|
||||
"alphabetically": "Alphabétique"
|
||||
"alphabetically": "Alphabétique",
|
||||
"badRequest": "Mauvaise requête. Réessayez",
|
||||
"continueListening": "Poursuivre l'écoute",
|
||||
"continueWatching": "Poursuivre le visionnage",
|
||||
"endDate": "Date de fin",
|
||||
"endsAt": "Se termine à {time}",
|
||||
"episodeNumber": "Épisode {episodeNumber}",
|
||||
"home": "Accueil",
|
||||
"incorrectUsernameOrPassword": "Nom d'utilisateur ou mot de passe incorrect",
|
||||
"itemNotFound": "Élément introuvable",
|
||||
"libraryEmpty": "Cette médiathèque est vide",
|
||||
"libraryNotFound": "Médiathèque introuvable",
|
||||
"login": "Connexion",
|
||||
"logout": "Se déconnecter",
|
||||
"more": "Plus",
|
||||
"movies": "",
|
||||
"password": "Mot de passe",
|
||||
"play": "Lire",
|
||||
"playType": "Lire {mediaType}",
|
||||
"present": "Maintenant",
|
||||
"rating": "Critique",
|
||||
"releaseDate": "Date de sortie",
|
||||
"serverAddress": "Adresse du serveur",
|
||||
"serverAddressMustBeUrl": "L'adresse du serveur doit être une URL valide",
|
||||
"serverAddressRequired": "L'adresse du serveur est requise",
|
||||
"serverNotFound": "Serveur introuvable",
|
||||
"settings": "Paramètres",
|
||||
"signIn": "Se connecter",
|
||||
"unexpectedError": "Erreur inattendue",
|
||||
"upNext": "À suivre",
|
||||
"username": "Nom d'utilisateur",
|
||||
"usernameRequired": "Nom d'utilisateur requis",
|
||||
"youMayAlsoLike": "",
|
||||
"youMightLike": ""
|
||||
}
|
||||
|
||||
+34
-32
@@ -1,34 +1,36 @@
|
||||
{
|
||||
"badRequest": "Bloga užklausa. Bandykite iš naujo",
|
||||
"continueListening": "Tęsti klausymą",
|
||||
"continueWatching": "Tęsti žiūrėjimą",
|
||||
"episodeNumber": "Epizodas nr. {episodeNumber}",
|
||||
"endsAt": "Baigiasi {time}",
|
||||
"home": "Pradžia",
|
||||
"incorrectUsernameOrPassword": "Neteisingas vartotojo vardas arba slaptažodis",
|
||||
"login": "Prisijungti",
|
||||
"logout": "Atsijungti",
|
||||
"more": "Daugiau",
|
||||
"moreLikeThis": "Daugiau panašių",
|
||||
"play": "Leisti",
|
||||
"present": "Dabar",
|
||||
"serverAddressMustBeUrl": "Serverio adresas turi būti teisingas URL",
|
||||
"serverAddressRequired": "Serverio adresas yra būtinas",
|
||||
"serverNotFound": "Serveris nerastas",
|
||||
"settings": "Nustatymai",
|
||||
"unexpectedError": "Netikėta klaida",
|
||||
"usernameRequired": "Vartotojo vardas yra būtinas",
|
||||
"serverAddress": "Serverio adresas",
|
||||
"username": "Vartotojo vardas",
|
||||
"password": "Slaptažodis",
|
||||
"playType": "Leisti {mediaType}",
|
||||
"signIn": "Prisijungti",
|
||||
"upNext": "Toliau",
|
||||
"libraryNotFound": "Mediateka nerasta",
|
||||
"itemNotFound": "Elementas nerastas",
|
||||
"libraryEmpty": "Tuščia mediateka",
|
||||
"alphabetically": "Paraidžiui",
|
||||
"releaseDate": "Išleidimo data",
|
||||
"endDate": "Pabaigos data",
|
||||
"rating": "Įvertinimas"
|
||||
"alphabetically": "Paraidžiui",
|
||||
"badRequest": "Bloga užklausa. Bandykite iš naujo",
|
||||
"continueListening": "Tęsti klausymą",
|
||||
"continueWatching": "Tęsti žiūrėjimą",
|
||||
"endDate": "Pabaigos data",
|
||||
"endsAt": "Baigiasi {time}",
|
||||
"episodeNumber": "Epizodas nr. {episodeNumber}",
|
||||
"home": "Pradžia",
|
||||
"incorrectUsernameOrPassword": "Neteisingas vartotojo vardas arba slaptažodis",
|
||||
"itemNotFound": "Elementas nerastas",
|
||||
"libraryEmpty": "Tuščia mediateka",
|
||||
"libraryNotFound": "Mediateka nerasta",
|
||||
"login": "Prisijungti",
|
||||
"logout": "Atsijungti",
|
||||
"more": "Daugiau",
|
||||
"movies": "",
|
||||
"password": "Slaptažodis",
|
||||
"play": "Leisti",
|
||||
"playType": "Leisti {mediaType}",
|
||||
"present": "Dabar",
|
||||
"rating": "Įvertinimas",
|
||||
"releaseDate": "Išleidimo data",
|
||||
"serverAddress": "Serverio adresas",
|
||||
"serverAddressMustBeUrl": "Serverio adresas turi būti teisingas URL",
|
||||
"serverAddressRequired": "Serverio adresas yra būtinas",
|
||||
"serverNotFound": "Serveris nerastas",
|
||||
"settings": "Nustatymai",
|
||||
"signIn": "Prisijungti",
|
||||
"unexpectedError": "Netikėta klaida",
|
||||
"upNext": "Toliau",
|
||||
"username": "Vartotojo vardas",
|
||||
"usernameRequired": "Vartotojo vardas yra būtinas",
|
||||
"youMayAlsoLike": "",
|
||||
"youMightLike": ""
|
||||
}
|
||||
|
||||
+32
-32
@@ -1,34 +1,34 @@
|
||||
{
|
||||
"badRequest": "Feil i forespørselen. Prøv igjen",
|
||||
"continueListening": "Fortsett å lytte",
|
||||
"continueWatching": "Fortsett å se",
|
||||
"home": "Hjem",
|
||||
"incorrectUsernameOrPassword": "Feil brukernavn eller passord",
|
||||
"login": "Logg inn",
|
||||
"logout": "Logg ut",
|
||||
"more": "Mer",
|
||||
"play": "Spill av",
|
||||
"serverAddressMustBeUrl": "Server adressen må være en gyldig link",
|
||||
"serverAddressRequired": "Serveradresse er påkrevd",
|
||||
"serverNotFound": "Server ikke funnet",
|
||||
"settings": "Innstillinger",
|
||||
"unexpectedError": "Uventet feil",
|
||||
"usernameRequired": "Brukernavn er påkrevd",
|
||||
"serverAddress": "Serveradresse",
|
||||
"username": "Brukernavn",
|
||||
"password": "Passord",
|
||||
"signIn": "Logg inn",
|
||||
"upNext": "Neste",
|
||||
"libraryNotFound": "Bibliotek ikke funnet",
|
||||
"itemNotFound": "Gjenstand ikke funnet",
|
||||
"episodeNumber": "Episode {episodeNumber}",
|
||||
"playType": "Spill av {mediaType}",
|
||||
"libraryEmpty": "Dette biblioteket er tomt",
|
||||
"present": "Tilstede",
|
||||
"moreLikeThis": "Mer som dette",
|
||||
"endsAt": "Slutter {Time}",
|
||||
"rating": "Vurdering",
|
||||
"endDate": "Sluttdato",
|
||||
"releaseDate": "Slippdato",
|
||||
"alphabetically": "Alfabetisk"
|
||||
"badRequest": "Feil i forespørselen. Prøv igjen",
|
||||
"continueListening": "Fortsett å lytte",
|
||||
"continueWatching": "Fortsett å se",
|
||||
"home": "Hjem",
|
||||
"incorrectUsernameOrPassword": "Feil brukernavn eller passord",
|
||||
"login": "Logg inn",
|
||||
"logout": "Logg ut",
|
||||
"more": "Mer",
|
||||
"play": "Spill av",
|
||||
"serverAddressMustBeUrl": "Server adressen må være en gyldig link",
|
||||
"serverAddressRequired": "Serveradresse er påkrevd",
|
||||
"serverNotFound": "Server ikke funnet",
|
||||
"settings": "Innstillinger",
|
||||
"unexpectedError": "Uventet feil",
|
||||
"usernameRequired": "Brukernavn er påkrevd",
|
||||
"serverAddress": "Serveradresse",
|
||||
"username": "Brukernavn",
|
||||
"password": "Passord",
|
||||
"signIn": "Logg inn",
|
||||
"upNext": "Neste",
|
||||
"libraryNotFound": "Bibliotek ikke funnet",
|
||||
"itemNotFound": "Gjenstand ikke funnet",
|
||||
"episodeNumber": "Episode {episodeNumber}",
|
||||
"playType": "Spill av {mediaType}",
|
||||
"libraryEmpty": "Dette biblioteket er tomt",
|
||||
"present": "Tilstede",
|
||||
"moreLikeThis": "Mer som dette",
|
||||
"endsAt": "Slutter {Time}",
|
||||
"rating": "Vurdering",
|
||||
"endDate": "Sluttdato",
|
||||
"releaseDate": "Slippdato",
|
||||
"alphabetically": "Alfabetisk"
|
||||
}
|
||||
|
||||
+3
-1
@@ -15,6 +15,7 @@
|
||||
"logout": "Uitloggen",
|
||||
"more": "Meer",
|
||||
"moreLikeThis": "Meer zoals dit",
|
||||
"movies": "",
|
||||
"password": "Wachtwoord",
|
||||
"play": "Afspelen",
|
||||
"playType": "Speel {mediaType}",
|
||||
@@ -30,5 +31,6 @@
|
||||
"unexpectedError": "Onverwachte fout",
|
||||
"upNext": "Volgende",
|
||||
"username": "Gebruikersnaam",
|
||||
"usernameRequired": "Gebruikersnaam is verplicht"
|
||||
"usernameRequired": "Gebruikersnaam is verplicht",
|
||||
"youMayAlsoLike": ""
|
||||
}
|
||||
|
||||
+3
-1
@@ -15,6 +15,7 @@
|
||||
"logout": "Wyloguj",
|
||||
"more": "Więcej",
|
||||
"moreLikeThis": "",
|
||||
"movies": "",
|
||||
"password": "Hasło",
|
||||
"play": "Odtwarzaj",
|
||||
"playType": "Odtwarzaj {mediaType}",
|
||||
@@ -30,5 +31,6 @@
|
||||
"unexpectedError": "Niespodziewany błąd",
|
||||
"upNext": "Następnie",
|
||||
"username": "Nazwa użytkownika",
|
||||
"usernameRequired": "Nazwa użytkownika jest wymagana"
|
||||
"usernameRequired": "Nazwa użytkownika jest wymagana",
|
||||
"youMayAlsoLike": ""
|
||||
}
|
||||
|
||||
+3
-1
@@ -15,6 +15,7 @@
|
||||
"logout": "Deconectare",
|
||||
"more": "Mai mult",
|
||||
"moreLikeThis": "Similare",
|
||||
"movies": "",
|
||||
"password": "Parolă",
|
||||
"play": "Rulează",
|
||||
"playType": "Redă {mediaType}",
|
||||
@@ -30,5 +31,6 @@
|
||||
"unexpectedError": "Eroare neașteptată",
|
||||
"upNext": "Urmează",
|
||||
"username": "Utilizator",
|
||||
"usernameRequired": "Numele de utilizator este necesar"
|
||||
"usernameRequired": "Numele de utilizator este necesar",
|
||||
"youMayAlsoLike": ""
|
||||
}
|
||||
|
||||
+3
-1
@@ -15,6 +15,7 @@
|
||||
"logout": "Odhlásenie",
|
||||
"more": "Viac",
|
||||
"moreLikeThis": "Podobné",
|
||||
"movies": "",
|
||||
"password": "Heslo",
|
||||
"play": "Prehrať",
|
||||
"playType": "Prehrať {mediaType}",
|
||||
@@ -30,5 +31,6 @@
|
||||
"unexpectedError": "Neočakávaná chyba",
|
||||
"upNext": "Nasleduje",
|
||||
"username": "Používateľské meno",
|
||||
"usernameRequired": "Používateľské meno je požadované"
|
||||
"usernameRequired": "Používateľské meno je požadované",
|
||||
"youMayAlsoLike": ""
|
||||
}
|
||||
|
||||
+3
-1
@@ -15,6 +15,7 @@
|
||||
"logout": "Odjava",
|
||||
"more": "Več",
|
||||
"moreLikeThis": "",
|
||||
"movies": "",
|
||||
"password": "Geslo",
|
||||
"play": "Predvajaj",
|
||||
"playType": "",
|
||||
@@ -30,5 +31,6 @@
|
||||
"unexpectedError": "Nepričakovana napaka",
|
||||
"upNext": "Naslednje",
|
||||
"username": "Uporabniško ime",
|
||||
"usernameRequired": "Uporabniško ime je zahtevano"
|
||||
"usernameRequired": "Uporabniško ime je zahtevano",
|
||||
"youMayAlsoLike": ""
|
||||
}
|
||||
|
||||
+3
-1
@@ -15,6 +15,7 @@
|
||||
"logout": "Logga ut",
|
||||
"more": "Mer",
|
||||
"moreLikeThis": "Mer som detta",
|
||||
"movies": "",
|
||||
"password": "Lösenord",
|
||||
"play": "Spela",
|
||||
"playType": "Spela {mediaType}",
|
||||
@@ -30,5 +31,6 @@
|
||||
"unexpectedError": "Oförutsett fel",
|
||||
"upNext": "Kommer näst",
|
||||
"username": "Användarnamn",
|
||||
"usernameRequired": "Användarnamn krävs"
|
||||
"usernameRequired": "Användarnamn krävs",
|
||||
"youMayAlsoLike": ""
|
||||
}
|
||||
|
||||
+34
-32
@@ -1,34 +1,36 @@
|
||||
{
|
||||
"badRequest": "தவறான கோரிக்கை. மீண்டும் முயற்சி செய்யவும்",
|
||||
"continueListening": "தொடர்ந்து கேளுங்கள்",
|
||||
"continueWatching": "தொடர்ந்து பார்த்துக் கொள்ளுங்கள்",
|
||||
"home": "முகப்பு",
|
||||
"incorrectUsernameOrPassword": "தவறான பயனர்பெயர் அல்லது கடவுச்சொல்",
|
||||
"login": "புகுபதிகை",
|
||||
"logout": "விடுபதிகை",
|
||||
"more": "மேலும்",
|
||||
"play": "இயக்கு",
|
||||
"serverAddressMustBeUrl": "சேவையக முகவரி சரியான URL ஆக இருக்க வேண்டும்",
|
||||
"serverAddressRequired": "சேவையக முகவரி தேவை",
|
||||
"serverNotFound": "சேவையகம் கிடைக்கவில்லை",
|
||||
"settings": "அமைப்புகள்",
|
||||
"unexpectedError": "எதிர்பாராத பிழை",
|
||||
"usernameRequired": "பயனர்பெயர் தேவை",
|
||||
"serverAddress": "சேவையக முகவரி",
|
||||
"username": "பயனர்பெயர்",
|
||||
"password": "கடவுச்சொல்",
|
||||
"signIn": "உள்நுழைக",
|
||||
"upNext": "அடுத்தது",
|
||||
"libraryNotFound": "நூலகம் கிடைக்கவில்லை",
|
||||
"itemNotFound": "பொருள் கிடைக்கவில்லை",
|
||||
"episodeNumber": "அத்தியாயம் {episodeNumber}",
|
||||
"playType": "{mediaType} ஐ இயக்கு",
|
||||
"present": "தற்போது",
|
||||
"moreLikeThis": "மேலும் இது போன்றது",
|
||||
"endsAt": "{time} இல் முடிகிறது",
|
||||
"libraryEmpty": "இந்த நூலகம் காலியாக உள்ளது",
|
||||
"rating": "மதிப்பீடு",
|
||||
"endDate": "கடைசி தேதி",
|
||||
"releaseDate": "வெளிவரும் தேதி",
|
||||
"alphabetically": "அகர வரிசைப்படி"
|
||||
"alphabetically": "அகர வரிசைப்படி",
|
||||
"badRequest": "தவறான கோரிக்கை. மீண்டும் முயற்சி செய்யவும்",
|
||||
"continueListening": "தொடர்ந்து கேளுங்கள்",
|
||||
"continueWatching": "தொடர்ந்து பார்த்துக் கொள்ளுங்கள்",
|
||||
"endDate": "கடைசி தேதி",
|
||||
"endsAt": "{time} இல் முடிகிறது",
|
||||
"episodeNumber": "அத்தியாயம் {episodeNumber}",
|
||||
"home": "முகப்பு",
|
||||
"incorrectUsernameOrPassword": "தவறான பயனர்பெயர் அல்லது கடவுச்சொல்",
|
||||
"itemNotFound": "பொருள் கிடைக்கவில்லை",
|
||||
"libraryEmpty": "இந்த நூலகம் காலியாக உள்ளது",
|
||||
"libraryNotFound": "நூலகம் கிடைக்கவில்லை",
|
||||
"login": "புகுபதிகை",
|
||||
"logout": "விடுபதிகை",
|
||||
"more": "மேலும்",
|
||||
"movies": "",
|
||||
"password": "கடவுச்சொல்",
|
||||
"play": "இயக்கு",
|
||||
"playType": "{mediaType} ஐ இயக்கு",
|
||||
"present": "தற்போது",
|
||||
"rating": "மதிப்பீடு",
|
||||
"releaseDate": "வெளிவரும் தேதி",
|
||||
"serverAddress": "சேவையக முகவரி",
|
||||
"serverAddressMustBeUrl": "சேவையக முகவரி சரியான URL ஆக இருக்க வேண்டும்",
|
||||
"serverAddressRequired": "சேவையக முகவரி தேவை",
|
||||
"serverNotFound": "சேவையகம் கிடைக்கவில்லை",
|
||||
"settings": "அமைப்புகள்",
|
||||
"signIn": "உள்நுழைக",
|
||||
"unexpectedError": "எதிர்பாராத பிழை",
|
||||
"upNext": "அடுத்தது",
|
||||
"username": "பயனர்பெயர்",
|
||||
"usernameRequired": "பயனர்பெயர் தேவை",
|
||||
"youMayAlsoLike": "",
|
||||
"youMightLike": ""
|
||||
}
|
||||
|
||||
+3
-1
@@ -15,6 +15,7 @@
|
||||
"logout": "Çıkış",
|
||||
"more": "Dahası",
|
||||
"moreLikeThis": "Buna Benzer Daha Fazla",
|
||||
"movies": "",
|
||||
"password": "Şifre",
|
||||
"play": "Oynat",
|
||||
"playType": "",
|
||||
@@ -30,5 +31,6 @@
|
||||
"unexpectedError": "Beklenmedik hata",
|
||||
"upNext": "Sonraki",
|
||||
"username": "Kullanıcı Adı",
|
||||
"usernameRequired": "Kullanıcı adı gerekli"
|
||||
"usernameRequired": "Kullanıcı adı gerekli",
|
||||
"youMayAlsoLike": ""
|
||||
}
|
||||
|
||||
+34
-32
@@ -1,34 +1,36 @@
|
||||
{
|
||||
"badRequest": "Yêu cầu đã thất bại. Thử lại",
|
||||
"continueListening": "Tiếp tục nghe",
|
||||
"continueWatching": "Tiếp tục xem",
|
||||
"episodeNumber": "Tập {episodeNumber}",
|
||||
"home": "Trang chủ",
|
||||
"incorrectUsernameOrPassword": "Sai tên đăng nhập hoặc mật khẩu",
|
||||
"login": "Đăng nhập",
|
||||
"logout": "Thoát",
|
||||
"more": "Thêm",
|
||||
"play": "Phát",
|
||||
"present": "Hiện tại",
|
||||
"serverAddressMustBeUrl": "Địa chỉ máy chủ phải là một URL hợp lệ",
|
||||
"serverAddressRequired": "Địa chỉ máy chủ là bắt buộc",
|
||||
"serverNotFound": "Không tìm thấy máy chủ",
|
||||
"settings": "Cài đặt",
|
||||
"unexpectedError": "Lỗi không mong muốn",
|
||||
"usernameRequired": "Tên đăng nhập là bắt buộc",
|
||||
"serverAddress": "Địa chỉ máy chủ",
|
||||
"username": "Tên đăng nhập",
|
||||
"password": "Mật khẩu",
|
||||
"playType": "Phát {mediaType}",
|
||||
"signIn": "Đăng nhập",
|
||||
"upNext": "Tiếp theo",
|
||||
"libraryNotFound": "Không tìm thấy thư viện",
|
||||
"itemNotFound": "Không tìm thấy mục",
|
||||
"moreLikeThis": "Thêm tương tự như thế này",
|
||||
"endsAt": "Kết thúc lúc {time}",
|
||||
"libraryEmpty": "Thư viện này trống",
|
||||
"rating": "Xếp hạng",
|
||||
"endDate": "Ngày kết thúc",
|
||||
"releaseDate": "Ngày phát hành",
|
||||
"alphabetically": "Theo bảng chữ cái"
|
||||
"alphabetically": "Theo bảng chữ cái",
|
||||
"badRequest": "Yêu cầu đã thất bại. Thử lại",
|
||||
"continueListening": "Tiếp tục nghe",
|
||||
"continueWatching": "Tiếp tục xem",
|
||||
"endDate": "Ngày kết thúc",
|
||||
"endsAt": "Kết thúc lúc {time}",
|
||||
"episodeNumber": "Tập {episodeNumber}",
|
||||
"home": "Trang chủ",
|
||||
"incorrectUsernameOrPassword": "Sai tên đăng nhập hoặc mật khẩu",
|
||||
"itemNotFound": "Không tìm thấy mục",
|
||||
"libraryEmpty": "Thư viện này trống",
|
||||
"libraryNotFound": "Không tìm thấy thư viện",
|
||||
"login": "Đăng nhập",
|
||||
"logout": "Thoát",
|
||||
"more": "Thêm",
|
||||
"movies": "",
|
||||
"password": "Mật khẩu",
|
||||
"play": "Phát",
|
||||
"playType": "Phát {mediaType}",
|
||||
"present": "Hiện tại",
|
||||
"rating": "Xếp hạng",
|
||||
"releaseDate": "Ngày phát hành",
|
||||
"serverAddress": "Địa chỉ máy chủ",
|
||||
"serverAddressMustBeUrl": "Địa chỉ máy chủ phải là một URL hợp lệ",
|
||||
"serverAddressRequired": "Địa chỉ máy chủ là bắt buộc",
|
||||
"serverNotFound": "Không tìm thấy máy chủ",
|
||||
"settings": "Cài đặt",
|
||||
"signIn": "Đăng nhập",
|
||||
"unexpectedError": "Lỗi không mong muốn",
|
||||
"upNext": "Tiếp theo",
|
||||
"username": "Tên đăng nhập",
|
||||
"usernameRequired": "Tên đăng nhập là bắt buộc",
|
||||
"youMayAlsoLike": "",
|
||||
"youMightLike": ""
|
||||
}
|
||||
|
||||
+34
-32
@@ -1,34 +1,36 @@
|
||||
{
|
||||
"badRequest": "错误的请求。请重试",
|
||||
"continueListening": "继续听",
|
||||
"continueWatching": "继续观看",
|
||||
"episodeNumber": "章节 {episodeNumber}",
|
||||
"home": "首页",
|
||||
"incorrectUsernameOrPassword": "用户名或密码错误",
|
||||
"login": "登陆",
|
||||
"logout": "注销",
|
||||
"more": "更多",
|
||||
"play": "播放",
|
||||
"serverAddressMustBeUrl": "服务器地址必须是有效的网址",
|
||||
"serverAddressRequired": "服务器地址为必填项",
|
||||
"serverNotFound": "找不到服务器",
|
||||
"settings": "设置",
|
||||
"unexpectedError": "意外的错误",
|
||||
"usernameRequired": "用户名为必填项",
|
||||
"serverAddress": "服务器地址",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"signIn": "登陆",
|
||||
"upNext": "下一个",
|
||||
"libraryNotFound": "未找到媒体库",
|
||||
"itemNotFound": "未找到项目",
|
||||
"playType": "播放 {mediaType}",
|
||||
"present": "现在",
|
||||
"moreLikeThis": "更像这样",
|
||||
"libraryEmpty": "这个库是空的",
|
||||
"endsAt": "结束于 {time}",
|
||||
"rating": "评价",
|
||||
"endDate": "结束日期",
|
||||
"releaseDate": "发布日期",
|
||||
"alphabetically": "按字母排序"
|
||||
"alphabetically": "按字母排序",
|
||||
"badRequest": "错误的请求。请重试",
|
||||
"continueListening": "继续听",
|
||||
"continueWatching": "继续观看",
|
||||
"endDate": "结束日期",
|
||||
"endsAt": "结束于 {time}",
|
||||
"episodeNumber": "章节 {episodeNumber}",
|
||||
"home": "首页",
|
||||
"incorrectUsernameOrPassword": "用户名或密码错误",
|
||||
"itemNotFound": "未找到项目",
|
||||
"libraryEmpty": "这个库是空的",
|
||||
"libraryNotFound": "未找到媒体库",
|
||||
"login": "登陆",
|
||||
"logout": "注销",
|
||||
"more": "更多",
|
||||
"movies": "",
|
||||
"password": "密码",
|
||||
"play": "播放",
|
||||
"playType": "播放 {mediaType}",
|
||||
"present": "现在",
|
||||
"rating": "评价",
|
||||
"releaseDate": "发布日期",
|
||||
"serverAddress": "服务器地址",
|
||||
"serverAddressMustBeUrl": "服务器地址必须是有效的网址",
|
||||
"serverAddressRequired": "服务器地址为必填项",
|
||||
"serverNotFound": "找不到服务器",
|
||||
"settings": "设置",
|
||||
"signIn": "登陆",
|
||||
"unexpectedError": "意外的错误",
|
||||
"upNext": "下一个",
|
||||
"username": "用户名",
|
||||
"usernameRequired": "用户名为必填项",
|
||||
"youMayAlsoLike": "",
|
||||
"youMightLike": ""
|
||||
}
|
||||
|
||||
+5
-2
@@ -19,7 +19,7 @@ declare module '@nuxt/types' {
|
||||
|
||||
declare module 'vue/types/vue' {
|
||||
interface Vue {
|
||||
ticksToMs: (ticks: number) => number;
|
||||
ticksToMs: (ticks: number | null | undefined) => number;
|
||||
msToTicks: (ms: number) => number;
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,10 @@ const timeUtils = Vue.extend({
|
||||
* @param {string} ticks number of ticks to convert from
|
||||
* @returns {number} Correct number of ms
|
||||
*/
|
||||
ticksToMs(ticks: number): number {
|
||||
ticksToMs(ticks: number | null | undefined): number {
|
||||
if (!ticks) {
|
||||
ticks = 0;
|
||||
}
|
||||
return ticks / 10000;
|
||||
},
|
||||
/**
|
||||
|
||||
+3
-1
@@ -63,7 +63,8 @@ const config: NuxtConfig = {
|
||||
'@nuxt/typescript-build',
|
||||
// Doc: https://github.com/nuxt-community/stylelint-module
|
||||
'@nuxtjs/stylelint-module',
|
||||
'@nuxtjs/vuetify'
|
||||
'@nuxtjs/vuetify',
|
||||
'@nuxtjs/date-fns'
|
||||
],
|
||||
/*
|
||||
** Nuxt.js modules
|
||||
@@ -176,6 +177,7 @@ const config: NuxtConfig = {
|
||||
*/
|
||||
vuetify: {
|
||||
customVariables: ['~/assets/variables.scss'],
|
||||
treeShake: true,
|
||||
defaultAssets: false,
|
||||
theme: {
|
||||
dark: true,
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
"@types/lodash": "^4.14.162",
|
||||
"@types/uuid": "^8.3.0",
|
||||
"blurhash": "^1.1.3",
|
||||
"fontsource-noto-sans": "^3.0.10",
|
||||
"langs": "^2.0.0",
|
||||
"lodash": "^4.17.20",
|
||||
"mux.js": "^5.6.7",
|
||||
"nuxt": "^2.14.7",
|
||||
@@ -48,6 +50,7 @@
|
||||
"@mdi/font": "^5.7.55",
|
||||
"@nuxt/types": "^2.14.7",
|
||||
"@nuxt/typescript-build": "^2.0.2",
|
||||
"@nuxtjs/date-fns": "^1.4.0",
|
||||
"@nuxtjs/eslint-config": "^4.0.0",
|
||||
"@nuxtjs/eslint-config-typescript": "^4.0.0",
|
||||
"@nuxtjs/eslint-module": "^3.0.0",
|
||||
|
||||
+363
-126
@@ -1,72 +1,370 @@
|
||||
<template>
|
||||
<v-container fluid class="pa-0 item-container">
|
||||
<v-img
|
||||
v-resize="updateBackdropImage"
|
||||
:src="backdropImageSource"
|
||||
class="d-flex align-end backdrop-image"
|
||||
max-width="100%"
|
||||
>
|
||||
<div class="d-flex align-end gradient-container">
|
||||
<div class="d-flex flex-wrap item-details-container">
|
||||
<div class="white--text">
|
||||
<v-img
|
||||
v-if="
|
||||
item.ImageTags && item.ImageTags.Logo && getAspectRatio() > 1
|
||||
"
|
||||
:src="getImageUrl(item.Id, 'Logo')"
|
||||
contain
|
||||
:alt="item.Name"
|
||||
max-width="50%"
|
||||
class="mb-4"
|
||||
></v-img>
|
||||
<h1 v-else>{{ item.Name }}</h1>
|
||||
<div class="item-sub-heading">{{ renderItemSubHeading() }}</div>
|
||||
<p class="item-overview">{{ item.Overview }}</p>
|
||||
</div>
|
||||
<div class="item-details-right">
|
||||
<v-btn
|
||||
class="play-button"
|
||||
color="primary"
|
||||
:to="`./${item.Id}/play`"
|
||||
>{{ $t('playType', { mediaType: item.Type }) }}</v-btn
|
||||
<v-container class="ml-3 mr-3">
|
||||
<v-row>
|
||||
<v-col cols="9">
|
||||
<v-row>
|
||||
<v-col cols="3">
|
||||
<card v-if="loaded" :item="item" no-text no-margin />
|
||||
<skeleton-card v-else no-text />
|
||||
</v-col>
|
||||
<v-col cols="9">
|
||||
<h1 v-if="loaded" class="text-h4 font-weight-light text-truncate">
|
||||
{{ item.Name }}
|
||||
</h1>
|
||||
<v-skeleton-loader
|
||||
v-else
|
||||
class="mt-3 mb-2"
|
||||
type="heading"
|
||||
width="50em"
|
||||
/>
|
||||
<h2
|
||||
v-if="loaded && item.OriginalTitle"
|
||||
class="text-subtitle-1 text-truncate"
|
||||
>
|
||||
<v-btn>{{ $t('more') }}</v-btn>
|
||||
</div>
|
||||
{{ item.OriginalTitle }}
|
||||
</h2>
|
||||
<v-skeleton-loader v-else type="heading" width="25em" />
|
||||
<div class="text-caption text-h4 font-weight-medium">
|
||||
<media-info
|
||||
v-if="loaded"
|
||||
:item="item"
|
||||
year
|
||||
runtime
|
||||
rating
|
||||
ends-at
|
||||
/>
|
||||
<v-skeleton-loader v-else type="text" width="50em" class="mt-2" />
|
||||
</div>
|
||||
<div class="mt-3 mb-2">
|
||||
<v-btn
|
||||
v-if="loaded"
|
||||
class="play-button mr-2"
|
||||
color="primary"
|
||||
min-width="8em"
|
||||
:disabled="isPlayable"
|
||||
depressed
|
||||
rounded
|
||||
:to="`./${item.Id}/play`"
|
||||
>{{ $t('play') }}</v-btn
|
||||
>
|
||||
<v-skeleton-loader v-else type="button" />
|
||||
<v-btn v-if="loaded" outlined icon>
|
||||
<v-icon>mdi-dots-horizontal</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-col class="mt-2" cols="10">
|
||||
<v-row
|
||||
v-if="loaded && item && item.Genres && item.Genres.length > 1"
|
||||
>
|
||||
<v-col cols="2" class="d-flex align-center pa-0">
|
||||
<label class="text--secondary">Genres</label>
|
||||
</v-col>
|
||||
<v-col cols="7">{{ item.Genres.join(', ') }}</v-col>
|
||||
</v-row>
|
||||
<div
|
||||
v-if="
|
||||
loaded &&
|
||||
item &&
|
||||
((item.MediaSources && item.MediaSources.length > 1) ||
|
||||
videoTracks.length > 0 ||
|
||||
audioTracks.length > 0 ||
|
||||
subtitleTracks.length > 0)
|
||||
"
|
||||
class="mt-2"
|
||||
>
|
||||
<v-row v-if="item.MediaSources.length > 1">
|
||||
<v-col cols="2" class="d-flex align-center pa-0">
|
||||
<label class="text--secondary">Video</label>
|
||||
</v-col>
|
||||
<v-col cols="7">
|
||||
<v-select
|
||||
v-model="currentSource"
|
||||
:items="item.MediaSources"
|
||||
outlined
|
||||
filled
|
||||
flat
|
||||
dense
|
||||
single-line
|
||||
hide-details
|
||||
>
|
||||
<template slot="selection" slot-scope="{ item }">
|
||||
{{ item.DisplayTitle }}
|
||||
</template>
|
||||
</v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="videoTracks.length > 0">
|
||||
<v-col cols="2" class="d-flex align-center pa-0">
|
||||
<label class="text--secondary">Video</label>
|
||||
</v-col>
|
||||
<v-col cols="7">
|
||||
<v-select
|
||||
v-model="currentVideoTrack"
|
||||
:items="videoTracks"
|
||||
:disabled="videoTracks.length <= 1"
|
||||
outlined
|
||||
filled
|
||||
flat
|
||||
dense
|
||||
single-line
|
||||
hide-details
|
||||
>
|
||||
<template slot="selection" slot-scope="{ item }">
|
||||
{{ item.DisplayTitle }}
|
||||
</template>
|
||||
</v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="audioTracks.length > 0">
|
||||
<v-col cols="2" class="d-flex align-center pa-0">
|
||||
<label class="text--secondary">Audio</label>
|
||||
</v-col>
|
||||
<v-col cols="7">
|
||||
<v-select
|
||||
v-if="audioTracks.length > 1"
|
||||
v-model="currentAudioTrack"
|
||||
:items="audioTracks"
|
||||
:disabled="audioTracks.length <= 1"
|
||||
outlined
|
||||
filled
|
||||
flat
|
||||
dense
|
||||
single-line
|
||||
hide-details
|
||||
>
|
||||
<template slot="selection" slot-scope="{ item }">
|
||||
{{ item.DisplayTitle }}
|
||||
</template>
|
||||
<template slot="item" slot-scope="{ item, on, attrs }">
|
||||
<v-list-item v-bind="attrs" two-line v-on="on">
|
||||
<v-list-item-avatar>
|
||||
<v-icon
|
||||
v-text="getSurroundIcon(item.ChannelLayout)"
|
||||
></v-icon>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{
|
||||
item.Title
|
||||
}}</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ getLanguageName(item.Language) }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</template>
|
||||
</v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="subtitleTracks.length > 0">
|
||||
<v-col cols="2" class="d-flex align-center pa-0">
|
||||
<label class="text--secondary">Subtitles</label>
|
||||
</v-col>
|
||||
<v-col cols="7">
|
||||
<v-select
|
||||
v-if="subtitleTracks.length > 0"
|
||||
v-model="currentSubtitleTrack"
|
||||
:items="subtitleTracks"
|
||||
outlined
|
||||
filled
|
||||
flat
|
||||
dense
|
||||
single-line
|
||||
hide-details
|
||||
>
|
||||
<template slot="selection" slot-scope="{ item }">
|
||||
{{ item.DisplayTitle }}
|
||||
</template>
|
||||
<template slot="item" slot-scope="{ item, on, attrs }">
|
||||
<v-list-item v-bind="attrs" two-line v-on="on">
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{
|
||||
item.Title
|
||||
}}</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ getLanguageName(item.Language) }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</template>
|
||||
</v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-col>
|
||||
<div>
|
||||
<p
|
||||
v-if="loaded && item.Taglines"
|
||||
class="text-subtitle-1 text-truncate"
|
||||
>
|
||||
{{ item.Taglines[0] }}
|
||||
</p>
|
||||
<v-skeleton-loader v-else type="text" width="25em" class="mb-4" />
|
||||
<p v-if="loaded" class="item-overview">{{ item.Overview }}</p>
|
||||
<div v-else>
|
||||
<v-skeleton-loader
|
||||
v-for="index in 2"
|
||||
:key="index"
|
||||
type="sentences"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<related-items
|
||||
v-if="item.Type !== 'Series'"
|
||||
:id="$route.params.itemId"
|
||||
/>
|
||||
<season-tabs
|
||||
v-if="item.Type === 'Series'"
|
||||
:item="item"
|
||||
></season-tabs>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
<v-col cols="3">
|
||||
<div v-if="crew.length > 0">
|
||||
<h2 v-if="loaded">Crew</h2>
|
||||
<v-skeleton-loader v-else type="heading" />
|
||||
<person-list :items="crew" :skeleton-length="3" />
|
||||
</div>
|
||||
</div>
|
||||
</v-img>
|
||||
<season-tabs v-if="item.Type === 'Series'" :item="item"></season-tabs>
|
||||
<related-items :id="$route.params.itemId" />
|
||||
<div v-if="actors.length > 0">
|
||||
<h2 v-if="loaded">Cast</h2>
|
||||
<v-skeleton-loader v-else type="heading" />
|
||||
<person-list :items="actors" :skeleton-length="5" />
|
||||
</div>
|
||||
<related-items
|
||||
v-if="item.Type === 'Series'"
|
||||
:id="$route.params.itemId"
|
||||
vertical
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { BaseItemDto } from '~/api';
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment -- Temporary module while waiting for fixes to language names on the server
|
||||
// @ts-ignore
|
||||
import langs from 'langs';
|
||||
import {
|
||||
BaseItemDto,
|
||||
BaseItemPerson,
|
||||
MediaSourceInfo,
|
||||
MediaStream
|
||||
} from '~/api';
|
||||
import imageHelper from '~/mixins/imageHelper';
|
||||
import timeUtils from '~/mixins/timeUtils';
|
||||
|
||||
export default Vue.extend({
|
||||
mixins: [imageHelper, timeUtils],
|
||||
mixins: [imageHelper],
|
||||
data() {
|
||||
return {
|
||||
loaded: false,
|
||||
item: {} as BaseItemDto,
|
||||
backdropImageSource: ''
|
||||
backdropImageSource: '',
|
||||
currentSource: {} as MediaSourceInfo,
|
||||
videoTracks: [] as MediaStream[],
|
||||
currentVideoTrack: {} as MediaStream,
|
||||
audioTracks: [] as MediaStream[],
|
||||
currentAudioTrack: {} as MediaStream,
|
||||
subtitleTracks: [] as MediaStream[],
|
||||
currentSubtitleTrack: {} as MediaStream
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isPlayable: {
|
||||
get() {
|
||||
// TODO: Move this to a mixin
|
||||
if (['Movie'].includes(this.$data.item.Type)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
crew: {
|
||||
get() {
|
||||
if (this.$data.item.People) {
|
||||
// TODO: Figure out how common it is to have more than one director
|
||||
return this.$data.item.People.filter((person: BaseItemPerson) => {
|
||||
return ['Director', 'Writer'].includes(person.Type || '');
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
actors: {
|
||||
get() {
|
||||
if (this.$data.item.People) {
|
||||
return this.$data.item.People.filter((person: BaseItemPerson) => {
|
||||
return person.Type === 'Actor';
|
||||
}).slice(0, 10);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
async beforeMount() {
|
||||
const items = (
|
||||
await this.$api.items.getItems({
|
||||
uId: this.$auth.user.Id,
|
||||
const item = (
|
||||
await this.$api.userLibrary.getItem({
|
||||
userId: this.$auth.user.Id,
|
||||
ids: this.$route.params.itemId,
|
||||
fields: 'Overview,Genres'
|
||||
itemId: this.$route.params.itemId
|
||||
})
|
||||
).data.Items;
|
||||
).data;
|
||||
|
||||
if (items) {
|
||||
this.$store.dispatch('backdrop/set', { item: items[0] });
|
||||
this.item = items[0];
|
||||
if (item) {
|
||||
this.$store.dispatch('backdrop/set', { item });
|
||||
|
||||
if (item.MediaSources) {
|
||||
this.currentSource = item.MediaSources[0];
|
||||
|
||||
// Filter the streams to get each type of track
|
||||
if (this.currentSource.MediaStreams) {
|
||||
this.videoTracks = this.currentSource.MediaStreams.filter(
|
||||
(stream: MediaStream) => {
|
||||
return stream.Type === 'Video';
|
||||
}
|
||||
);
|
||||
this.audioTracks = this.currentSource.MediaStreams.filter(
|
||||
(stream: MediaStream) => {
|
||||
return stream.Type === 'Audio';
|
||||
}
|
||||
);
|
||||
this.subtitleTracks = this.currentSource.MediaStreams.filter(
|
||||
(stream: MediaStream) => {
|
||||
return stream.Type === 'Subtitle';
|
||||
}
|
||||
);
|
||||
|
||||
// Set default tracks
|
||||
if (this.videoTracks.length > 0) {
|
||||
this.currentVideoTrack = this.videoTracks[0];
|
||||
}
|
||||
if (
|
||||
this.audioTracks.length > 0 &&
|
||||
this.currentSource.DefaultAudioStreamIndex
|
||||
) {
|
||||
this.currentAudioTrack = this.audioTracks[
|
||||
this.currentSource.DefaultAudioStreamIndex
|
||||
];
|
||||
}
|
||||
if (
|
||||
this.subtitleTracks.length > 0 &&
|
||||
this.currentSource.DefaultSubtitleStreamIndex
|
||||
) {
|
||||
this.currentSubtitleTrack = this.subtitleTracks[
|
||||
this.currentSource.DefaultSubtitleStreamIndex
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.item = item;
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
this.updateBackdropImage();
|
||||
@@ -75,8 +373,22 @@ export default Vue.extend({
|
||||
this.$store.dispatch('backdrop/clear');
|
||||
},
|
||||
methods: {
|
||||
getAspectRatio() {
|
||||
return window.innerWidth / window.innerHeight;
|
||||
getLanguageName(code: string) {
|
||||
return langs.where('2B', code).name;
|
||||
},
|
||||
getSurroundIcon(layout: string) {
|
||||
switch (layout) {
|
||||
case '2.0':
|
||||
return 'mdi-surround-sound-2-0';
|
||||
case '3.1':
|
||||
return 'mdi-surround-sound-3-1';
|
||||
case '5.1':
|
||||
return 'mdi-surround-sound-5-1';
|
||||
case '7.1':
|
||||
return 'mdi-surround-sound-7-1';
|
||||
default:
|
||||
return 'mdi-surround-sound';
|
||||
}
|
||||
},
|
||||
getItemBackdrop(id: string): string {
|
||||
if (window.innerWidth < window.innerHeight) {
|
||||
@@ -85,84 +397,9 @@ export default Vue.extend({
|
||||
return `${this.$axios.defaults.baseURL}/Items/${id}/Images/Backdrop`;
|
||||
}
|
||||
},
|
||||
getEndsAtTime(ticks: number): string {
|
||||
const ms = this.ticksToMs(ticks);
|
||||
const endTimeLong = new Date(Date.now() + ms);
|
||||
// TODO: Respect user locale when rendering time
|
||||
const endTimeShort = endTimeLong.toLocaleString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric'
|
||||
});
|
||||
|
||||
// TODO: Use a Date object
|
||||
return this.$t('endsAt', {
|
||||
time: endTimeShort
|
||||
}).toString();
|
||||
},
|
||||
ticksToTime(ticks: number) {
|
||||
const min = this.ticksToMs(ticks) / 60;
|
||||
if (Math.floor(min / 60) && Math.floor(min % 60)) {
|
||||
return `${Math.floor(min / 60)} hrs ${Math.floor(min % 60)} min`;
|
||||
} else if (Math.floor(min / 60)) {
|
||||
return `${Math.floor(min / 60)} hrs`;
|
||||
} else {
|
||||
return `${Math.floor(min % 60)} min`;
|
||||
}
|
||||
},
|
||||
renderItemSubHeading() {
|
||||
const response = [];
|
||||
if (this.item.ProductionYear) {
|
||||
response.push(this.item.ProductionYear);
|
||||
}
|
||||
if (this.item.Genres) {
|
||||
response.push(this.item.Genres[0]);
|
||||
}
|
||||
if (this.item.RunTimeTicks) {
|
||||
response.push(this.ticksToTime(this.item.RunTimeTicks));
|
||||
}
|
||||
if (this.item.RunTimeTicks) {
|
||||
response.push(this.getEndsAtTime(this.item.RunTimeTicks));
|
||||
}
|
||||
return response.join(' • ');
|
||||
},
|
||||
updateBackdropImage() {
|
||||
this.backdropImageSource = this.getItemBackdrop(this.item.Id || '');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.item-container {
|
||||
margin: auto;
|
||||
max-width: calc(85vh * 16 / 9);
|
||||
}
|
||||
|
||||
.backdrop-image {
|
||||
max-width: 95em;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.item-details-container {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.gradient-container {
|
||||
background: linear-gradient(0deg, #0c0c0c, transparent);
|
||||
height: 30vh;
|
||||
}
|
||||
|
||||
.item-sub-heading {
|
||||
font-size: 0.8rem;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 30em) {
|
||||
.item-overview {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 90vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-row>
|
||||
<v-col cols="2">
|
||||
<v-img
|
||||
class="person-image elevation-2 ml-2"
|
||||
cover
|
||||
aspect-ratio="1"
|
||||
:src="`${$axios.defaults.baseURL}/Items/${item.Id}/Images/Primary`"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="7">
|
||||
<div
|
||||
class="text-subtitle-1 text--secondary font-weight-medium text-capitalize"
|
||||
>
|
||||
{{ $t('person') }}
|
||||
</div>
|
||||
<h1 class="text-h2 font-weight-light">{{ item.Name }}</h1>
|
||||
<v-col cols="9" class="pl-0 pr-0">
|
||||
<p class="item-overview">{{ item.Overview }}</p>
|
||||
</v-col>
|
||||
</v-col>
|
||||
<v-col cols="3">
|
||||
<v-row v-if="birthDate">
|
||||
<v-col cols="3" class="text--secondary">Birth</v-col>
|
||||
<v-col cols="9">
|
||||
{{ birthDate }}
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="deathDate">
|
||||
<v-col cols="3" class="text--secondary">Death</v-col>
|
||||
<v-col cols="9">
|
||||
{{ deathDate }}
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="birthPlace">
|
||||
<v-col cols="3" class="text--secondary">Birthplace</v-col>
|
||||
<v-col cols="9">
|
||||
{{ birthPlace }}
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="ProductionLocations && ProductionLocations.length > 0">
|
||||
<v-col cols="3" class="text--secondary">Birth place</v-col>
|
||||
<v-col cols="9">
|
||||
{{ item.ProductionLocations[0] }}
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="movies.length > 0">
|
||||
<v-col>
|
||||
<h1 class="text-h5 mb-2 ml-2 header">
|
||||
<span>{{ $t('movies') }}</span>
|
||||
</h1>
|
||||
<vueper-slides
|
||||
:bullets="false"
|
||||
:bullets-outside="false"
|
||||
:arrows-outside="false"
|
||||
:visible-slides="6"
|
||||
:slide-multiple="true"
|
||||
:breakpoints="breakpoints"
|
||||
fixed-height="true"
|
||||
>
|
||||
<vueper-slide v-for="movie in movies" :key="movie.Id">
|
||||
<template v-slot:content>
|
||||
<card :item="movie" />
|
||||
</template>
|
||||
</vueper-slide>
|
||||
|
||||
<template v-slot:arrow-left>
|
||||
<v-btn icon large>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<template v-slot:arrow-right>
|
||||
<v-btn icon large>
|
||||
<v-icon>mdi-arrow-right</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</vueper-slides>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-if="shows.length > 0">
|
||||
<v-col>
|
||||
<h1 class="text-h5 mb-2 ml-2 header">
|
||||
<span>{{ $t('shows') }}</span>
|
||||
</h1>
|
||||
<vueper-slides
|
||||
:bullets="false"
|
||||
:bullets-outside="false"
|
||||
:arrows-outside="false"
|
||||
:visible-slides="6"
|
||||
:slide-multiple="true"
|
||||
:breakpoints="breakpoints"
|
||||
fixed-height="true"
|
||||
>
|
||||
<vueper-slide v-for="show in shows" :key="show.Id">
|
||||
<template v-slot:content>
|
||||
<card :item="show" />
|
||||
</template>
|
||||
</vueper-slide>
|
||||
|
||||
<template v-slot:arrow-left>
|
||||
<v-btn icon large>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<template v-slot:arrow-right>
|
||||
<v-btn icon large>
|
||||
<v-icon>mdi-arrow-right</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</vueper-slides>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { BaseItemDto } from '~/api';
|
||||
import imageHelper from '~/mixins/imageHelper';
|
||||
import timeUtils from '~/mixins/timeUtils';
|
||||
|
||||
export default Vue.extend({
|
||||
mixins: [imageHelper, timeUtils],
|
||||
data() {
|
||||
return {
|
||||
item: {} as BaseItemDto,
|
||||
appearances: [] as BaseItemDto[],
|
||||
backdropImageSource: '',
|
||||
breakpoints: {
|
||||
600: {
|
||||
visibleSlides: 3
|
||||
},
|
||||
960: {
|
||||
visibleSlides: 4
|
||||
},
|
||||
1264: {
|
||||
visibleSlides: 6
|
||||
},
|
||||
1904: {
|
||||
visibleSlides: 6
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
birthDate() {
|
||||
if (this.$data.item.PremiereDate) {
|
||||
return new Date(this.$data.item.PremiereDate);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
deathDate: {
|
||||
get() {
|
||||
if (this.$data.item.EndDate) {
|
||||
return new Date(this.$data.item.EndDate);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
birthPlace: {
|
||||
get() {
|
||||
if (this.$data.item.ProductionLocations) {
|
||||
return this.$data.item.ProductionLocations[0];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
movies: {
|
||||
get() {
|
||||
return this.$data.appearances
|
||||
.filter((appearance: BaseItemDto) => {
|
||||
return appearance.Type === 'Movie';
|
||||
})
|
||||
.slice(0, 10);
|
||||
}
|
||||
},
|
||||
shows: {
|
||||
get() {
|
||||
return this.$data.appearances
|
||||
.filter((appearance: BaseItemDto) => {
|
||||
return appearance.Type === 'Series';
|
||||
})
|
||||
.slice(0, 10);
|
||||
}
|
||||
}
|
||||
},
|
||||
async beforeMount() {
|
||||
const item = (
|
||||
await this.$api.userLibrary.getItem({
|
||||
userId: this.$auth.user.Id,
|
||||
itemId: this.$route.params.itemId
|
||||
})
|
||||
).data;
|
||||
|
||||
if (item) {
|
||||
this.$store.dispatch('backdrop/set', { item });
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
const appearances = (
|
||||
await this.$api.items.getItems({
|
||||
uId: this.$auth.user.Id,
|
||||
userId: this.$auth.user.Id,
|
||||
personIds: this.$route.params.itemId,
|
||||
recursive: true,
|
||||
collapseBoxSetItems: false
|
||||
})
|
||||
).data.Items;
|
||||
|
||||
if (appearances) {
|
||||
this.appearances = appearances;
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.$store.dispatch('backdrop/clear');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.person-image {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.header span {
|
||||
padding-left: 0.25em;
|
||||
}
|
||||
.header::before {
|
||||
background-color: white;
|
||||
content: '';
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: 1px;
|
||||
bottom: 0.3em;
|
||||
left: 0;
|
||||
width: 1.25em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.vueperslides__track {
|
||||
position: relative;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
.vueperslides__arrows {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.vueperslides__arrows {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: -2.75em;
|
||||
right: 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.vueperslides__arrow {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.vueperslides__arrow--prev {
|
||||
margin-right: 0.75em;
|
||||
}
|
||||
.vueperslides:not(.no-shadow):not(.vueperslides--3d)
|
||||
.vueperslides__parallax-wrapper::after,
|
||||
.vueperslides:not(.no-shadow):not(.vueperslides--3d)
|
||||
.vueperslides__parallax-wrapper::before {
|
||||
box-shadow: none;
|
||||
}
|
||||
</style>
|
||||
@@ -21,6 +21,7 @@
|
||||
"@nuxtjs/axios",
|
||||
"@nuxtjs/auth-next",
|
||||
"@nuxtjs/vuetify",
|
||||
"@nuxtjs/date-fns",
|
||||
"nuxt-i18n",
|
||||
"jest"
|
||||
]
|
||||
|
||||
@@ -1733,6 +1733,13 @@
|
||||
consola "^2.15.0"
|
||||
defu "^3.1.0"
|
||||
|
||||
"@nuxtjs/date-fns@^1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@nuxtjs/date-fns/-/date-fns-1.4.0.tgz#7cb0a03938e95bced9b089820c32982a9e0638c7"
|
||||
integrity sha512-YuAKM9Y1Ig3u5ZPlW466taL4S7llE5oxgM4Vnth3pEqpRtnAF3jlxlQ3MYv5HcdOnhWBx2pgrbRTSQmCqKF/4w==
|
||||
dependencies:
|
||||
date-fns "^2.16.1"
|
||||
|
||||
"@nuxtjs/eslint-config-typescript@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@nuxtjs/eslint-config-typescript/-/eslint-config-typescript-4.0.0.tgz#0873aab4ac74cb2b9d7f7b0f8bc9449a0f57a6a5"
|
||||
@@ -4627,6 +4634,11 @@ data-urls@^2.0.0:
|
||||
whatwg-mimetype "^2.3.0"
|
||||
whatwg-url "^8.0.0"
|
||||
|
||||
date-fns@^2.16.1:
|
||||
version "2.16.1"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b"
|
||||
integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==
|
||||
|
||||
de-indent@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
||||
@@ -5945,6 +5957,11 @@ follow-redirects@^1.0.0, follow-redirects@^1.10.0:
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
|
||||
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
|
||||
|
||||
fontsource-noto-sans@^3.0.10:
|
||||
version "3.0.10"
|
||||
resolved "https://registry.yarnpkg.com/fontsource-noto-sans/-/fontsource-noto-sans-3.0.10.tgz#1fffa82a748b91c2a4c0b2d97bbd5bd7245eed8b"
|
||||
integrity sha512-Gn8ucYOSk2p2Fzj9x4K6DMoX+bhk6nfwVD7h59QFr5uJL8ha2B5i3It9ZIy3AV1+qbgIY3kCmyx9pSWnpB85SQ==
|
||||
|
||||
for-in@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||
@@ -7928,6 +7945,11 @@ known-css-properties@^0.19.0:
|
||||
resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.19.0.tgz#5d92b7fa16c72d971bda9b7fe295bdf61836ee5b"
|
||||
integrity sha512-eYboRV94Vco725nKMlpkn3nV2+96p9c3gKXRsYqAJSswSENvBhN7n5L+uDhY58xQa0UukWsDMTGELzmD8Q+wTA==
|
||||
|
||||
langs@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/langs/-/langs-2.0.0.tgz#00c32ce48152a49a614450b9ba2632ab58a0a364"
|
||||
integrity sha1-AMMs5IFSpJphRFC5uiYyq1igo2Q=
|
||||
|
||||
last-call-webpack-plugin@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555"
|
||||
|
||||
Reference in New Issue
Block a user