diff --git a/app/(auth)/(tabs)/(home)/settings.tsx b/app/(auth)/(tabs)/(home)/settings.tsx
index c688f9b2..b89821ce 100644
--- a/app/(auth)/(tabs)/(home)/settings.tsx
+++ b/app/(auth)/(tabs)/(home)/settings.tsx
@@ -35,11 +35,13 @@ export default function settings() {
}}
>
- Information
+
+ Information
-
-
-
+
+
+
+
@@ -71,26 +73,27 @@ export default function settings() {
Delete all logs
-
- Logs
-
- {logs?.map((log, index) => (
-
-
+ Logs
+
+ {logs?.map((log, index) => (
+
+
- {log.level}
-
- {log.message}
-
- ))}
- {logs?.length === 0 && (
- No logs available
- )}
+ >
+ {log.level}
+
+ {log.message}
+
+ ))}
+ {logs?.length === 0 && (
+ No logs available
+ )}
+
diff --git a/components/AudioTrackSelector.tsx b/components/AudioTrackSelector.tsx
index 5b3f91db..9a55adf3 100644
--- a/components/AudioTrackSelector.tsx
+++ b/components/AudioTrackSelector.tsx
@@ -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 {
source: MediaSourceInfo;
@@ -22,6 +23,8 @@ export const AudioTrackSelector: React.FC = ({
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 = ({
);
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 (
{
source: MediaSourceInfo;
@@ -22,6 +23,8 @@ export const SubtitleTrackSelector: React.FC = ({
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 = ({
);
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;
diff --git a/components/settings/SettingToggles.tsx b/components/settings/SettingToggles.tsx
index c10f84f9..b9330003 100644
--- a/components/settings/SettingToggles.tsx
+++ b/components/settings/SettingToggles.tsx
@@ -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,314 +56,397 @@ export const SettingToggles: React.FC = () => {
});
return (
-
-
-
- Auto rotate
-
- Important on android since the video player orientation is locked to
- the app orientation.
-
-
- updateSettings({ autoRotate: value })}
- />
-
- {/*
-
- Download quality
-
- Choose the download quality.
-
-
-
-
-
- {settings?.downloadQuality?.label}
-
-
-
+
+ Downloads
+
+
- Quality
- {DownloadOptions.map((option) => (
- {
- updateSettings({ downloadQuality: option });
- }}
+
+ Audio language
+
+ Choose a default audio language for downloads.
+
+
+
+
+
+ {settings?.defaultAudioLanguage?.label || "None"}
+
+
+
- {option.label}
-
- ))}
-
-
- */}
-
-
- Start videos in fullscreen
-
- Clicking a video will start it in fullscreen mode, instead of
- inline.
-
-
-
- updateSettings({ openFullScreenVideoPlayerByDefault: value })
- }
- />
-
-
-
-
- Use external player (VLC)
-
- Open all videos in VLC instead of the default player. This requries
- VLC to be installed on the phone.
-
-
- {
- updateSettings({ openInVLC: value, forceDirectPlay: value });
- }}
- />
-
-
-
-
-
- Use popular lists plugin
- Made by: lostb1t
- {
- Linking.openURL(
- "https://github.com/lostb1t/jellyfin-plugin-media-lists"
- );
- }}
- >
- More info
-
-
-
- updateSettings({ usePopularPlugin: value })
- }
- />
-
- {settings?.usePopularPlugin && (
-
- {mediaListCollections?.map((mlc) => (
-
-
- {mlc.Name}
-
- {
- if (!settings.mediaListCollectionIds) {
- updateSettings({
- mediaListCollectionIds: [mlc.Id!],
- });
- return;
- }
-
+ Languages
+ {
updateSettings({
- mediaListCollectionIds:
- settings?.mediaListCollectionIds.includes(mlc.Id!)
- ? settings?.mediaListCollectionIds.filter(
- (id) => id !== mlc.Id
- )
- : [...settings?.mediaListCollectionIds, mlc.Id!],
+ defaultAudioLanguage: null,
});
}}
- />
+ >
+ None
+
+ {LANGUAGES.map((l) => (
+ {
+ updateSettings({
+ defaultAudioLanguage: l,
+ });
+ }}
+ >
+ {l.label}
+
+ ))}
+
+
+
+
+
+ Subtitle language
+
+ Choose a default subtitle language for downloads.
+
+
+
+
+
+
+ {settings?.defaultSubtitleLanguage?.label || "None"}
+
+
+
+
+ Languages
+ {
+ updateSettings({
+ defaultSubtitleLanguage: null,
+ });
+ }}
+ >
+ None
+
+ {LANGUAGES.map((l) => (
+ {
+ updateSettings({
+ defaultSubtitleLanguage: l,
+ });
+ }}
+ >
+ {l.label}
+
+ ))}
+
+
+
+
+
+
+
+ Other
+
+
+
+
+ Auto rotate
+
+ Important on android since the video player orientation is
+ locked to the app orientation.
+
+
+ updateSettings({ autoRotate: value })}
+ />
+
+
+
+
+ Start videos in fullscreen
+
+ Clicking a video will start it in fullscreen mode, instead of
+ inline.
+
+
+
+ updateSettings({ openFullScreenVideoPlayerByDefault: value })
+ }
+ />
+
+
+
+
+ Use external player (VLC)
+
+ Open all videos in VLC instead of the default player. This
+ requries VLC to be installed on the phone.
+
+
+ {
+ updateSettings({ openInVLC: value, forceDirectPlay: value });
+ }}
+ />
+
+
+
+
+
+ Use popular lists plugin
+ Made by: lostb1t
+ {
+ Linking.openURL(
+ "https://github.com/lostb1t/jellyfin-plugin-media-lists"
+ );
+ }}
+ >
+ More info
+
- ))}
- {isLoadingMediaListCollections && (
-
-
-
- )}
- {mediaListCollections?.length === 0 && (
-
-
- No collections found. Add some in Jellyfin.
-
+
+ updateSettings({ usePopularPlugin: value })
+ }
+ />
+
+ {settings?.usePopularPlugin && (
+
+ {mediaListCollections?.map((mlc) => (
+
+
+ {mlc.Name}
+
+ {
+ if (!settings.mediaListCollectionIds) {
+ updateSettings({
+ mediaListCollectionIds: [mlc.Id!],
+ });
+ return;
+ }
+
+ updateSettings({
+ mediaListCollectionIds:
+ settings?.mediaListCollectionIds.includes(mlc.Id!)
+ ? settings?.mediaListCollectionIds.filter(
+ (id) => id !== mlc.Id
+ )
+ : [...settings?.mediaListCollectionIds, mlc.Id!],
+ });
+ }}
+ />
+
+ ))}
+ {isLoadingMediaListCollections && (
+
+
+
+ )}
+ {mediaListCollections?.length === 0 && (
+
+
+ No collections found. Add some in Jellyfin.
+
+
+ )}
)}
- )}
-
-
-
- Force direct play
-
- This will always request direct play. This is good if you want to
- try to stream movies you think the device supports.
-
-
- updateSettings({ forceDirectPlay: value })}
- />
-
+
+
+ Force direct play
+
+ This will always request direct play. This is good if you want
+ to try to stream movies you think the device supports.
+
+
+
+ updateSettings({ forceDirectPlay: value })
+ }
+ />
+
-
-
- Device profile
-
- A profile used for deciding what audio and video codecs the device
- supports.
-
-
-
-
-
- {settings?.deviceProfile}
-
-
-
- Profiles
- {
- updateSettings({ deviceProfile: "Expo" });
- }}
- >
- Expo
-
- {
- updateSettings({ deviceProfile: "Native" });
- }}
- >
- Native
-
- {
- updateSettings({ deviceProfile: "Old" });
- }}
- >
- Old
-
-
-
-
-
-
-
- Search engine
-
- Choose the search engine you want to use.
-
-
-
-
-
- {settings?.searchEngine}
-
-
-
- Profiles
- {
- updateSettings({ searchEngine: "Jellyfin" });
- queryClient.invalidateQueries({ queryKey: ["search"] });
- }}
+
+ Device profile
+
+ A profile used for deciding what audio and video codecs the
+ device supports.
+
+
+
+
+
+ {settings?.deviceProfile}
+
+
+
- Jellyfin
-
- {
- updateSettings({ searchEngine: "Marlin" });
- queryClient.invalidateQueries({ queryKey: ["search"] });
- }}
- >
- Marlin
-
-
-
-
- {settings?.searchEngine === "Marlin" && (
-
- <>
-
-
- setMarlinUrl(text)}
- />
-
-
-
-
-
- {settings?.marlinServerUrl}
-
- >
+ Expo
+
+ {
+ updateSettings({ deviceProfile: "Native" });
+ }}
+ >
+ Native
+
+ {
+ updateSettings({ deviceProfile: "Old" });
+ }}
+ >
+ Old
+
+
+
- )}
+
+
+
+ Search engine
+
+ Choose the search engine you want to use.
+
+
+
+
+
+ {settings?.searchEngine}
+
+
+
+ Profiles
+ {
+ updateSettings({ searchEngine: "Jellyfin" });
+ queryClient.invalidateQueries({ queryKey: ["search"] });
+ }}
+ >
+ Jellyfin
+
+ {
+ updateSettings({ searchEngine: "Marlin" });
+ queryClient.invalidateQueries({ queryKey: ["search"] });
+ }}
+ >
+ Marlin
+
+
+
+
+ {settings?.searchEngine === "Marlin" && (
+
+ <>
+
+
+ setMarlinUrl(text)}
+ />
+
+
+
+
+
+ {settings?.marlinServerUrl}
+
+ >
+
+ )}
+
+
);
diff --git a/utils/atoms/settings.ts b/utils/atoms/settings.ts
index 1342061f..cde69411 100644
--- a/utils/atoms/settings.ts
+++ b/utils/atoms/settings.ts
@@ -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 => {
showTitles: true,
showStats: true,
},
+ defaultAudioLanguage: null,
+ defaultSubtitleLanguage: null,
};
try {