feat: default sub/audio setting

This commit is contained in:
Fredrik Burmester
2024-09-03 08:54:48 +03:00
parent 10e0a45cd4
commit 2509a8d6e2
5 changed files with 452 additions and 318 deletions

View File

@@ -35,12 +35,14 @@ export default function settings() {
}}
>
<View className="p-4 flex flex-col gap-y-4">
<Text className="font-bold text-2xl">Information</Text>
<View>
<Text className="font-bold text-lg mb-2">Information</Text>
<View className="flex flex-col rounded-xl mb-4 overflow-hidden border-neutral-800 divide-y-2 divide-solid divide-neutral-800 ">
<ListItem title="User" subTitle={user?.Name} />
<ListItem title="Server" subTitle={api?.basePath} />
</View>
</View>
<SettingToggles />
@@ -71,8 +73,8 @@ export default function settings() {
Delete all logs
</Button>
</View>
<Text className="font-bold text-2xl">Logs</Text>
<View>
<Text className="font-bold text-lg mb-2">Logs</Text>
<View className="flex flex-col space-y-2">
{logs?.map((log, index) => (
<View key={index} className="bg-neutral-900 rounded-xl p-3">
@@ -93,6 +95,7 @@ export default function settings() {
)}
</View>
</View>
</View>
</ScrollView>
);
}

View File

@@ -9,6 +9,7 @@ import {
import { useEffect, useMemo } from "react";
import { MediaStream } from "@jellyfin/sdk/lib/generated-client/models";
import { tc } from "@/utils/textTools";
import { useSettings } from "@/utils/atoms/settings";
interface Props extends React.ComponentProps<typeof View> {
source: MediaSourceInfo;
@@ -22,6 +23,8 @@ export const AudioTrackSelector: React.FC<Props> = ({
selected,
...props
}) => {
const [settings] = useSettings();
const audioStreams = useMemo(
() => source.MediaStreams?.filter((x) => x.Type === "Audio"),
[source]
@@ -33,9 +36,22 @@ export const AudioTrackSelector: React.FC<Props> = ({
);
useEffect(() => {
const defaultAudioIndex = audioStreams?.find(
(x) => x.Language === settings?.defaultAudioLanguage
)?.Index;
if (defaultAudioIndex !== undefined && defaultAudioIndex !== null) {
onChange(defaultAudioIndex);
return;
}
const index = source.DefaultAudioStreamIndex;
if (index !== undefined && index !== null) onChange(index);
}, []);
if (index !== undefined && index !== null) {
console.log("DefaultAudioStreamIndex", index);
onChange(index);
return;
}
onChange(0);
}, [audioStreams, settings]);
return (
<View

View File

@@ -9,6 +9,7 @@ import {
import { useEffect, useMemo } from "react";
import { MediaStream } from "@jellyfin/sdk/lib/generated-client/models";
import { tc } from "@/utils/textTools";
import { useSettings } from "@/utils/atoms/settings";
interface Props extends React.ComponentProps<typeof View> {
source: MediaSourceInfo;
@@ -22,6 +23,8 @@ export const SubtitleTrackSelector: React.FC<Props> = ({
selected,
...props
}) => {
const [settings] = useSettings();
const subtitleStreams = useMemo(
() => source.MediaStreams?.filter((x) => x.Type === "Subtitle") ?? [],
[source]
@@ -33,13 +36,21 @@ export const SubtitleTrackSelector: React.FC<Props> = ({
);
useEffect(() => {
const index = source.DefaultSubtitleStreamIndex;
if (index !== undefined && index !== null) {
onChange(index);
} else {
onChange(-1);
// const index = source.DefaultAudioStreamIndex;
// if (index !== undefined && index !== null) {
// onChange(index);
// return;
// }
const defaultSubIndex = subtitleStreams?.find(
(x) => x.Language === settings?.defaultSubtitleLanguage?.value
)?.Index;
if (defaultSubIndex !== undefined && defaultSubIndex !== null) {
onChange(defaultSubIndex);
return;
}
}, []);
onChange(-1);
}, [subtitleStreams, settings]);
if (subtitleStreams.length === 0) return null;

View File

@@ -1,5 +1,9 @@
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { DownloadOptions, useSettings } from "@/utils/atoms/settings";
import {
DefaultLanguageOption,
DownloadOptions,
useSettings,
} from "@/utils/atoms/settings";
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useAtom } from "jotai";
@@ -11,6 +15,14 @@ import { Input } from "../common/Input";
import { useState } from "react";
import { Button } from "../Button";
const LANGUAGES: DefaultLanguageOption[] = [
{ label: "eng", value: "eng" },
{
label: "sv",
value: "sv",
},
];
export const SettingToggles: React.FC = () => {
const [settings, updateSettings] = useSettings();
@@ -44,35 +56,25 @@ export const SettingToggles: React.FC = () => {
});
return (
<View className="flex flex-col rounded-xl mb-4 overflow-hidden border-neutral-800 divide-y-2 divide-solid divide-neutral-800 ">
<View className="flex flex-row items-center justify-between bg-neutral-900 p-4">
<View className="shrink">
<Text className="font-semibold">Auto rotate</Text>
<Text className="text-xs opacity-50">
Important on android since the video player orientation is locked to
the app orientation.
</Text>
</View>
<Switch
value={settings?.autoRotate}
onValueChange={(value) => updateSettings({ autoRotate: value })}
/>
</View>
{/* <View
<View>
<View>
<Text className="text-lg font-bold mb-2">Downloads</Text>
<View className="flex flex-col rounded-xl mb-4 overflow-hidden divide-y-2 divide-solid divide-neutral-800">
<View
className={`
flex flex-row items-center space-x-2 justify-between bg-neutral-900 p-4
`}
>
<View className="flex flex-col shrink">
<Text className="font-semibold">Download quality</Text>
<Text className="font-semibold">Audio language</Text>
<Text className="text-xs opacity-50">
Choose the download quality.
Choose a default audio language for downloads.
</Text>
</View>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<TouchableOpacity className="bg-neutral-800 rounded-lg border-neutral-900 border px-3 py-2 flex flex-row items-center justify-between">
<Text>{settings?.downloadQuality?.label}</Text>
<Text>{settings?.defaultAudioLanguage?.label || "None"}</Text>
</TouchableOpacity>
</DropdownMenu.Trigger>
<DropdownMenu.Content
@@ -84,20 +86,107 @@ export const SettingToggles: React.FC = () => {
collisionPadding={8}
sideOffset={8}
>
<DropdownMenu.Label>Quality</DropdownMenu.Label>
{DownloadOptions.map((option) => (
<DropdownMenu.Label>Languages</DropdownMenu.Label>
<DropdownMenu.Item
key={option.value}
key={"none-audio"}
onSelect={() => {
updateSettings({ downloadQuality: option });
updateSettings({
defaultAudioLanguage: null,
});
}}
>
<DropdownMenu.ItemTitle>{option.label}</DropdownMenu.ItemTitle>
<DropdownMenu.ItemTitle>None</DropdownMenu.ItemTitle>
</DropdownMenu.Item>
{LANGUAGES.map((l) => (
<DropdownMenu.Item
key={l.value}
onSelect={() => {
updateSettings({
defaultAudioLanguage: l,
});
}}
>
<DropdownMenu.ItemTitle>{l.label}</DropdownMenu.ItemTitle>
</DropdownMenu.Item>
))}
</DropdownMenu.Content>
</DropdownMenu.Root>
</View> */}
</View>
<View
className={`
flex flex-row items-center space-x-2 justify-between bg-neutral-900 p-4
`}
>
<View className="flex flex-col shrink">
<Text className="font-semibold">Subtitle language</Text>
<Text className="text-xs opacity-50">
Choose a default subtitle language for downloads.
</Text>
</View>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<TouchableOpacity className="bg-neutral-800 rounded-lg border-neutral-900 border px-3 py-2 flex flex-row items-center justify-between">
<Text>
{settings?.defaultSubtitleLanguage?.label || "None"}
</Text>
</TouchableOpacity>
</DropdownMenu.Trigger>
<DropdownMenu.Content
loop={true}
side="bottom"
align="start"
alignOffset={0}
avoidCollisions={true}
collisionPadding={8}
sideOffset={8}
>
<DropdownMenu.Label>Languages</DropdownMenu.Label>
<DropdownMenu.Item
key={"none-subs"}
onSelect={() => {
updateSettings({
defaultSubtitleLanguage: null,
});
}}
>
<DropdownMenu.ItemTitle>None</DropdownMenu.ItemTitle>
</DropdownMenu.Item>
{LANGUAGES.map((l) => (
<DropdownMenu.Item
key={l.value}
onSelect={() => {
updateSettings({
defaultSubtitleLanguage: l,
});
}}
>
<DropdownMenu.ItemTitle>{l.label}</DropdownMenu.ItemTitle>
</DropdownMenu.Item>
))}
</DropdownMenu.Content>
</DropdownMenu.Root>
</View>
</View>
</View>
<View>
<Text className="text-lg font-bold mb-2">Other</Text>
<View className="flex flex-col rounded-xl mb-4 overflow-hidden divide-y-2 divide-solid divide-neutral-800">
<View className="flex flex-row items-center justify-between bg-neutral-900 p-4">
<View className="shrink">
<Text className="font-semibold">Auto rotate</Text>
<Text className="text-xs opacity-50">
Important on android since the video player orientation is
locked to the app orientation.
</Text>
</View>
<Switch
value={settings?.autoRotate}
onValueChange={(value) => updateSettings({ autoRotate: value })}
/>
</View>
<View className="flex flex-row items-center justify-between bg-neutral-900 p-4">
<View className="shrink">
<Text className="font-semibold">Start videos in fullscreen</Text>
@@ -118,8 +207,8 @@ export const SettingToggles: React.FC = () => {
<View className="flex flex-col shrink">
<Text className="font-semibold">Use external player (VLC)</Text>
<Text className="text-xs opacity-50 shrink">
Open all videos in VLC instead of the default player. This requries
VLC to be installed on the phone.
Open all videos in VLC instead of the default player. This
requries VLC to be installed on the phone.
</Text>
</View>
<Switch
@@ -163,7 +252,9 @@ export const SettingToggles: React.FC = () => {
<Text className="font-semibold">{mlc.Name}</Text>
</View>
<Switch
value={settings?.mediaListCollectionIds?.includes(mlc.Id!)}
value={settings?.mediaListCollectionIds?.includes(
mlc.Id!
)}
onValueChange={(value) => {
if (!settings.mediaListCollectionIds) {
updateSettings({
@@ -204,13 +295,15 @@ export const SettingToggles: React.FC = () => {
<View className="flex flex-col shrink">
<Text className="font-semibold">Force direct play</Text>
<Text className="text-xs opacity-50 shrink">
This will always request direct play. This is good if you want to
try to stream movies you think the device supports.
This will always request direct play. This is good if you want
to try to stream movies you think the device supports.
</Text>
</View>
<Switch
value={settings?.forceDirectPlay}
onValueChange={(value) => updateSettings({ forceDirectPlay: value })}
onValueChange={(value) =>
updateSettings({ forceDirectPlay: value })
}
/>
</View>
@@ -223,8 +316,8 @@ export const SettingToggles: React.FC = () => {
<View className="flex flex-col shrink">
<Text className="font-semibold">Device profile</Text>
<Text className="text-xs opacity-50">
A profile used for deciding what audio and video codecs the device
supports.
A profile used for deciding what audio and video codecs the
device supports.
</Text>
</View>
<DropdownMenu.Root>
@@ -354,5 +447,7 @@ export const SettingToggles: React.FC = () => {
)}
</View>
</View>
</View>
</View>
);
};

View File

@@ -32,6 +32,11 @@ export type LibraryOptions = {
showStats: boolean;
};
export type DefaultLanguageOption = {
value: string;
label: string;
};
type Settings = {
autoRotate?: boolean;
forceLandscapeInVideoPlayer?: boolean;
@@ -45,6 +50,8 @@ type Settings = {
openInVLC?: boolean;
downloadQuality?: DownloadOption;
libraryOptions: LibraryOptions;
defaultSubtitleLanguage: DefaultLanguageOption | null;
defaultAudioLanguage: DefaultLanguageOption | null;
};
/**
@@ -75,6 +82,8 @@ const loadSettings = async (): Promise<Settings> => {
showTitles: true,
showStats: true,
},
defaultAudioLanguage: null,
defaultSubtitleLanguage: null,
};
try {