|
|
|
|
@ -1,8 +1,8 @@
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import EpisodeThumb from '@/components/EpisodeThumb.vue'
|
|
|
|
|
import MediaInfo from '@/components/MediaInfo.vue'
|
|
|
|
|
import jfapi, { type Item } from '@/jfapi'
|
|
|
|
|
import { ref } from 'vue'
|
|
|
|
|
import jfapi, { type Item, type MediaStream } from '@/jfapi'
|
|
|
|
|
import { onBeforeMount, ref } from 'vue'
|
|
|
|
|
import { useRoute, RouterLink } from 'vue-router'
|
|
|
|
|
|
|
|
|
|
const route = useRoute()
|
|
|
|
|
@ -12,70 +12,79 @@ const itemId = String(route.params.id)
|
|
|
|
|
const item = ref<Item>()
|
|
|
|
|
const directors = ref<string[]>([])
|
|
|
|
|
const writers = ref<string[]>([])
|
|
|
|
|
const selectSubTrack = ref('')
|
|
|
|
|
const selectAudioTrack = ref('')
|
|
|
|
|
const selectSubTrack = ref<MediaStream>()
|
|
|
|
|
const selectAudioTrack = ref<MediaStream>()
|
|
|
|
|
const nextUp = ref<Item>()
|
|
|
|
|
const numAudioStreams = ref(0)
|
|
|
|
|
const userid = localStorage.getItem('jf_userid') || ''
|
|
|
|
|
jfapi.GetItem(userid, itemId).then((data) => {
|
|
|
|
|
if (data === null) {
|
|
|
|
|
// error
|
|
|
|
|
} else {
|
|
|
|
|
item.value = data
|
|
|
|
|
if (item.value.IsFolder) {
|
|
|
|
|
console.log('Item is a Series')
|
|
|
|
|
// item is a full series
|
|
|
|
|
jfapi.GetNextUpInSeries(userid, item.value.Id || '').then((data) => {
|
|
|
|
|
if (data?.Items && data.Items.length > 0) {
|
|
|
|
|
nextUp.value = data.Items[0]
|
|
|
|
|
} else {
|
|
|
|
|
jfapi.GetFirstEpisode(userid, item.value?.Id).then((data) => {
|
|
|
|
|
if (data?.Items) {
|
|
|
|
|
nextUp.value = data.Items[0]
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
const VideoStream = ref<MediaStream>()
|
|
|
|
|
const AudioStreams = ref<MediaStream[]>([])
|
|
|
|
|
const SubtitleStreams = ref<MediaStream[]>([])
|
|
|
|
|
onBeforeMount(() => {
|
|
|
|
|
jfapi.GetItem(itemId).then((data) => {
|
|
|
|
|
if (data === null) {
|
|
|
|
|
// error
|
|
|
|
|
} else {
|
|
|
|
|
item.value = data
|
|
|
|
|
if (item.value.IsFolder) {
|
|
|
|
|
console.log('Item is a Series')
|
|
|
|
|
// item is a full series
|
|
|
|
|
jfapi.GetNextUpInSeries(item.value.Id || '').then((data) => {
|
|
|
|
|
if (data?.Items && data.Items.length > 0) {
|
|
|
|
|
nextUp.value = data.Items[0]
|
|
|
|
|
} else {
|
|
|
|
|
jfapi.GetFirstEpisode(item.value?.Id || '').then((data) => {
|
|
|
|
|
if (data?.Items) {
|
|
|
|
|
nextUp.value = data.Items[0]
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (item.value !== undefined && item.value.MediaStreams !== undefined) {
|
|
|
|
|
for (const stream of item.value.MediaStreams) {
|
|
|
|
|
if (stream.Type.toLowerCase() === 'audio') {
|
|
|
|
|
numAudioStreams.value++
|
|
|
|
|
if (stream.IsForced && !selectAudioTrack.value.includes('Forced')) {
|
|
|
|
|
selectAudioTrack.value = stream.DisplayTitle
|
|
|
|
|
} else if (
|
|
|
|
|
stream.IsDefault &&
|
|
|
|
|
!selectAudioTrack.value.includes('Forced') &&
|
|
|
|
|
!selectAudioTrack.value.includes('Default')
|
|
|
|
|
) {
|
|
|
|
|
selectAudioTrack.value = stream.DisplayTitle
|
|
|
|
|
}
|
|
|
|
|
} else if (stream.Type.toLowerCase() === 'subtitle') {
|
|
|
|
|
if (stream.IsForced && !selectSubTrack.value.includes('Forced')) {
|
|
|
|
|
selectSubTrack.value = stream.DisplayTitle
|
|
|
|
|
} else if (
|
|
|
|
|
stream.IsDefault &&
|
|
|
|
|
!selectSubTrack.value.includes('Forced') &&
|
|
|
|
|
!selectSubTrack.value.includes('Default')
|
|
|
|
|
) {
|
|
|
|
|
console.log(stream)
|
|
|
|
|
selectSubTrack.value = stream.DisplayTitle
|
|
|
|
|
if (item.value !== undefined && item.value.MediaStreams !== undefined) {
|
|
|
|
|
for (const stream of item.value.MediaStreams) {
|
|
|
|
|
if (stream.Type.toLowerCase() === 'video') {
|
|
|
|
|
VideoStream.value = stream
|
|
|
|
|
} else if (stream.Type.toLowerCase() === 'audio') {
|
|
|
|
|
AudioStreams.value.push(stream)
|
|
|
|
|
numAudioStreams.value++
|
|
|
|
|
if (stream.IsForced && !selectAudioTrack.value?.DisplayTitle.includes('Forced')) {
|
|
|
|
|
selectAudioTrack.value = stream
|
|
|
|
|
} else if (
|
|
|
|
|
stream.IsDefault &&
|
|
|
|
|
!selectAudioTrack.value?.DisplayTitle.includes('Forced') &&
|
|
|
|
|
!selectAudioTrack.value?.DisplayTitle.includes('Default')
|
|
|
|
|
) {
|
|
|
|
|
selectAudioTrack.value = stream
|
|
|
|
|
}
|
|
|
|
|
} else if (stream.Type.toLowerCase() === 'subtitle') {
|
|
|
|
|
SubtitleStreams.value.push(stream)
|
|
|
|
|
if (stream.IsForced && !selectSubTrack.value?.DisplayTitle.includes('Forced')) {
|
|
|
|
|
selectSubTrack.value = stream
|
|
|
|
|
} else if (
|
|
|
|
|
stream.IsDefault &&
|
|
|
|
|
!selectSubTrack.value?.DisplayTitle.includes('Forced') &&
|
|
|
|
|
!selectSubTrack.value?.DisplayTitle.includes('Default')
|
|
|
|
|
) {
|
|
|
|
|
console.log(stream)
|
|
|
|
|
selectSubTrack.value = stream
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const person of item.value.People) {
|
|
|
|
|
if (person.Type.toLowerCase() === 'director') {
|
|
|
|
|
directors.value.push(person.Name)
|
|
|
|
|
} else if (person.Type.toLowerCase() === 'writer') {
|
|
|
|
|
writers.value.push(person.Name)
|
|
|
|
|
for (const person of item.value.People) {
|
|
|
|
|
if (person.Type.toLowerCase() === 'director') {
|
|
|
|
|
directors.value.push(person.Name)
|
|
|
|
|
} else if (person.Type.toLowerCase() === 'writer') {
|
|
|
|
|
writers.value.push(person.Name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
console.log(writers.value, directors.value)
|
|
|
|
|
console.log(writers.value, directors.value)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const arrayRange = (start: number, stop: number) =>
|
|
|
|
|
Array.from({ length: (stop - start) / 1 + 1 }, (value, index) => start + index * 1)
|
|
|
|
|
Array.from({ length: (stop - start) / 1 }, (value, index) => start + index * 1)
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
@ -105,12 +114,12 @@ const arrayRange = (start: number, stop: number) =>
|
|
|
|
|
:src="`${
|
|
|
|
|
item.ParentLogoImageTag && item.ParentLogoItemId
|
|
|
|
|
? jfapi.LogoImageUrl(item.ParentLogoItemId, item.ParentLogoImageTag)
|
|
|
|
|
: jfapi.LogoImageUrl(item.Id, item.ImageTags.Logo)
|
|
|
|
|
: jfapi.LogoImageUrl(item.Id, item.ImageTags.Logo || '')
|
|
|
|
|
}`"
|
|
|
|
|
alt=""
|
|
|
|
|
/>
|
|
|
|
|
<div class="title" v-else>{{ item.SeriesName ? item.SeriesName : item.Name }}</div>
|
|
|
|
|
<MediaInfo />
|
|
|
|
|
<MediaInfo :itemId="item.Id" :endsAt="item.Type !== 'Series'" size="lg" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="info-wrapper">
|
|
|
|
|
@ -135,22 +144,22 @@ const arrayRange = (start: number, stop: number) =>
|
|
|
|
|
<div class="media-streams" v-if="item.MediaStreams !== undefined">
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
<div class="stream-type">Video</div>
|
|
|
|
|
<div class="stream-value">{{ item?.MediaStreams[0].DisplayTitle }}</div>
|
|
|
|
|
<div class="stream-value">{{ VideoStream?.DisplayTitle }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
<div class="stream-type">Audio</div>
|
|
|
|
|
<div class="stream-selector" v-if="numAudioStreams > 1">
|
|
|
|
|
<div class="stream-selector" v-if="AudioStreams.length > 1">
|
|
|
|
|
<select name="audio-stream-select" :id="`${item?.Id}-audio-select`">
|
|
|
|
|
<option
|
|
|
|
|
:value="`${item?.MediaStreams}`"
|
|
|
|
|
v-for="i in arrayRange(1, numAudioStreams)"
|
|
|
|
|
:value="`${AudioStreams[i].DisplayTitle}`"
|
|
|
|
|
v-for="i in arrayRange(0, AudioStreams.length)"
|
|
|
|
|
:key="`audio-stream-${i}`"
|
|
|
|
|
>
|
|
|
|
|
{{ item?.MediaStreams[i].DisplayTitle }}
|
|
|
|
|
{{ AudioStreams[i].DisplayTitle }}
|
|
|
|
|
</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stream-value" v-else>{{ item?.MediaStreams[1].DisplayTitle }}</div>
|
|
|
|
|
<div class="stream-value" v-else>{{ AudioStreams[0].DisplayTitle }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
<div class="stream-type">Subtitles</div>
|
|
|
|
|
@ -158,16 +167,16 @@ const arrayRange = (start: number, stop: number) =>
|
|
|
|
|
<select
|
|
|
|
|
name="subtitle-stream-select"
|
|
|
|
|
:id="`${item?.Id}-subtitle-select`"
|
|
|
|
|
v-if="item?.MediaStreams.length > numAudioStreams + 1"
|
|
|
|
|
v-if="SubtitleStreams.length > 0"
|
|
|
|
|
>
|
|
|
|
|
<option value="Off">Off</option>
|
|
|
|
|
<option
|
|
|
|
|
:value="`${item?.MediaStreams[i].DisplayTitle}`"
|
|
|
|
|
v-for="i in arrayRange(numAudioStreams + 1, item.MediaStreams.length - 1)"
|
|
|
|
|
:selected="item?.MediaStreams[i].DisplayTitle === selectSubTrack"
|
|
|
|
|
v-for="i in arrayRange(0, SubtitleStreams.length)"
|
|
|
|
|
:value="`${SubtitleStreams[i]?.DisplayTitle}`"
|
|
|
|
|
:selected="SubtitleStreams[i]?.DisplayTitle === selectSubTrack?.DisplayTitle"
|
|
|
|
|
:key="`subtitle-stream-${i}`"
|
|
|
|
|
>
|
|
|
|
|
{{ item?.MediaStreams[i].DisplayTitle }}
|
|
|
|
|
{{ SubtitleStreams[i]?.DisplayTitle }}
|
|
|
|
|
</option>
|
|
|
|
|
</select>
|
|
|
|
|
<div class="stream-value" v-else>None</div>
|
|
|
|
|
@ -175,6 +184,9 @@ const arrayRange = (start: number, stop: number) =>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="title episode-title" v-if="item.Type === 'Episode'">
|
|
|
|
|
{{ `S${item.ParentIndexNumber}:E${item.IndexNumber} - ${item.Name}` }}
|
|
|
|
|
</div>
|
|
|
|
|
<blockquote class="tagline" v-if="item?.Taglines.length > 0">
|
|
|
|
|
{{ item?.Taglines[0] }}
|
|
|
|
|
</blockquote>
|
|
|
|
|
@ -218,6 +230,9 @@ const arrayRange = (start: number, stop: number) =>
|
|
|
|
|
margin: auto;
|
|
|
|
|
padding: 3em 0;
|
|
|
|
|
}
|
|
|
|
|
.subheading {
|
|
|
|
|
width: 70%;
|
|
|
|
|
}
|
|
|
|
|
.item-info {
|
|
|
|
|
padding: 3em 4em;
|
|
|
|
|
width: 800px;
|
|
|
|
|
@ -238,8 +253,7 @@ const arrayRange = (start: number, stop: number) =>
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 1.5em;
|
|
|
|
|
width: 95%;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
margin-top: 2em;
|
|
|
|
|
margin: 2em auto;
|
|
|
|
|
}
|
|
|
|
|
.media-title {
|
|
|
|
|
min-height: 200px;
|
|
|
|
|
@ -252,6 +266,13 @@ const arrayRange = (start: number, stop: number) =>
|
|
|
|
|
font-size: 52px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
font-family: 'DM Sans', sans-serif;
|
|
|
|
|
text-align: center;
|
|
|
|
|
width: 90%;
|
|
|
|
|
}
|
|
|
|
|
.title.episode-title {
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
text-align: left;
|
|
|
|
|
}
|
|
|
|
|
.logo {
|
|
|
|
|
display: flex;
|
|
|
|
|
|