feat: Save audio and subtitles preferences

This commit is contained in:
Aleksi Lassila
2024-05-09 03:00:34 +03:00
parent 1e9675dc72
commit 605936d603
5 changed files with 94 additions and 38 deletions

View File

@@ -9,6 +9,14 @@
import { appState } from '../../stores/app-state.store';
import { onDestroy } from 'svelte';
import { modalStack, modalStackTop } from '../Modal/modal.store';
import { createLocalStorageStore } from '../../stores/localstorage.store';
import { get } from 'svelte/store';
import { ISO_2_LANGUAGES } from '../../utils/iso-2-languages';
type MediaLanguageStore = {
subtitles?: string;
audio?: string;
};
export let id: string;
export let modalId: symbol;
@@ -41,18 +49,24 @@
id: string,
options: { audioStreamIndex?: number; bitrate?: number; playbackPosition?: number } = {}
) {
const itemP = jellyfinApi.getLibraryItem(id);
const jellyfinPlaybackInfoP = itemP.then((item) =>
jellyfinApi.getPlaybackInfo(
id,
getDeviceProfile(),
options.playbackPosition || item?.UserData?.PlaybackPositionTicks || 0,
options.bitrate || getQualities(item?.Height || 1080)[0]?.maxBitrate,
options.audioStreamIndex || undefined
)
const item = await jellyfinApi.getLibraryItem(id);
const mediaLanguagesStore = createLocalStorageStore<MediaLanguageStore>(
'media-tracks-' + (item?.SeriesName || id),
{}
);
const storedAudioStreamIndex = item?.MediaStreams?.find(
(s) => s.Type === 'Audio' && s.Language === mediaLanguagesStore.get().audio
)?.Index;
const audioStreamIndex = options.audioStreamIndex ?? storedAudioStreamIndex ?? undefined;
const jellyfinPlaybackInfo = await jellyfinApi.getPlaybackInfo(
id,
getDeviceProfile(),
options.playbackPosition || item?.UserData?.PlaybackPositionTicks || 0,
options.bitrate || getQualities(item?.Height || 1080)[0]?.maxBitrate,
audioStreamIndex
);
const item = await itemP;
const jellyfinPlaybackInfo = await jellyfinPlaybackInfoP;
if (!item || !jellyfinPlaybackInfo) {
console.error('No item or playback info', item, jellyfinPlaybackInfo);
@@ -70,14 +84,30 @@
const mediaSource = jellyfinPlaybackInfo.MediaSources?.[0];
const storedSubtitlesLang = mediaLanguagesStore.get().subtitles;
if (options.audioStreamIndex) {
const audioLang = mediaSource?.MediaStreams?.[options.audioStreamIndex]?.Language;
mediaLanguagesStore.update((prev) => ({
...prev,
audio: audioLang || undefined
}));
}
let subtitles: Subtitles | undefined;
for (const stream of mediaSource?.MediaStreams || []) {
if (stream.Type === 'Subtitle' && stream.IsDefault) {
if (
stream.Type === 'Subtitle' &&
(storedSubtitlesLang !== undefined
? stream.Language === storedSubtitlesLang
: stream.IsDefault)
) {
subtitles = {
kind: 'subtitles',
srclang: stream.Language || '',
url: `${$appState.user?.settings.jellyfin.baseUrl}/Videos/${id}/${mediaSource?.Id}/Subtitles/${stream.Index}/${stream.Level}/Stream.vtt`,
language: 'English'
// @ts-ignore
language: ISO_2_LANGUAGES[stream?.Language || '']?.name || 'English'
};
}
}
@@ -90,13 +120,34 @@
language: 'English'
})) || [];
const selectSubtitles = (subtitles?: Subtitles) => {
mediaLanguagesStore.update((prev) => ({
...prev,
subtitles: subtitles?.srclang || ''
}));
if (subtitleInfo) {
if (subtitles)
subtitleInfo = {
...subtitleInfo,
subtitles
};
else
subtitleInfo = {
...subtitleInfo,
subtitles: undefined
};
}
};
subtitleInfo = {
subtitles,
availableSubtitles
availableSubtitles,
selectSubtitles
};
playbackInfo = {
audioStreamIndex: options.audioStreamIndex || mediaSource?.DefaultAudioStreamIndex || -1,
audioStreamIndex: audioStreamIndex ?? mediaSource?.DefaultAudioStreamIndex ?? -1,
audioTracks:
mediaSource?.MediaStreams?.filter((s) => s.Type === 'Audio').map((s) => ({
index: s.Index || -1,

View File

@@ -81,10 +81,17 @@
}
}
$: if (subtitleInfo?.subtitles) {
console.log('Unpausing because subtitles were set');
video.play();
}
$: subtitleInfo && updateSubtitlesVisibility();
const updateSubtitlesVisibility = () => {
const tracks = video?.textTracks;
for (const track of tracks) {
track.mode = track.id === subtitleInfo?.subtitles?.url ? 'showing' : 'disabled';
}
};
// $: if (subtitleInfo?.subtitles) {
// console.log('Unpausing because subtitles were set');
// video.play();
// }
</script>
<!-- svelte-ignore a11y-media-has-caption -->
@@ -110,13 +117,18 @@
crossorigin="anonymous"
class="w-full h-full"
>
{#if subtitleInfo?.subtitles}
<track
src={subtitleInfo.subtitles.url}
kind={subtitleInfo.subtitles.kind}
srclang={subtitleInfo.subtitles.srclang}
default={true}
label={subtitleInfo.subtitles.language}
/>
{#if subtitleInfo?.availableSubtitles}
{#each subtitleInfo.availableSubtitles as subtitle}
<track
default={subtitle.url === subtitleInfo.subtitles?.url}
id={subtitle.url}
src={subtitle.url}
kind={subtitle.kind}
srclang={subtitle.srclang}
label={subtitle.language}
/>
{/each}
<!--{:else}-->
<!-- <track kind="subtitles" src="" />-->
{/if}
</video>

View File

@@ -8,7 +8,7 @@
import type { Selectable } from '../../selectable';
import { modalStack } from '../Modal/modal.store';
import SelectSubtitlesModal from './SelectSubtitlesModal.svelte';
import { ChatBubble, TextAlignLeft, Update } from 'radix-icons-svelte';
import { ChatBubble, TextAlignLeft } from 'radix-icons-svelte';
import IconButton from './IconButton.svelte';
import SelectAudioModal from './SelectAudioModal.svelte';
import Spinner from '../Utils/Spinner.svelte';
@@ -58,16 +58,7 @@
function selectSubtitles(subtitles?: Subtitles) {
if (subtitleInfo) {
if (subtitles)
subtitleInfo = {
...subtitleInfo,
subtitles
};
else
subtitleInfo = {
...subtitleInfo,
subtitles: undefined
};
subtitleInfo.selectSubtitles(subtitles);
} else {
console.error('No subtitle info when selecting subtitles');
}

View File

@@ -6,6 +6,7 @@ import VideoPlayerModal from './JellyfinVideoPlayerModal.svelte';
export type SubtitleInfo = {
subtitles?: Subtitles;
availableSubtitles: Subtitles[];
selectSubtitles: (subtitles?: Subtitles) => void;
};
export type Subtitles = {

View File

@@ -5,6 +5,7 @@ export function createLocalStorageStore<T>(key: string, defaultValue: T) {
return {
subscribe: store.subscribe,
get: () => get(store),
set: (value: T) => {
localStorage.setItem(key, JSON.stringify(value));
store.set(value);