mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-07-16 01:13:27 +01:00
Compare commits
10 Commits
fix/networ
...
fix/auth-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1860863c60 | ||
|
|
69651cdfa3 | ||
|
|
25ee7dad85 | ||
|
|
76d9b3177b | ||
|
|
34d9b43dcb | ||
|
|
3ff3847d80 | ||
|
|
8cf141e03b | ||
|
|
34f01e2c13 | ||
|
|
98dfefbdd0 | ||
|
|
49a903f48a |
@@ -11,6 +11,7 @@ import { Image } from "expo-image";
|
|||||||
import { DarkTheme, ThemeProvider } from "expo-router/react-navigation";
|
import { DarkTheme, ThemeProvider } from "expo-router/react-navigation";
|
||||||
import { Platform } from "react-native";
|
import { Platform } from "react-native";
|
||||||
import { GlobalModal } from "@/components/GlobalModal";
|
import { GlobalModal } from "@/components/GlobalModal";
|
||||||
|
import { PendingAccountSaveModal } from "@/components/PendingAccountSaveModal";
|
||||||
import { enableTVMenuKeyInterception } from "@/hooks/useTVBackHandler";
|
import { enableTVMenuKeyInterception } from "@/hooks/useTVBackHandler";
|
||||||
import i18n from "@/i18n";
|
import i18n from "@/i18n";
|
||||||
import { DownloadProvider } from "@/providers/DownloadProvider";
|
import { DownloadProvider } from "@/providers/DownloadProvider";
|
||||||
@@ -27,7 +28,6 @@ import { NetworkStatusProvider } from "@/providers/NetworkStatusProvider";
|
|||||||
import { PlaySettingsProvider } from "@/providers/PlaySettingsProvider";
|
import { PlaySettingsProvider } from "@/providers/PlaySettingsProvider";
|
||||||
import { ServerUrlProvider } from "@/providers/ServerUrlProvider";
|
import { ServerUrlProvider } from "@/providers/ServerUrlProvider";
|
||||||
import { WebSocketProvider } from "@/providers/WebSocketProvider";
|
import { WebSocketProvider } from "@/providers/WebSocketProvider";
|
||||||
import { WifiSsidProvider } from "@/providers/WifiSsidProvider";
|
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
import {
|
import {
|
||||||
BACKGROUND_FETCH_TASK,
|
BACKGROUND_FETCH_TASK,
|
||||||
@@ -426,7 +426,6 @@ function Layout() {
|
|||||||
>
|
>
|
||||||
<JellyfinProvider>
|
<JellyfinProvider>
|
||||||
<InactivityProvider>
|
<InactivityProvider>
|
||||||
<WifiSsidProvider>
|
|
||||||
<ServerUrlProvider>
|
<ServerUrlProvider>
|
||||||
<NetworkStatusProvider>
|
<NetworkStatusProvider>
|
||||||
<PlaySettingsProvider>
|
<PlaySettingsProvider>
|
||||||
@@ -469,8 +468,7 @@ function Layout() {
|
|||||||
options={{
|
options={{
|
||||||
headerShown: true,
|
headerShown: true,
|
||||||
title: "",
|
title: "",
|
||||||
headerTransparent:
|
headerTransparent: Platform.OS === "ios",
|
||||||
Platform.OS === "ios",
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen name='+not-found' />
|
<Stack.Screen name='+not-found' />
|
||||||
@@ -554,6 +552,7 @@ function Layout() {
|
|||||||
closeButton
|
closeButton
|
||||||
/>
|
/>
|
||||||
{!Platform.isTV && <GlobalModal />}
|
{!Platform.isTV && <GlobalModal />}
|
||||||
|
{!Platform.isTV && <PendingAccountSaveModal />}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</IntroSheetProvider>
|
</IntroSheetProvider>
|
||||||
</BottomSheetModalProvider>
|
</BottomSheetModalProvider>
|
||||||
@@ -565,7 +564,6 @@ function Layout() {
|
|||||||
</PlaySettingsProvider>
|
</PlaySettingsProvider>
|
||||||
</NetworkStatusProvider>
|
</NetworkStatusProvider>
|
||||||
</ServerUrlProvider>
|
</ServerUrlProvider>
|
||||||
</WifiSsidProvider>
|
|
||||||
</InactivityProvider>
|
</InactivityProvider>
|
||||||
</JellyfinProvider>
|
</JellyfinProvider>
|
||||||
</PersistQueryClientProvider>
|
</PersistQueryClientProvider>
|
||||||
|
|||||||
45
components/PendingAccountSaveModal.tsx
Normal file
45
components/PendingAccountSaveModal.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { useAtom, useAtomValue } from "jotai";
|
||||||
|
import type React from "react";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { Platform } from "react-native";
|
||||||
|
import { SaveAccountModal } from "@/components/SaveAccountModal";
|
||||||
|
import {
|
||||||
|
pendingAccountSaveAtom,
|
||||||
|
useJellyfin,
|
||||||
|
userAtom,
|
||||||
|
} from "@/providers/JellyfinProvider";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post-login save-account prompt. Login flows (password or Quick Connect)
|
||||||
|
* only flag the intent via pendingAccountSaveAtom; the protection picker
|
||||||
|
* shows here, AFTER the session is authorized — the login screen itself
|
||||||
|
* unmounts as soon as the user is set, so it can't host the modal.
|
||||||
|
*/
|
||||||
|
export const PendingAccountSaveModal: React.FC = () => {
|
||||||
|
const [pending, setPending] = useAtom(pendingAccountSaveAtom);
|
||||||
|
const user = useAtomValue(userAtom);
|
||||||
|
const { saveCurrentAccount } = useJellyfin();
|
||||||
|
|
||||||
|
// A logout before answering drops the intent — it must not resurface on
|
||||||
|
// the next (possibly different) login.
|
||||||
|
useEffect(() => {
|
||||||
|
if (!user && pending) setPending(null);
|
||||||
|
}, [user, pending, setPending]);
|
||||||
|
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SaveAccountModal
|
||||||
|
visible={!!pending && !!user}
|
||||||
|
username={user?.Name ?? ""}
|
||||||
|
onClose={() => setPending(null)}
|
||||||
|
onSave={(securityType, pinCode) => {
|
||||||
|
const serverName = pending?.serverName;
|
||||||
|
setPending(null);
|
||||||
|
saveCurrentAccount({ securityType, pinCode, serverName }).catch(
|
||||||
|
(error) => console.warn("Failed to save account:", error),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -39,7 +39,11 @@ import { useRefreshLibraryOnFocus } from "@/hooks/useRefreshLibraryOnFocus";
|
|||||||
import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache";
|
import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache";
|
||||||
import { useDownload } from "@/providers/DownloadProvider";
|
import { useDownload } from "@/providers/DownloadProvider";
|
||||||
import { useIntroSheet } from "@/providers/IntroSheetProvider";
|
import { useIntroSheet } from "@/providers/IntroSheetProvider";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import {
|
||||||
|
apiAtom,
|
||||||
|
pendingAccountSaveAtom,
|
||||||
|
userAtom,
|
||||||
|
} from "@/providers/JellyfinProvider";
|
||||||
import { SortByOption, SortOrderOption } from "@/utils/atoms/filters";
|
import { SortByOption, SortOrderOption } from "@/utils/atoms/filters";
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
import { eventBus } from "@/utils/eventBus";
|
import { eventBus } from "@/utils/eventBus";
|
||||||
@@ -89,6 +93,9 @@ const HomeMobile = () => {
|
|||||||
const invalidateCache = useInvalidatePlaybackProgressCache();
|
const invalidateCache = useInvalidatePlaybackProgressCache();
|
||||||
const [loadedSections, setLoadedSections] = useState<Set<string>>(new Set());
|
const [loadedSections, setLoadedSections] = useState<Set<string>>(new Set());
|
||||||
const { showIntro } = useIntroSheet();
|
const { showIntro } = useIntroSheet();
|
||||||
|
// Gate the intro so it can't steal presentation from the post-login
|
||||||
|
// save-account sheet (both are BottomSheetModals): wait until no save is pending.
|
||||||
|
const pendingAccountSave = useAtomValue(pendingAccountSaveAtom);
|
||||||
|
|
||||||
// Fallback refresh for newly added content when returning to the home screen
|
// Fallback refresh for newly added content when returning to the home screen
|
||||||
// (primary path is the LibraryChanged WebSocket event).
|
// (primary path is the LibraryChanged WebSocket event).
|
||||||
@@ -97,7 +104,9 @@ const HomeMobile = () => {
|
|||||||
// Show intro modal on first launch
|
// Show intro modal on first launch
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const hasShownIntro = storage.getBoolean("hasShownIntro");
|
const hasShownIntro = storage.getBoolean("hasShownIntro");
|
||||||
if (!hasShownIntro) {
|
// Defer while the save-account sheet is up; this effect re-runs and schedules
|
||||||
|
// the intro once the sheet is dismissed (pendingAccountSaveAtom cleared).
|
||||||
|
if (!hasShownIntro && !pendingAccountSave) {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
showIntro();
|
showIntro();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@@ -106,7 +115,7 @@ const HomeMobile = () => {
|
|||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [showIntro]);
|
}, [showIntro, pendingAccountSave]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isConnected && !prevIsConnected.current) {
|
if (isConnected && !prevIsConnected.current) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { PublicSystemInfo } from "@jellyfin/sdk/lib/generated-client";
|
|||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { useLocalSearchParams, useNavigation } from "expo-router";
|
import { useLocalSearchParams, useNavigation } from "expo-router";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue, useSetAtom } from "jotai";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
@@ -21,13 +21,14 @@ import { Input } from "@/components/common/Input";
|
|||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import JellyfinServerDiscovery from "@/components/JellyfinServerDiscovery";
|
import JellyfinServerDiscovery from "@/components/JellyfinServerDiscovery";
|
||||||
import { PreviousServersList } from "@/components/PreviousServersList";
|
import { PreviousServersList } from "@/components/PreviousServersList";
|
||||||
import { SaveAccountModal } from "@/components/SaveAccountModal";
|
|
||||||
import { Colors } from "@/constants/Colors";
|
import { Colors } from "@/constants/Colors";
|
||||||
import { apiAtom, useJellyfin } from "@/providers/JellyfinProvider";
|
import {
|
||||||
import type {
|
apiAtom,
|
||||||
AccountSecurityType,
|
pendingAccountSaveAtom,
|
||||||
SavedServer,
|
useJellyfin,
|
||||||
} from "@/utils/secureCredentials";
|
userAtom,
|
||||||
|
} from "@/providers/JellyfinProvider";
|
||||||
|
import type { SavedServer } from "@/utils/secureCredentials";
|
||||||
|
|
||||||
const CredentialsSchema = z.object({
|
const CredentialsSchema = z.object({
|
||||||
username: z.string().min(1, t("login.username_required")),
|
username: z.string().min(1, t("login.username_required")),
|
||||||
@@ -35,6 +36,7 @@ const CredentialsSchema = z.object({
|
|||||||
|
|
||||||
export const Login: React.FC = () => {
|
export const Login: React.FC = () => {
|
||||||
const api = useAtomValue(apiAtom);
|
const api = useAtomValue(apiAtom);
|
||||||
|
const user = useAtomValue(userAtom);
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const params = useLocalSearchParams();
|
const params = useLocalSearchParams();
|
||||||
const {
|
const {
|
||||||
@@ -45,6 +47,7 @@ export const Login: React.FC = () => {
|
|||||||
loginWithSavedCredential,
|
loginWithSavedCredential,
|
||||||
loginWithPassword,
|
loginWithPassword,
|
||||||
} = useJellyfin();
|
} = useJellyfin();
|
||||||
|
const setPendingAccountSave = useSetAtom(pendingAccountSaveAtom);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
apiUrl: _apiUrl,
|
apiUrl: _apiUrl,
|
||||||
@@ -64,13 +67,24 @@ export const Login: React.FC = () => {
|
|||||||
password: _password || "",
|
password: _password || "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Save account state
|
// Save account state — only the intent lives here; the protection picker is
|
||||||
|
// the global PendingAccountSaveModal, shown after the login succeeds.
|
||||||
const [saveAccount, setSaveAccount] = useState(false);
|
const [saveAccount, setSaveAccount] = useState(false);
|
||||||
const [showSaveModal, setShowSaveModal] = useState(false);
|
|
||||||
const [pendingLogin, setPendingLogin] = useState<{
|
// Tracks an in-flight Quick Connect attempt (code issued, provider polling).
|
||||||
username: string;
|
const [quickConnectActive, setQuickConnectActive] = useState(false);
|
||||||
password: string;
|
|
||||||
} | null>(null);
|
// A Quick Connect login with "save account" on flags the post-login save:
|
||||||
|
// the protection picker shows globally once the session exists (this screen
|
||||||
|
// unmounts on login, so it can't host the modal).
|
||||||
|
useEffect(() => {
|
||||||
|
if (user) {
|
||||||
|
if (quickConnectActive && saveAccount) {
|
||||||
|
setPendingAccountSave({ serverName });
|
||||||
|
}
|
||||||
|
setQuickConnectActive(false);
|
||||||
|
}
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
// Handle URL params for server connection
|
// Handle URL params for server connection
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -117,29 +131,22 @@ export const Login: React.FC = () => {
|
|||||||
const result = CredentialsSchema.safeParse(credentials);
|
const result = CredentialsSchema.safeParse(credentials);
|
||||||
if (!result.success) return;
|
if (!result.success) return;
|
||||||
|
|
||||||
if (saveAccount) {
|
const ok = await performLogin(credentials.username, credentials.password);
|
||||||
setPendingLogin({
|
// The protection picker shows AFTER a successful login (global modal) —
|
||||||
username: credentials.username,
|
// never for a failed one.
|
||||||
password: credentials.password,
|
if (ok && saveAccount) {
|
||||||
});
|
setPendingAccountSave({ serverName });
|
||||||
setShowSaveModal(true);
|
|
||||||
} else {
|
|
||||||
await performLogin(credentials.username, credentials.password);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const performLogin = async (
|
const performLogin = async (
|
||||||
username: string,
|
username: string,
|
||||||
password: string,
|
password: string,
|
||||||
options?: {
|
): Promise<boolean> => {
|
||||||
saveAccount?: boolean;
|
|
||||||
securityType?: AccountSecurityType;
|
|
||||||
pinCode?: string;
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
await login(username, password, serverName, options);
|
await login(username, password, serverName);
|
||||||
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
Alert.alert(t("login.connection_failed"), error.message);
|
Alert.alert(t("login.connection_failed"), error.message);
|
||||||
@@ -149,23 +156,9 @@ export const Login: React.FC = () => {
|
|||||||
t("login.an_unexpected_error_occurred"),
|
t("login.an_unexpected_error_occurred"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setPendingLogin(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSaveAccountConfirm = async (
|
|
||||||
securityType: AccountSecurityType,
|
|
||||||
pinCode?: string,
|
|
||||||
) => {
|
|
||||||
setShowSaveModal(false);
|
|
||||||
if (pendingLogin) {
|
|
||||||
await performLogin(pendingLogin.username, pendingLogin.password, {
|
|
||||||
saveAccount: true,
|
|
||||||
securityType,
|
|
||||||
pinCode,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -259,6 +252,7 @@ export const Login: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
const code = await initiateQuickConnect();
|
const code = await initiateQuickConnect();
|
||||||
if (code) {
|
if (code) {
|
||||||
|
setQuickConnectActive(true);
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
t("login.quick_connect"),
|
t("login.quick_connect"),
|
||||||
t("login.enter_code_to_login", { code: code }),
|
t("login.enter_code_to_login", { code: code }),
|
||||||
@@ -443,16 +437,6 @@ export const Login: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
|
|
||||||
<SaveAccountModal
|
|
||||||
visible={showSaveModal}
|
|
||||||
onClose={() => {
|
|
||||||
setShowSaveModal(false);
|
|
||||||
setPendingLogin(null);
|
|
||||||
}}
|
|
||||||
onSave={handleSaveAccountConfirm}
|
|
||||||
username={pendingLogin?.username || credentials.username}
|
|
||||||
/>
|
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { Switch, TouchableOpacity, View } from "react-native";
|
import { Switch, TouchableOpacity, View } from "react-native";
|
||||||
import { toast } from "sonner-native";
|
import { toast } from "sonner-native";
|
||||||
import { useWifiSSID } from "@/hooks/useWifiSSID";
|
import { useWifiSSID } from "@/hooks/useWifiSSID";
|
||||||
import { openLocationSettings } from "@/modules/wifi-ssid";
|
|
||||||
import { useServerUrl } from "@/providers/ServerUrlProvider";
|
import { useServerUrl } from "@/providers/ServerUrlProvider";
|
||||||
import { storage } from "@/utils/mmkv";
|
import { storage } from "@/utils/mmkv";
|
||||||
import {
|
import {
|
||||||
@@ -27,26 +26,16 @@ const DEFAULT_CONFIG: LocalNetworkConfig = {
|
|||||||
|
|
||||||
interface StatusDisplayProps {
|
interface StatusDisplayProps {
|
||||||
currentSSID: string | null;
|
currentSSID: string | null;
|
||||||
connectedToWifi: boolean;
|
|
||||||
isUsingLocalUrl: boolean;
|
isUsingLocalUrl: boolean;
|
||||||
locationBlocked: boolean;
|
|
||||||
onOpenLocationSettings: () => void;
|
|
||||||
t: (key: string) => string;
|
t: (key: string) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function StatusDisplay({
|
function StatusDisplay({
|
||||||
currentSSID,
|
currentSSID,
|
||||||
connectedToWifi,
|
|
||||||
isUsingLocalUrl,
|
isUsingLocalUrl,
|
||||||
locationBlocked,
|
|
||||||
onOpenLocationSettings,
|
|
||||||
t,
|
t,
|
||||||
}: StatusDisplayProps): React.ReactElement {
|
}: StatusDisplayProps): React.ReactElement {
|
||||||
const wifiStatus = currentSSID
|
const wifiStatus = currentSSID ?? t("home.settings.network.not_connected");
|
||||||
? currentSSID
|
|
||||||
: connectedToWifi
|
|
||||||
? t("home.settings.network.ssid_hidden")
|
|
||||||
: t("home.settings.network.not_connected");
|
|
||||||
const urlType = isUsingLocalUrl
|
const urlType = isUsingLocalUrl
|
||||||
? t("home.settings.network.local")
|
? t("home.settings.network.local")
|
||||||
: t("home.settings.network.remote");
|
: t("home.settings.network.remote");
|
||||||
@@ -66,23 +55,6 @@ function StatusDisplay({
|
|||||||
</Text>
|
</Text>
|
||||||
<Text className={urlTypeColor}>{urlType}</Text>
|
<Text className={urlTypeColor}>{urlType}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{locationBlocked && (
|
|
||||||
<View className='mt-2 pt-2 border-t border-neutral-800'>
|
|
||||||
<Text className='text-xs text-amber-400'>
|
|
||||||
{t("home.settings.network.location_off_description")}
|
|
||||||
</Text>
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={onOpenLocationSettings}
|
|
||||||
className='mt-2 self-start'
|
|
||||||
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
||||||
>
|
|
||||||
<Text className='text-xs text-blue-400 font-semibold'>
|
|
||||||
{t("home.settings.network.open_location_settings")}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -90,17 +62,7 @@ function StatusDisplay({
|
|||||||
export function LocalNetworkSettings(): React.ReactElement | null {
|
export function LocalNetworkSettings(): React.ReactElement | null {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { permissionStatus, requestPermission } = useWifiSSID();
|
const { permissionStatus, requestPermission } = useWifiSSID();
|
||||||
const { isUsingLocalUrl, currentSSID, connectedToWifi, refreshUrlState } =
|
const { isUsingLocalUrl, currentSSID, refreshUrlState } = useServerUrl();
|
||||||
useServerUrl();
|
|
||||||
|
|
||||||
// Connected to Wi-Fi and have permission, but the OS won't reveal the SSID —
|
|
||||||
// on Android this means device Location services are off.
|
|
||||||
const locationBlocked =
|
|
||||||
permissionStatus === "granted" && connectedToWifi && !currentSSID;
|
|
||||||
|
|
||||||
const handleOpenLocationSettings = useCallback(() => {
|
|
||||||
openLocationSettings();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const remoteUrl = storage.getString("serverUrl");
|
const remoteUrl = storage.getString("serverUrl");
|
||||||
const [config, setConfig] = useState<LocalNetworkConfig>(DEFAULT_CONFIG);
|
const [config, setConfig] = useState<LocalNetworkConfig>(DEFAULT_CONFIG);
|
||||||
@@ -233,7 +195,6 @@ export function LocalNetworkSettings(): React.ReactElement | null {
|
|||||||
)}
|
)}
|
||||||
</ListGroup>
|
</ListGroup>
|
||||||
|
|
||||||
{!locationBlocked && (
|
|
||||||
<View className='py-2'>
|
<View className='py-2'>
|
||||||
<Button
|
<Button
|
||||||
onPress={handleAddCurrentNetwork}
|
onPress={handleAddCurrentNetwork}
|
||||||
@@ -242,14 +203,10 @@ export function LocalNetworkSettings(): React.ReactElement | null {
|
|||||||
{addNetworkButtonText}
|
{addNetworkButtonText}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
)}
|
|
||||||
|
|
||||||
<StatusDisplay
|
<StatusDisplay
|
||||||
currentSSID={currentSSID}
|
currentSSID={currentSSID}
|
||||||
connectedToWifi={connectedToWifi}
|
|
||||||
isUsingLocalUrl={isUsingLocalUrl}
|
isUsingLocalUrl={isUsingLocalUrl}
|
||||||
locationBlocked={locationBlocked}
|
|
||||||
onOpenLocationSettings={handleOpenLocationSettings}
|
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,5 +1,125 @@
|
|||||||
export {
|
import { useCallback, useEffect, useState } from "react";
|
||||||
type PermissionStatus,
|
import { Platform } from "react-native";
|
||||||
type UseWifiSSIDReturn,
|
import { getSSID } from "@/modules/wifi-ssid";
|
||||||
useWifiSsid as useWifiSSID,
|
|
||||||
} from "@/providers/WifiSsidProvider";
|
export type PermissionStatus =
|
||||||
|
| "granted"
|
||||||
|
| "denied"
|
||||||
|
| "undetermined"
|
||||||
|
| "unavailable";
|
||||||
|
|
||||||
|
export interface UseWifiSSIDReturn {
|
||||||
|
ssid: string | null;
|
||||||
|
permissionStatus: PermissionStatus;
|
||||||
|
requestPermission: () => Promise<boolean>;
|
||||||
|
isLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// WiFi SSID is not available on tvOS
|
||||||
|
if (Platform.isTV) {
|
||||||
|
// Export a stub hook for tvOS
|
||||||
|
module.exports = {
|
||||||
|
useWifiSSID: (): UseWifiSSIDReturn => ({
|
||||||
|
ssid: null,
|
||||||
|
permissionStatus: "unavailable" as PermissionStatus,
|
||||||
|
requestPermission: async () => false,
|
||||||
|
isLoading: false,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only import Location on non-TV platforms
|
||||||
|
const Location = Platform.isTV ? null : require("expo-location");
|
||||||
|
|
||||||
|
function mapLocationStatus(status: number | undefined): PermissionStatus {
|
||||||
|
if (!Location) return "unavailable";
|
||||||
|
switch (status) {
|
||||||
|
case Location.PermissionStatus?.GRANTED:
|
||||||
|
return "granted";
|
||||||
|
case Location.PermissionStatus?.DENIED:
|
||||||
|
return "denied";
|
||||||
|
default:
|
||||||
|
return "undetermined";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useWifiSSID(): UseWifiSSIDReturn {
|
||||||
|
const [ssid, setSSID] = useState<string | null>(null);
|
||||||
|
const [permissionStatus, setPermissionStatus] = useState<PermissionStatus>(
|
||||||
|
Platform.isTV ? "unavailable" : "undetermined",
|
||||||
|
);
|
||||||
|
const [isLoading, setIsLoading] = useState(!Platform.isTV);
|
||||||
|
|
||||||
|
const fetchSSID = useCallback(async () => {
|
||||||
|
if (Platform.isTV) return;
|
||||||
|
const result = await getSSID();
|
||||||
|
setSSID(result);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const requestPermission = useCallback(async (): Promise<boolean> => {
|
||||||
|
if (Platform.isTV || !Location) {
|
||||||
|
setPermissionStatus("unavailable");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status } = await Location.requestForegroundPermissionsAsync();
|
||||||
|
const newStatus = mapLocationStatus(status);
|
||||||
|
setPermissionStatus(newStatus);
|
||||||
|
|
||||||
|
if (newStatus === "granted") {
|
||||||
|
await fetchSSID();
|
||||||
|
}
|
||||||
|
|
||||||
|
return newStatus === "granted";
|
||||||
|
} catch {
|
||||||
|
setPermissionStatus("unavailable");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, [fetchSSID]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Platform.isTV || !Location) {
|
||||||
|
setIsLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initialize() {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const { status } = await Location.getForegroundPermissionsAsync();
|
||||||
|
const mappedStatus = mapLocationStatus(status);
|
||||||
|
setPermissionStatus(mappedStatus);
|
||||||
|
|
||||||
|
if (mappedStatus === "granted") {
|
||||||
|
await fetchSSID();
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setPermissionStatus("unavailable");
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
}, [fetchSSID]);
|
||||||
|
|
||||||
|
// Refresh SSID when permission status changes to granted
|
||||||
|
useEffect(() => {
|
||||||
|
if (Platform.isTV) return;
|
||||||
|
|
||||||
|
if (permissionStatus === "granted") {
|
||||||
|
fetchSSID();
|
||||||
|
|
||||||
|
// Also set up an interval to periodically check SSID
|
||||||
|
const interval = setInterval(fetchSSID, 10000); // Check every 10 seconds
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}
|
||||||
|
}, [permissionStatus, fetchSSID]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
ssid,
|
||||||
|
permissionStatus,
|
||||||
|
requestPermission,
|
||||||
|
isLoading,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
apply plugin: 'expo-module-gradle-plugin'
|
|
||||||
|
|
||||||
group = 'expo.modules.wifissid'
|
|
||||||
version = '1.0.0'
|
|
||||||
|
|
||||||
expoModule {
|
|
||||||
canBePublished false
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace "expo.modules.wifissid"
|
|
||||||
defaultConfig {
|
|
||||||
versionCode 1
|
|
||||||
versionName "1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<!-- Reading the connected Wi-Fi SSID requires ACCESS_WIFI_STATE, and (since
|
|
||||||
Android 8) ACCESS_FINE_LOCATION granted at runtime. Location is requested
|
|
||||||
at runtime by the app via expo-location; the manifest declares the
|
|
||||||
Wi-Fi/network permissions this module needs directly. -->
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
||||||
</manifest>
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
package expo.modules.wifissid
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.net.ConnectivityManager
|
|
||||||
import android.net.NetworkCapabilities
|
|
||||||
import android.net.wifi.WifiInfo
|
|
||||||
import android.net.wifi.WifiManager
|
|
||||||
import android.os.Build
|
|
||||||
import android.provider.Settings
|
|
||||||
import android.util.Log
|
|
||||||
import expo.modules.kotlin.modules.Module
|
|
||||||
import expo.modules.kotlin.modules.ModuleDefinition
|
|
||||||
|
|
||||||
class WifiSsidModule : Module() {
|
|
||||||
companion object {
|
|
||||||
private const val TAG = "WifiSsid"
|
|
||||||
private const val UNKNOWN_SSID = "<unknown ssid>"
|
|
||||||
}
|
|
||||||
|
|
||||||
private val context
|
|
||||||
get() = requireNotNull(appContext.reactContext)
|
|
||||||
|
|
||||||
override fun definition() = ModuleDefinition {
|
|
||||||
Name("WifiSsid")
|
|
||||||
|
|
||||||
AsyncFunction("getSSID") {
|
|
||||||
val (ssid, connectedToWifi) = readWifi()
|
|
||||||
mapOf(
|
|
||||||
"ssid" to ssid,
|
|
||||||
"connectedToWifi" to connectedToWifi,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Function("getSSIDSync") {
|
|
||||||
val (ssid, connectedToWifi) = readWifi()
|
|
||||||
mapOf(
|
|
||||||
"ssid" to ssid,
|
|
||||||
"connectedToWifi" to connectedToWifi,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Function("openLocationSettings") {
|
|
||||||
openLocationSettings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current Wi-Fi SSID (or null when it can't be read) and whether
|
|
||||||
* the device is connected to a Wi-Fi network. When the device is connected but
|
|
||||||
* the SSID is null, the OS is withholding the network name — on Android this
|
|
||||||
* typically means device Location services are off (the SSID is treated as
|
|
||||||
* location-sensitive).
|
|
||||||
*/
|
|
||||||
private fun readWifi(): Pair<String?, Boolean> {
|
|
||||||
val androidContext = context.applicationContext
|
|
||||||
var connectedToWifi = false
|
|
||||||
var ssid: String? = null
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
try {
|
|
||||||
val cm = androidContext.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
|
|
||||||
val caps = cm?.let { manager ->
|
|
||||||
manager.activeNetwork?.let { net -> manager.getNetworkCapabilities(net) }
|
|
||||||
}
|
|
||||||
connectedToWifi = caps?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true
|
|
||||||
if (hasFineLocationPermission()) {
|
|
||||||
ssid = (caps?.transportInfo as? WifiInfo)?.ssid?.sanitize()
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Error reading Wi-Fi via ConnectivityManager", e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Pre-Q (API 26–28): NetworkCapabilities/transportInfo aren't available, so
|
|
||||||
// detect a Wi-Fi connection via the legacy active-network API. Without this,
|
|
||||||
// connectedToWifi stays false on Android 8/9 even when connected, so the
|
|
||||||
// "connected but SSID hidden" state never surfaces there.
|
|
||||||
try {
|
|
||||||
val cm = androidContext.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
connectedToWifi = cm?.activeNetworkInfo?.type == ConnectivityManager.TYPE_WIFI
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Error reading Wi-Fi via ConnectivityManager (legacy)", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ssid == null && hasFineLocationPermission()) {
|
|
||||||
try {
|
|
||||||
val wm = androidContext.getSystemService(Context.WIFI_SERVICE) as? WifiManager
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
ssid = wm?.connectionInfo?.ssid?.sanitize()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Error reading SSID via WifiManager", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Pair(ssid, connectedToWifi)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun openLocationSettings() {
|
|
||||||
try {
|
|
||||||
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS).apply {
|
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
}
|
|
||||||
context.applicationContext.startActivity(intent)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Unable to open Location settings", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hasFineLocationPermission(): Boolean =
|
|
||||||
context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) ==
|
|
||||||
PackageManager.PERMISSION_GRANTED
|
|
||||||
|
|
||||||
private fun String.sanitize(): String? {
|
|
||||||
val cleaned = trim('"')
|
|
||||||
return if (cleaned.isBlank() || cleaned == UNKNOWN_SSID) null else cleaned
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "wifi-ssid",
|
"name": "wifi-ssid",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"platforms": ["ios", "android"],
|
"platforms": ["ios"],
|
||||||
"ios": {
|
"ios": {
|
||||||
"modules": ["WifiSsidModule"]
|
"modules": ["WifiSsidModule"]
|
||||||
},
|
|
||||||
"android": {
|
|
||||||
"modules": ["expo.modules.wifissid.WifiSsidModule"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,92 +1,44 @@
|
|||||||
import { Platform, requireNativeModule } from "expo-modules-core";
|
import { Platform, requireNativeModule } from "expo-modules-core";
|
||||||
import { Linking } from "react-native";
|
|
||||||
|
|
||||||
export interface WifiSsidResult {
|
// Only load the native module on iOS
|
||||||
ssid: string | null;
|
const WifiSsidModule =
|
||||||
/**
|
Platform.OS === "ios" ? requireNativeModule("WifiSsid") : null;
|
||||||
* Whether the device is connected to a Wi-Fi network. When true but `ssid` is
|
|
||||||
* null, the OS is withholding the network name — on Android this happens when
|
|
||||||
* device Location services are off (the SSID is location-sensitive).
|
|
||||||
*/
|
|
||||||
connectedToWifi: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the native module on iOS and Android (not TV/web). Wrapped so a
|
|
||||||
// missing/unlinked module degrades to null instead of crashing the bundle.
|
|
||||||
const WifiSsidModule = (() => {
|
|
||||||
if (Platform.OS !== "ios" && Platform.OS !== "android") return null;
|
|
||||||
try {
|
|
||||||
return requireNativeModule("WifiSsid");
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current WiFi SSID and whether the device is on a Wi-Fi network.
|
* Get the current WiFi SSID on iOS.
|
||||||
|
* Returns null on Android or if not connected to WiFi.
|
||||||
*
|
*
|
||||||
* iOS requirements:
|
* Requires:
|
||||||
* - Location permission granted
|
* - Location permission granted
|
||||||
* - com.apple.developer.networking.wifi-info entitlement
|
* - com.apple.developer.networking.wifi-info entitlement
|
||||||
*
|
* - Access WiFi Information capability enabled in Apple Developer Portal
|
||||||
* Android requirements:
|
|
||||||
* - ACCESS_FINE_LOCATION granted at runtime, and device Location services on
|
|
||||||
*/
|
*/
|
||||||
export async function getSSID(): Promise<WifiSsidResult> {
|
export async function getSSID(): Promise<string | null> {
|
||||||
if (!WifiSsidModule) {
|
if (!WifiSsidModule) {
|
||||||
return { ssid: null, connectedToWifi: false };
|
return null;
|
||||||
}
|
|
||||||
try {
|
|
||||||
return normalize(await WifiSsidModule.getSSID());
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[WifiSsid] Error getting SSID:", error);
|
|
||||||
return { ssid: null, connectedToWifi: false };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Synchronous version (uses the older CNCopyCurrentNetworkInfo API on iOS). */
|
|
||||||
export function getSSIDSync(): WifiSsidResult {
|
|
||||||
if (!WifiSsidModule) {
|
|
||||||
return { ssid: null, connectedToWifi: false };
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
return normalize(WifiSsidModule.getSSIDSync());
|
const ssid = await WifiSsidModule.getSSID();
|
||||||
|
return ssid ?? null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[WifiSsid] Error getting SSID (sync):", error);
|
console.error("[WifiSsid] Error getting SSID:", error);
|
||||||
return { ssid: null, connectedToWifi: false };
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the system Location settings so the user can enable Location services
|
* Synchronous version - uses older CNCopyCurrentNetworkInfo API
|
||||||
* (required on Android for the OS to reveal the Wi-Fi SSID). Falls back to the
|
|
||||||
* app's settings page where there is no dedicated native action (e.g. iOS).
|
|
||||||
*/
|
*/
|
||||||
export function openLocationSettings(): void {
|
export function getSSIDSync(): string | null {
|
||||||
try {
|
if (!WifiSsidModule) {
|
||||||
if (WifiSsidModule?.openLocationSettings) {
|
return null;
|
||||||
WifiSsidModule.openLocationSettings();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[WifiSsid] Error opening location settings:", error);
|
|
||||||
}
|
|
||||||
Linking.openSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both native modules return { ssid, connectedToWifi }. The string branch is a
|
try {
|
||||||
// defensive fallback in case a build returns the older plain-string shape.
|
return WifiSsidModule.getSSIDSync() ?? null;
|
||||||
function normalize(result: unknown): WifiSsidResult {
|
} catch (error) {
|
||||||
if (typeof result === "string") {
|
console.error("[WifiSsid] Error getting SSID (sync):", error);
|
||||||
const ssid = result || null;
|
return null;
|
||||||
return { ssid, connectedToWifi: ssid !== null };
|
|
||||||
}
|
}
|
||||||
if (result && typeof result === "object") {
|
|
||||||
const obj = result as { ssid?: string | null; connectedToWifi?: boolean };
|
|
||||||
return {
|
|
||||||
ssid: obj.ssid ?? null,
|
|
||||||
connectedToWifi: Boolean(obj.connectedToWifi),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return { ssid: null, connectedToWifi: false };
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,113 +1,50 @@
|
|||||||
import ExpoModulesCore
|
import ExpoModulesCore
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
import Network
|
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import SystemConfiguration.CaptiveNetwork
|
import SystemConfiguration.CaptiveNetwork
|
||||||
#endif
|
#endif
|
||||||
import UIKit
|
|
||||||
|
|
||||||
public class WifiSsidModule: Module {
|
public class WifiSsidModule: Module {
|
||||||
#if !os(tvOS)
|
|
||||||
// NWPathMonitor reports whether the active path uses Wi-Fi WITHOUT requiring
|
|
||||||
// the wifi-info entitlement, so we can tell "on Wi-Fi but can't read the SSID"
|
|
||||||
// apart from "not connected to Wi-Fi" even when fetchCurrent returns nil.
|
|
||||||
private let pathMonitor = NWPathMonitor()
|
|
||||||
private let monitorQueue = DispatchQueue(label: "expo.modules.wifissid.pathmonitor")
|
|
||||||
private let lock = NSLock()
|
|
||||||
private var cachedOnWifi = false
|
|
||||||
private var pathMonitorStarted = false
|
|
||||||
|
|
||||||
private var isOnWifi: Bool {
|
|
||||||
lock.lock()
|
|
||||||
defer { lock.unlock() }
|
|
||||||
return cachedOnWifi
|
|
||||||
}
|
|
||||||
|
|
||||||
private func startPathMonitorIfNeeded() {
|
|
||||||
lock.lock()
|
|
||||||
if pathMonitorStarted {
|
|
||||||
lock.unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pathMonitorStarted = true
|
|
||||||
lock.unlock()
|
|
||||||
|
|
||||||
pathMonitor.pathUpdateHandler = { [weak self] path in
|
|
||||||
guard let self else { return }
|
|
||||||
let onWifi = path.status == .satisfied && path.usesInterfaceType(.wifi)
|
|
||||||
self.lock.lock()
|
|
||||||
self.cachedOnWifi = onWifi
|
|
||||||
self.lock.unlock()
|
|
||||||
}
|
|
||||||
pathMonitor.start(queue: monitorQueue)
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
pathMonitor.cancel()
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public func definition() -> ModuleDefinition {
|
public func definition() -> ModuleDefinition {
|
||||||
Name("WifiSsid")
|
Name("WifiSsid")
|
||||||
|
|
||||||
AsyncFunction("getSSID") { () -> [String: Any] in
|
// Get current WiFi SSID using NEHotspotNetwork (iOS 14+)
|
||||||
|
// Not available on tvOS
|
||||||
|
AsyncFunction("getSSID") { () -> String? in
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
return ["ssid": NSNull(), "connectedToWifi": false]
|
return nil
|
||||||
#else
|
#else
|
||||||
self.startPathMonitorIfNeeded()
|
return await withCheckedContinuation { continuation in
|
||||||
let ssid = await self.resolveSSID()
|
|
||||||
var result: [String: Any] = ["connectedToWifi": self.isOnWifi]
|
|
||||||
if let ssid = ssid {
|
|
||||||
result["ssid"] = ssid
|
|
||||||
} else {
|
|
||||||
result["ssid"] = NSNull()
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
Function("getSSIDSync") { () -> [String: Any] in
|
|
||||||
#if os(tvOS)
|
|
||||||
return ["ssid": NSNull(), "connectedToWifi": false]
|
|
||||||
#else
|
|
||||||
self.startPathMonitorIfNeeded()
|
|
||||||
let ssid = self.getSSIDViaCNCopy()
|
|
||||||
var result: [String: Any] = ["connectedToWifi": self.isOnWifi]
|
|
||||||
if let ssid = ssid {
|
|
||||||
result["ssid"] = ssid
|
|
||||||
} else {
|
|
||||||
result["ssid"] = NSNull()
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
Function("openLocationSettings") {
|
|
||||||
#if !os(tvOS)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
if let url = URL(string: UIApplication.openSettingsURLString) {
|
|
||||||
UIApplication.shared.open(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !os(tvOS)
|
|
||||||
private func resolveSSID() async -> String? {
|
|
||||||
await withCheckedContinuation { continuation in
|
|
||||||
NEHotspotNetwork.fetchCurrent { network in
|
NEHotspotNetwork.fetchCurrent { network in
|
||||||
if let ssid = network?.ssid {
|
if let ssid = network?.ssid {
|
||||||
|
print("[WifiSsid] Got SSID via NEHotspotNetwork: \(ssid)")
|
||||||
continuation.resume(returning: ssid)
|
continuation.resume(returning: ssid)
|
||||||
} else {
|
} else {
|
||||||
continuation.resume(returning: self.getSSIDViaCNCopy())
|
// Fallback to CNCopyCurrentNetworkInfo for older iOS
|
||||||
|
print("[WifiSsid] NEHotspotNetwork returned nil, trying CNCopyCurrentNetworkInfo")
|
||||||
|
let ssid = self.getSSIDViaCNCopy()
|
||||||
|
continuation.resume(returning: ssid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronous version using only CNCopyCurrentNetworkInfo
|
||||||
|
// Not available on tvOS
|
||||||
|
Function("getSSIDSync") { () -> String? in
|
||||||
|
#if os(tvOS)
|
||||||
|
return nil
|
||||||
|
#else
|
||||||
|
return self.getSSIDViaCNCopy()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
private func getSSIDViaCNCopy() -> String? {
|
private func getSSIDViaCNCopy() -> String? {
|
||||||
guard let interfaces = CNCopySupportedInterfaces() as? [String] else {
|
guard let interfaces = CNCopySupportedInterfaces() as? [String] else {
|
||||||
|
print("[WifiSsid] CNCopySupportedInterfaces returned nil")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,10 +54,12 @@ public class WifiSsidModule: Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let ssid = networkInfo[kCNNetworkInfoKeySSID as String] as? String {
|
if let ssid = networkInfo[kCNNetworkInfoKeySSID as String] as? String {
|
||||||
|
print("[WifiSsid] Got SSID via CNCopyCurrentNetworkInfo: \(ssid)")
|
||||||
return ssid
|
return ssid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print("[WifiSsid] No SSID found via CNCopyCurrentNetworkInfo")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -91,6 +92,12 @@ export const apiAtom = atom<Api | null>(initialApi);
|
|||||||
export const userAtom = atom<UserDto | null>(initialUser);
|
export const userAtom = atom<UserDto | null>(initialUser);
|
||||||
export const wsAtom = atom<WebSocket | null>(null);
|
export const wsAtom = atom<WebSocket | null>(null);
|
||||||
export const cacheVersionAtom = atom<number>(0);
|
export const cacheVersionAtom = atom<number>(0);
|
||||||
|
// Set by a login flow that wants the account saved: the protection picker
|
||||||
|
// shows AFTER the session is authorized (the login screen unmounts on
|
||||||
|
// success, so the modal lives at the root — see PendingAccountSaveModal).
|
||||||
|
export const pendingAccountSaveAtom = atom<{ serverName?: string } | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
interface LoginOptions {
|
interface LoginOptions {
|
||||||
saveAccount?: boolean;
|
saveAccount?: boolean;
|
||||||
@@ -108,6 +115,11 @@ interface JellyfinContextValue {
|
|||||||
serverName?: string,
|
serverName?: string,
|
||||||
options?: LoginOptions,
|
options?: LoginOptions,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
saveCurrentAccount: (options?: {
|
||||||
|
securityType?: AccountSecurityType;
|
||||||
|
pinCode?: string;
|
||||||
|
serverName?: string;
|
||||||
|
}) => Promise<void>;
|
||||||
logout: () => Promise<void>;
|
logout: () => Promise<void>;
|
||||||
initiateQuickConnect: () => Promise<string | undefined>;
|
initiateQuickConnect: () => Promise<string | undefined>;
|
||||||
stopQuickConnectPolling: () => void;
|
stopQuickConnectPolling: () => void;
|
||||||
@@ -165,6 +177,69 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
const { clearAllJellyseerData, setJellyseerrUser } = useJellyseerr();
|
const { clearAllJellyseerData, setJellyseerrUser } = useJellyseerr();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
// --- Session-expiry handling ----------------------------------------------
|
||||||
|
// When the server revokes the token (e.g. the device/session is deleted), a
|
||||||
|
// 401 can surface from any authenticated request. Without central handling
|
||||||
|
// the dead token stays in storage, so every reload re-fires authed calls →
|
||||||
|
// 401 spam + uncaught rejections, and the app lingers in a half-authenticated
|
||||||
|
// state. A single response interceptor on the authenticated api clears the
|
||||||
|
// session on the first 401 so the app drops cleanly to the login screen.
|
||||||
|
const sessionExpiredRef = useRef(false);
|
||||||
|
|
||||||
|
// Shared teardown for manual logout AND forced session expiry — keeping it
|
||||||
|
// in one place prevents the two paths from drifting (a 401 expiry must wipe
|
||||||
|
// plugin settings / Jellyseerr state too, or the next login on the same
|
||||||
|
// device inherits the previous user's data).
|
||||||
|
// Saved credentials are kept so the user can quick-login again.
|
||||||
|
const clearSessionState = useCallback(async () => {
|
||||||
|
// All synchronous teardown first: if the async Jellyseerr cleanup below
|
||||||
|
// fails or resolves late (user may already be re-authenticating), the
|
||||||
|
// session/cache state is already gone.
|
||||||
|
storage.remove("token");
|
||||||
|
storage.remove("user");
|
||||||
|
storage.remove("REACT_QUERY_OFFLINE_CACHE");
|
||||||
|
clearTVDiscoverySafely();
|
||||||
|
setUser(null);
|
||||||
|
setApi(null);
|
||||||
|
setPluginSettings(undefined);
|
||||||
|
queryClient.clear();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await clearAllJellyseerData();
|
||||||
|
} catch (e) {
|
||||||
|
writeErrorLog(
|
||||||
|
`Failed to clear Jellyseerr data: ${e instanceof Error ? e.message : e}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [setUser, setApi, setPluginSettings, clearAllJellyseerData, queryClient]);
|
||||||
|
|
||||||
|
const handleSessionExpired = useCallback(() => {
|
||||||
|
if (sessionExpiredRef.current) return; // run once per session
|
||||||
|
sessionExpiredRef.current = true;
|
||||||
|
clearSessionState().catch((e) =>
|
||||||
|
writeErrorLog(`Session-expiry cleanup failed: ${e?.message ?? e}`),
|
||||||
|
);
|
||||||
|
}, [clearSessionState]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Only guard an authenticated session. A pre-auth api (login screen) keeps
|
||||||
|
// its own handling — a wrong-password 401 is not a session expiry.
|
||||||
|
if (!api?.accessToken) return;
|
||||||
|
sessionExpiredRef.current = false; // re-arm for this fresh session
|
||||||
|
const interceptorId = api.axiosInstance.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
(error) => {
|
||||||
|
if (error?.response?.status === 401) {
|
||||||
|
handleSessionExpired();
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return () => {
|
||||||
|
api.axiosInstance.interceptors.response.eject(interceptorId);
|
||||||
|
};
|
||||||
|
}, [api, handleSessionExpired]);
|
||||||
|
|
||||||
const headers = useMemo(() => {
|
const headers = useMemo(() => {
|
||||||
if (!deviceId) return {};
|
if (!deviceId) return {};
|
||||||
return {
|
return {
|
||||||
@@ -307,6 +382,40 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Persist the CURRENT session to secure storage — used by the post-login
|
||||||
|
// save-account modal (the protection picker shows AFTER a successful
|
||||||
|
// login, for both the password and Quick Connect flows).
|
||||||
|
const saveCurrentAccount = useCallback(
|
||||||
|
async (options?: {
|
||||||
|
securityType?: AccountSecurityType;
|
||||||
|
pinCode?: string;
|
||||||
|
serverName?: string;
|
||||||
|
}) => {
|
||||||
|
const token = storage.getString("token");
|
||||||
|
if (!api?.basePath || !user?.Id || !user.Name || !token) return;
|
||||||
|
const securityType = options?.securityType || "none";
|
||||||
|
let pinHash: string | undefined;
|
||||||
|
if (securityType === "pin") {
|
||||||
|
// Never persist a "pin" credential without its hash — it would be
|
||||||
|
// impossible to unlock.
|
||||||
|
if (!options?.pinCode) throw new Error("PIN code is required");
|
||||||
|
pinHash = await hashPIN(options.pinCode);
|
||||||
|
}
|
||||||
|
await saveAccountCredential({
|
||||||
|
serverUrl: api.basePath,
|
||||||
|
serverName: options?.serverName || "",
|
||||||
|
token,
|
||||||
|
userId: user.Id,
|
||||||
|
username: user.Name,
|
||||||
|
savedAt: Date.now(),
|
||||||
|
securityType,
|
||||||
|
pinHash,
|
||||||
|
primaryImageTag: user.PrimaryImageTag ?? undefined,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[api?.basePath, user],
|
||||||
|
);
|
||||||
|
|
||||||
const loginMutation = useMutation({
|
const loginMutation = useMutation({
|
||||||
mutationFn: async ({
|
mutationFn: async ({
|
||||||
username,
|
username,
|
||||||
@@ -338,7 +447,12 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
if (securityType === "pin" && options.pinCode) {
|
if (securityType === "pin" && options.pinCode) {
|
||||||
pinHash = await hashPIN(options.pinCode);
|
pinHash = await hashPIN(options.pinCode);
|
||||||
}
|
}
|
||||||
|
if (securityType === "pin" && !pinHash) {
|
||||||
|
// Never persist a "pin" credential without its hash — it would be
|
||||||
|
// impossible to unlock. Skip the save rather than failing a login
|
||||||
|
// that already succeeded.
|
||||||
|
writeErrorLog("Account save skipped: PIN required but missing");
|
||||||
|
} else {
|
||||||
await saveAccountCredential({
|
await saveAccountCredential({
|
||||||
serverUrl: api.basePath,
|
serverUrl: api.basePath,
|
||||||
serverName: serverName || "",
|
serverName: serverName || "",
|
||||||
@@ -351,6 +465,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
primaryImageTag: auth.data.User.PrimaryImageTag ?? undefined,
|
primaryImageTag: auth.data.User.PrimaryImageTag ?? undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const recentPluginSettings = await refreshStreamyfinPluginSettings();
|
const recentPluginSettings = await refreshStreamyfinPluginSettings();
|
||||||
if (recentPluginSettings?.jellyseerrServerUrl?.value) {
|
if (recentPluginSettings?.jellyseerrServerUrl?.value) {
|
||||||
@@ -409,19 +524,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
writeErrorLog("Failed to delete expo push token for device"),
|
writeErrorLog("Failed to delete expo push token for device"),
|
||||||
);
|
);
|
||||||
|
|
||||||
storage.remove("token");
|
await clearSessionState();
|
||||||
storage.remove("user");
|
|
||||||
clearTVDiscoverySafely();
|
|
||||||
setUser(null);
|
|
||||||
setApi(null);
|
|
||||||
setPluginSettings(undefined);
|
|
||||||
await clearAllJellyseerData();
|
|
||||||
|
|
||||||
// Clear React Query cache to prevent data from previous account lingering
|
|
||||||
queryClient.clear();
|
|
||||||
storage.remove("REACT_QUERY_OFFLINE_CACHE");
|
|
||||||
|
|
||||||
// Note: We keep saved credentials for quick switching back
|
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("Logout failed:", error);
|
console.error("Logout failed:", error);
|
||||||
@@ -509,7 +612,9 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("Quick login failed:", error);
|
// Expected, handled case (e.g. revoked token → "Session Expired", or
|
||||||
|
// server unreachable): the UI surfaces the message, so warn, don't error.
|
||||||
|
console.warn("Quick login failed:", error);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -620,12 +725,18 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
setUser(storedUser);
|
setUser(storedUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dismiss splash screen with cached data immediately,
|
// Validate the token and refresh user data in the background. Do NOT
|
||||||
// fetch fresh user data in the background
|
// await this: the Jellyfin SDK axios instance has no timeout, so when
|
||||||
setInitialLoaded(true);
|
// offline this call hangs for the full OS TCP timeout (75-120s) and
|
||||||
|
// blocks splash dismissal. The cached storedUser (set above) is enough
|
||||||
try {
|
// to render; on success we just refresh it.
|
||||||
const response = await getUserApi(apiInstance).getCurrentUser();
|
getUserApi(apiInstance)
|
||||||
|
.getCurrentUser()
|
||||||
|
.then(async (response) => {
|
||||||
|
// The response can resolve long after startup (no axios timeout).
|
||||||
|
// If the session changed meanwhile (logout, account switch), drop
|
||||||
|
// it instead of repopulating a stale user / re-saving credentials.
|
||||||
|
if (getTokenFromStorage() !== token) return;
|
||||||
setUser(response.data);
|
setUser(response.data);
|
||||||
|
|
||||||
// Migrate current session to secure storage if not already saved
|
// Migrate current session to secure storage if not already saved
|
||||||
@@ -659,15 +770,21 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
})
|
||||||
// Background fetch failed — app already rendered with cached data
|
.catch((e) => {
|
||||||
console.warn("Background user fetch failed, using cached data:", e);
|
// Expected, handled case (offline, or a token the server rejects —
|
||||||
}
|
// the UI prompts re-login): warn, don't error. Log only
|
||||||
} else {
|
// status/message — never the raw error (axios errors carry the
|
||||||
setInitialLoaded(true);
|
// request config incl. the Authorization header / token).
|
||||||
|
console.warn(
|
||||||
|
"Background user validation failed:",
|
||||||
|
e?.response?.status ?? e?.message ?? "unknown error",
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
} finally {
|
||||||
setInitialLoaded(true);
|
setInitialLoaded(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -681,6 +798,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
removeServer: () => removeServerMutation.mutateAsync(),
|
removeServer: () => removeServerMutation.mutateAsync(),
|
||||||
login: (username, password, serverName, options) =>
|
login: (username, password, serverName, options) =>
|
||||||
loginMutation.mutateAsync({ username, password, serverName, options }),
|
loginMutation.mutateAsync({ username, password, serverName, options }),
|
||||||
|
saveCurrentAccount,
|
||||||
logout: () => logoutMutation.mutateAsync(),
|
logout: () => logoutMutation.mutateAsync(),
|
||||||
initiateQuickConnect,
|
initiateQuickConnect,
|
||||||
stopQuickConnectPolling,
|
stopQuickConnectPolling,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
@@ -19,7 +18,6 @@ interface ServerUrlContextValue {
|
|||||||
effectiveServerUrl: string | null;
|
effectiveServerUrl: string | null;
|
||||||
isUsingLocalUrl: boolean;
|
isUsingLocalUrl: boolean;
|
||||||
currentSSID: string | null;
|
currentSSID: string | null;
|
||||||
connectedToWifi: boolean;
|
|
||||||
refreshUrlState: () => void;
|
refreshUrlState: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +32,7 @@ interface Props {
|
|||||||
export function ServerUrlProvider({ children }: Props): React.ReactElement {
|
export function ServerUrlProvider({ children }: Props): React.ReactElement {
|
||||||
const api = useAtomValue(apiAtom);
|
const api = useAtomValue(apiAtom);
|
||||||
const { switchServerUrl } = useJellyfin();
|
const { switchServerUrl } = useJellyfin();
|
||||||
const { ssid, connectedToWifi, permissionStatus } = useWifiSSID();
|
const { ssid, permissionStatus } = useWifiSSID();
|
||||||
|
|
||||||
const [isUsingLocalUrl, setIsUsingLocalUrl] = useState(false);
|
const [isUsingLocalUrl, setIsUsingLocalUrl] = useState(false);
|
||||||
const [effectiveServerUrl, setEffectiveServerUrl] = useState<string | null>(
|
const [effectiveServerUrl, setEffectiveServerUrl] = useState<string | null>(
|
||||||
@@ -103,25 +101,15 @@ export function ServerUrlProvider({ children }: Props): React.ReactElement {
|
|||||||
};
|
};
|
||||||
}, [ssid, permissionStatus, evaluateAndSwitchUrl]);
|
}, [ssid, permissionStatus, evaluateAndSwitchUrl]);
|
||||||
|
|
||||||
const value = useMemo(
|
return (
|
||||||
() => ({
|
<ServerUrlContext.Provider
|
||||||
|
value={{
|
||||||
effectiveServerUrl,
|
effectiveServerUrl,
|
||||||
isUsingLocalUrl,
|
isUsingLocalUrl,
|
||||||
currentSSID: ssid,
|
currentSSID: ssid,
|
||||||
connectedToWifi,
|
|
||||||
refreshUrlState,
|
refreshUrlState,
|
||||||
}),
|
}}
|
||||||
[
|
>
|
||||||
effectiveServerUrl,
|
|
||||||
isUsingLocalUrl,
|
|
||||||
ssid,
|
|
||||||
connectedToWifi,
|
|
||||||
refreshUrlState,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ServerUrlContext.Provider value={value}>
|
|
||||||
{children}
|
{children}
|
||||||
</ServerUrlContext.Provider>
|
</ServerUrlContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,157 +0,0 @@
|
|||||||
import type React from "react";
|
|
||||||
import {
|
|
||||||
createContext,
|
|
||||||
type ReactNode,
|
|
||||||
useCallback,
|
|
||||||
useContext,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from "react";
|
|
||||||
import { Platform } from "react-native";
|
|
||||||
import { getSSID } from "@/modules/wifi-ssid";
|
|
||||||
|
|
||||||
export type PermissionStatus =
|
|
||||||
| "granted"
|
|
||||||
| "denied"
|
|
||||||
| "undetermined"
|
|
||||||
| "unavailable";
|
|
||||||
|
|
||||||
export interface UseWifiSSIDReturn {
|
|
||||||
ssid: string | null;
|
|
||||||
connectedToWifi: boolean;
|
|
||||||
permissionStatus: PermissionStatus;
|
|
||||||
requestPermission: () => Promise<boolean>;
|
|
||||||
isLoading: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const WifiSsidContext = createContext<UseWifiSSIDReturn | null>(null);
|
|
||||||
|
|
||||||
// WiFi SSID is not available on tvOS, and expo-location isn't needed there.
|
|
||||||
const Location = Platform.isTV ? null : require("expo-location");
|
|
||||||
|
|
||||||
const POLL_INTERVAL_MS = 10_000;
|
|
||||||
|
|
||||||
function mapLocationStatus(status: number | undefined): PermissionStatus {
|
|
||||||
if (!Location) return "unavailable";
|
|
||||||
switch (status) {
|
|
||||||
case Location.PermissionStatus?.GRANTED:
|
|
||||||
return "granted";
|
|
||||||
case Location.PermissionStatus?.DENIED:
|
|
||||||
return "denied";
|
|
||||||
default:
|
|
||||||
return "undetermined";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
children: ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Single source of truth for the current WiFi SSID and the location permission
|
|
||||||
* that gates it. State is shared via context so that granting permission in one
|
|
||||||
* place (e.g. the network settings screen) is reflected everywhere else — most
|
|
||||||
* importantly ServerUrlProvider, which switches between the local and remote
|
|
||||||
* server URLs based on the connected SSID.
|
|
||||||
*
|
|
||||||
* Previously each consumer created its own useWifiSSID instance with independent
|
|
||||||
* state, so the provider never learned that permission had been granted later
|
|
||||||
* in the session and reported "not connected to WiFi" forever.
|
|
||||||
*/
|
|
||||||
export function WifiSsidProvider({ children }: Props): React.ReactElement {
|
|
||||||
const [ssid, setSSID] = useState<string | null>(null);
|
|
||||||
const [connectedToWifi, setConnectedToWifi] = useState(false);
|
|
||||||
const [permissionStatus, setPermissionStatus] = useState<PermissionStatus>(
|
|
||||||
Platform.isTV ? "unavailable" : "undetermined",
|
|
||||||
);
|
|
||||||
const [isLoading, setIsLoading] = useState(!Platform.isTV);
|
|
||||||
|
|
||||||
const fetchSSID = useCallback(async () => {
|
|
||||||
if (Platform.isTV) return;
|
|
||||||
const result = await getSSID();
|
|
||||||
setSSID(result.ssid);
|
|
||||||
setConnectedToWifi(result.connectedToWifi);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const requestPermission = useCallback(async (): Promise<boolean> => {
|
|
||||||
if (Platform.isTV || !Location) {
|
|
||||||
setPermissionStatus("unavailable");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { status } = await Location.requestForegroundPermissionsAsync();
|
|
||||||
const newStatus = mapLocationStatus(status);
|
|
||||||
setPermissionStatus(newStatus);
|
|
||||||
return newStatus === "granted";
|
|
||||||
} catch {
|
|
||||||
setPermissionStatus("unavailable");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Check the location permission once on mount. The poll effect below takes
|
|
||||||
// over fetching the SSID whenever permission becomes "granted".
|
|
||||||
useEffect(() => {
|
|
||||||
if (Platform.isTV || !Location) {
|
|
||||||
setIsLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cancelled = false;
|
|
||||||
async function initialize() {
|
|
||||||
setIsLoading(true);
|
|
||||||
try {
|
|
||||||
const { status } = await Location.getForegroundPermissionsAsync();
|
|
||||||
if (cancelled) return;
|
|
||||||
setPermissionStatus(mapLocationStatus(status));
|
|
||||||
} catch {
|
|
||||||
if (!cancelled) setPermissionStatus("unavailable");
|
|
||||||
}
|
|
||||||
if (!cancelled) setIsLoading(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize();
|
|
||||||
return () => {
|
|
||||||
cancelled = true;
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Fetch the SSID immediately, then poll, whenever permission is granted.
|
|
||||||
// Because permissionStatus is shared, this also covers the case where
|
|
||||||
// permission was granted later (from another consumer) than this provider's
|
|
||||||
// mount time — the original root cause of the "not connected to WiFi" bug.
|
|
||||||
useEffect(() => {
|
|
||||||
if (Platform.isTV || permissionStatus !== "granted") return;
|
|
||||||
|
|
||||||
fetchSSID();
|
|
||||||
const interval = setInterval(fetchSSID, POLL_INTERVAL_MS);
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, [permissionStatus, fetchSSID]);
|
|
||||||
|
|
||||||
const value = useMemo(
|
|
||||||
() => ({
|
|
||||||
ssid,
|
|
||||||
connectedToWifi,
|
|
||||||
permissionStatus,
|
|
||||||
requestPermission,
|
|
||||||
isLoading,
|
|
||||||
}),
|
|
||||||
[ssid, connectedToWifi, permissionStatus, requestPermission, isLoading],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<WifiSsidContext.Provider value={value}>
|
|
||||||
{children}
|
|
||||||
</WifiSsidContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useWifiSsid(): UseWifiSSIDReturn {
|
|
||||||
const context = useContext(WifiSsidContext);
|
|
||||||
if (!context) {
|
|
||||||
throw new Error("useWifiSsid must be used within WifiSsidProvider");
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "حسنًا",
|
"got_it": "حسنًا",
|
||||||
"connection_failed": "فشل الاتصال",
|
"connection_failed": "فشل الاتصال",
|
||||||
"could_not_connect_to_server": "تعذر الاتصال بالخادم. يرجى التحقق من الرابط واتصال الشبكة.",
|
"could_not_connect_to_server": "تعذر الاتصال بالخادم. يرجى التحقق من الرابط واتصال الشبكة.",
|
||||||
"an_unexpected_error_occured": "حدث خطأ غير متوقع",
|
"an_unexpected_error_occurred": "حدث خطأ غير متوقع",
|
||||||
"change_server": "تغيير الخادم",
|
"change_server": "تغيير الخادم",
|
||||||
"invalid_username_or_password": "اسم المستخدم أو كلمة المرور غير صالحة",
|
"invalid_username_or_password": "اسم المستخدم أو كلمة المرور غير صالحة",
|
||||||
"user_does_not_have_permission_to_log_in": "ليس لدى المستخدم صَلاحِيَة تسجيل الدخول",
|
"user_does_not_have_permission_to_log_in": "ليس لدى المستخدم صَلاحِيَة تسجيل الدخول",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "يستغرق الخادم وقتًا طويلاً للرد، يرجى المحاولة مرة أخرى لاحقًا",
|
"server_is_taking_too_long_to_respond_try_again_later": "يستغرق الخادم وقتًا طويلاً للرد، يرجى المحاولة مرة أخرى لاحقًا",
|
||||||
"server_received_too_many_requests_try_again_later": "تلقى الخادم عددًا كبيرًا جدًا من الطلبات، يرجى المحاولة مرة أخرى لاحقًا.",
|
"server_received_too_many_requests_try_again_later": "تلقى الخادم عددًا كبيرًا جدًا من الطلبات، يرجى المحاولة مرة أخرى لاحقًا.",
|
||||||
"there_is_a_server_error": "هناك خطأ في الخادم",
|
"there_is_a_server_error": "هناك خطأ في الخادم",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "حدث خطأ غير متوقع. هل أدخلت رابط الخادم بشكل صحيح؟",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "حدث خطأ غير متوقع. هل أدخلت رابط الخادم بشكل صحيح؟",
|
||||||
"too_old_server_text": "تم اكتشاف خادم Jellyfin غير مدعوم",
|
"too_old_server_text": "تم اكتشاف خادم Jellyfin غير مدعوم",
|
||||||
"too_old_server_description": "يرجى تحديث Jellyfin إلى أحدث إصدار"
|
"too_old_server_description": "يرجى تحديث Jellyfin إلى أحدث إصدار"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "تفويض الدخول السريع",
|
"authorize_button": "تفويض الدخول السريع",
|
||||||
"enter_the_quick_connect_code": "أدخل رمز الدخول السريع...",
|
"enter_the_quick_connect_code": "أدخل رمز الدخول السريع...",
|
||||||
"success": "نجاح",
|
"success": "نجاح",
|
||||||
"quick_connect_autorized": "تم تفويض الدخول السريع",
|
"quick_connect_authorized": "تم تفويض الدخول السريع",
|
||||||
"error": "خطأ",
|
"error": "خطأ",
|
||||||
"invalid_code": "رمز غير صالح",
|
"invalid_code": "رمز غير صالح",
|
||||||
"authorize": "تفويض"
|
"authorize": "تفويض"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "إظهار روابط القائمة المخصصة",
|
"show_custom_menu_links": "إظهار روابط القائمة المخصصة",
|
||||||
"show_large_home_carousel": "إظهار شريط العرض الكبير (تجريبي)",
|
"show_large_home_carousel": "إظهار شريط العرض الكبير (تجريبي)",
|
||||||
"hide_libraries": "إخفاء المكتبات",
|
"hide_libraries": "إخفاء المكتبات",
|
||||||
"select_liraries_you_want_to_hide": "اختر المكتبات التي تريد إخفاءها من تبويب المكتبة وأقسام الصفحة الرئيسية.",
|
"select_libraries_you_want_to_hide": "اختر المكتبات التي تريد إخفاءها من تبويب المكتبة وأقسام الصفحة الرئيسية.",
|
||||||
"disable_haptic_feedback": "تعطيل ردود الفعل اللمسية",
|
"disable_haptic_feedback": "تعطيل ردود الفعل اللمسية",
|
||||||
"default_quality": "الجودة الافتراضية",
|
"default_quality": "الجودة الافتراضية",
|
||||||
"default_playback_speed": "سرعة التشغيل الافتراضية",
|
"default_playback_speed": "سرعة التشغيل الافتراضية",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "الجهاز {{availableSpace}}%",
|
"device_usage": "الجهاز {{availableSpace}}%",
|
||||||
"size_used": "تم استخدام {{used}} من {{total}}",
|
"size_used": "تم استخدام {{used}} من {{total}}",
|
||||||
"delete_all_downloaded_files": "حذف جميع الملفات التي تم تنزيلها",
|
"delete_all_downloaded_files": "حذف جميع الملفات التي تم تنزيلها",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "التخزين المؤقت للموسيقى",
|
"music_cache_title": "التخزين المؤقت للموسيقى",
|
||||||
"music_cache_description": "تخزين الأغاني تلقائياً أثناء الاستماع لضمان تشغيل أكثر سلاسة ودعم الاستماع بدون اتصال",
|
"music_cache_description": "تخزين الأغاني تلقائياً أثناء الاستماع لضمان تشغيل أكثر سلاسة ودعم الاستماع بدون اتصال",
|
||||||
"clear_music_cache": "مسح التخزين المؤقت للموسيقى",
|
"clear_music_cache": "مسح التخزين المؤقت للموسيقى",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "خطأ",
|
"error": "خطأ",
|
||||||
"failed_to_get_stream_url": "فشل في الحصول على رابط البث",
|
"failed_to_get_stream_url": "فشل في الحصول على رابط البث",
|
||||||
"an_error_occured_while_playing_the_video": "حدث خطأ أثناء تشغيل الفيديو. تحقق من السجلات في الإعدادات.",
|
"an_error_occurred_while_playing_the_video": "حدث خطأ أثناء تشغيل الفيديو. تحقق من السجلات في الإعدادات.",
|
||||||
"client_error": "خطأ في المشغّل",
|
"client_error": "خطأ في المشغّل",
|
||||||
"could_not_create_stream_for_chromecast": "تعذر إنشاء بث لـChromecast",
|
"could_not_create_stream_for_chromecast": "تعذر إنشاء بث لـChromecast",
|
||||||
"message_from_server": "رسالة من الخادم: {{message}}",
|
"message_from_server": "رسالة من الخادم: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Entesos",
|
"got_it": "Entesos",
|
||||||
"connection_failed": "Ha fallat la connexió",
|
"connection_failed": "Ha fallat la connexió",
|
||||||
"could_not_connect_to_server": "No s'ha pogut connectar amb el servidor. Comproveu l'URL i la connexió de xarxa.",
|
"could_not_connect_to_server": "No s'ha pogut connectar amb el servidor. Comproveu l'URL i la connexió de xarxa.",
|
||||||
"an_unexpected_error_occured": "S'ha produït un error inesperat",
|
"an_unexpected_error_occurred": "S'ha produït un error inesperat",
|
||||||
"change_server": "Canvia el servidor",
|
"change_server": "Canvia el servidor",
|
||||||
"invalid_username_or_password": "Nom d'usuari o contrasenya incorrectes",
|
"invalid_username_or_password": "Nom d'usuari o contrasenya incorrectes",
|
||||||
"user_does_not_have_permission_to_log_in": "L'usuari no té permís per iniciar sessió",
|
"user_does_not_have_permission_to_log_in": "L'usuari no té permís per iniciar sessió",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "El servidor triga massa a respondre, torneu-ho a provar més tard",
|
"server_is_taking_too_long_to_respond_try_again_later": "El servidor triga massa a respondre, torneu-ho a provar més tard",
|
||||||
"server_received_too_many_requests_try_again_later": "El servidor ha rebut massa sol·licituds, torneu-ho a provar més tard.",
|
"server_received_too_many_requests_try_again_later": "El servidor ha rebut massa sol·licituds, torneu-ho a provar més tard.",
|
||||||
"there_is_a_server_error": "Error del servidor",
|
"there_is_a_server_error": "Error del servidor",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "S'ha produït un error inesperat. Heu introduït correctament l'URL del servidor?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "S'ha produït un error inesperat. Heu introduït correctament l'URL del servidor?",
|
||||||
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
||||||
"too_old_server_description": "Please update Jellyfin to the latest version"
|
"too_old_server_description": "Please update Jellyfin to the latest version"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Autoritza connexió ràpida",
|
"authorize_button": "Autoritza connexió ràpida",
|
||||||
"enter_the_quick_connect_code": "Introdueix el codi de connexió ràpida...",
|
"enter_the_quick_connect_code": "Introdueix el codi de connexió ràpida...",
|
||||||
"success": "Èxit",
|
"success": "Èxit",
|
||||||
"quick_connect_autorized": "Connexió ràpida autoritzada",
|
"quick_connect_authorized": "Connexió ràpida autoritzada",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"invalid_code": "Codi invàlid",
|
"invalid_code": "Codi invàlid",
|
||||||
"authorize": "Autoritza"
|
"authorize": "Autoritza"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Mostrar enllaços del menú personalitzats",
|
"show_custom_menu_links": "Mostrar enllaços del menú personalitzats",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
"hide_libraries": "Oculta biblioteques",
|
"hide_libraries": "Oculta biblioteques",
|
||||||
"select_liraries_you_want_to_hide": "Seleccioneu les biblioteques que voleu ocultar de la pestanya Biblioteca i de les seccions de la pàgina d'inici.",
|
"select_libraries_you_want_to_hide": "Seleccioneu les biblioteques que voleu ocultar de la pestanya Biblioteca i de les seccions de la pàgina d'inici.",
|
||||||
"disable_haptic_feedback": "Desactiva la resposta hàptica",
|
"disable_haptic_feedback": "Desactiva la resposta hàptica",
|
||||||
"default_quality": "Qualitat per defecte",
|
"default_quality": "Qualitat per defecte",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Dispositiu {{availableSpace}}%",
|
"device_usage": "Dispositiu {{availableSpace}}%",
|
||||||
"size_used": "{{used}} de {{total}} utilitzat",
|
"size_used": "{{used}} de {{total}} utilitzat",
|
||||||
"delete_all_downloaded_files": "Suprimeix tots els fitxers descarregats",
|
"delete_all_downloaded_files": "Suprimeix tots els fitxers descarregats",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"failed_to_get_stream_url": "No s'ha pogut obtenir l'URL del flux",
|
"failed_to_get_stream_url": "No s'ha pogut obtenir l'URL del flux",
|
||||||
"an_error_occured_while_playing_the_video": "S'ha produït un error en reproduir el vídeo. Consulteu els registres a la configuració.",
|
"an_error_occurred_while_playing_the_video": "S'ha produït un error en reproduir el vídeo. Consulteu els registres a la configuració.",
|
||||||
"client_error": "Error del client",
|
"client_error": "Error del client",
|
||||||
"could_not_create_stream_for_chromecast": "No s'ha pogut crear un flux per a Chromecast",
|
"could_not_create_stream_for_chromecast": "No s'ha pogut crear un flux per a Chromecast",
|
||||||
"message_from_server": "Missatge del servidor: {{message}}",
|
"message_from_server": "Missatge del servidor: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Rozumím",
|
"got_it": "Rozumím",
|
||||||
"connection_failed": "Připojení se nezdařilo",
|
"connection_failed": "Připojení se nezdařilo",
|
||||||
"could_not_connect_to_server": "Nelze se připojit k serveru. Zkontrolujte adresu URL a síťové připojení.",
|
"could_not_connect_to_server": "Nelze se připojit k serveru. Zkontrolujte adresu URL a síťové připojení.",
|
||||||
"an_unexpected_error_occured": "Došlo k neočekávané chybě",
|
"an_unexpected_error_occurred": "Došlo k neočekávané chybě",
|
||||||
"change_server": "Změnit server",
|
"change_server": "Změnit server",
|
||||||
"invalid_username_or_password": "Neplatné uživatelské jméno nebo heslo",
|
"invalid_username_or_password": "Neplatné uživatelské jméno nebo heslo",
|
||||||
"user_does_not_have_permission_to_log_in": "Uživatel nemá oprávnění k přihlášení",
|
"user_does_not_have_permission_to_log_in": "Uživatel nemá oprávnění k přihlášení",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Reakce na server trvá příliš dlouho, zkuste to znovu později",
|
"server_is_taking_too_long_to_respond_try_again_later": "Reakce na server trvá příliš dlouho, zkuste to znovu později",
|
||||||
"server_received_too_many_requests_try_again_later": "Server obdržel příliš mnoho požadavků, opakujte akci později.",
|
"server_received_too_many_requests_try_again_later": "Server obdržel příliš mnoho požadavků, opakujte akci později.",
|
||||||
"there_is_a_server_error": "Došlo k chybě serveru",
|
"there_is_a_server_error": "Došlo k chybě serveru",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Došlo k neočekávané chybě. Vložili jste adresu URL serveru?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Došlo k neočekávané chybě. Vložili jste adresu URL serveru?",
|
||||||
"too_old_server_text": "Objeven nepodporovaný Jellyfin server",
|
"too_old_server_text": "Objeven nepodporovaný Jellyfin server",
|
||||||
"too_old_server_description": "Prosím aktualizujte Jellyfin na nejnovější verzi"
|
"too_old_server_description": "Prosím aktualizujte Jellyfin na nejnovější verzi"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Autorizovat rychlé připojení",
|
"authorize_button": "Autorizovat rychlé připojení",
|
||||||
"enter_the_quick_connect_code": "Zadejte kód rychlého připojení...",
|
"enter_the_quick_connect_code": "Zadejte kód rychlého připojení...",
|
||||||
"success": "Úspěšně",
|
"success": "Úspěšně",
|
||||||
"quick_connect_autorized": "Oprávněné Rychlé připojení",
|
"quick_connect_authorized": "Oprávněné Rychlé připojení",
|
||||||
"error": "Chyba",
|
"error": "Chyba",
|
||||||
"invalid_code": "Neplatný kód",
|
"invalid_code": "Neplatný kód",
|
||||||
"authorize": "Autorizovat"
|
"authorize": "Autorizovat"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Zobrazit vlastní Menu odkazy",
|
"show_custom_menu_links": "Zobrazit vlastní Menu odkazy",
|
||||||
"show_large_home_carousel": "Zobrazit velký přehled (beta)",
|
"show_large_home_carousel": "Zobrazit velký přehled (beta)",
|
||||||
"hide_libraries": "Skrýt knihovny",
|
"hide_libraries": "Skrýt knihovny",
|
||||||
"select_liraries_you_want_to_hide": "Vyberte knihovny, které chcete skrýt v záložce Knihovna a v sekcích domovské stránky.",
|
"select_libraries_you_want_to_hide": "Vyberte knihovny, které chcete skrýt v záložce Knihovna a v sekcích domovské stránky.",
|
||||||
"disable_haptic_feedback": "Zakázat Haptickou zpětnou vazbu",
|
"disable_haptic_feedback": "Zakázat Haptickou zpětnou vazbu",
|
||||||
"default_quality": "Výchozí kvalita",
|
"default_quality": "Výchozí kvalita",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Zařízení {{availableSpace}}%",
|
"device_usage": "Zařízení {{availableSpace}}%",
|
||||||
"size_used": "{{used}} z {{total}} využito",
|
"size_used": "{{used}} z {{total}} využito",
|
||||||
"delete_all_downloaded_files": "Odstranit všechny stažené soubory",
|
"delete_all_downloaded_files": "Odstranit všechny stažené soubory",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Chyba",
|
"error": "Chyba",
|
||||||
"failed_to_get_stream_url": "Nepodařilo se získat URL streamu",
|
"failed_to_get_stream_url": "Nepodařilo se získat URL streamu",
|
||||||
"an_error_occured_while_playing_the_video": "Při přehrávání videa došlo k chybě. Zkontrolujte logy v nastavení.",
|
"an_error_occurred_while_playing_the_video": "Při přehrávání videa došlo k chybě. Zkontrolujte logy v nastavení.",
|
||||||
"client_error": "Chyba klienta",
|
"client_error": "Chyba klienta",
|
||||||
"could_not_create_stream_for_chromecast": "Nelze vytvořit stream pro Chromecast",
|
"could_not_create_stream_for_chromecast": "Nelze vytvořit stream pro Chromecast",
|
||||||
"message_from_server": "Zpráva od serveru: {{message}}",
|
"message_from_server": "Zpráva od serveru: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Forstået",
|
"got_it": "Forstået",
|
||||||
"connection_failed": "Forbindelsen mislykkedes",
|
"connection_failed": "Forbindelsen mislykkedes",
|
||||||
"could_not_connect_to_server": "Kunne ikke oprette forbindelse til serveren. Tjek venligst URL'en og din netværksforbindelse.",
|
"could_not_connect_to_server": "Kunne ikke oprette forbindelse til serveren. Tjek venligst URL'en og din netværksforbindelse.",
|
||||||
"an_unexpected_error_occured": "Der opstod en uventet fejl",
|
"an_unexpected_error_occurred": "Der opstod en uventet fejl",
|
||||||
"change_server": "Skift server",
|
"change_server": "Skift server",
|
||||||
"invalid_username_or_password": "Ugyldigt brugernavn eller adgangskode",
|
"invalid_username_or_password": "Ugyldigt brugernavn eller adgangskode",
|
||||||
"user_does_not_have_permission_to_log_in": "Brugeren har ikke tilladelse til at logge ind",
|
"user_does_not_have_permission_to_log_in": "Brugeren har ikke tilladelse til at logge ind",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Serveren svarer for langsomt, prøv igen senere",
|
"server_is_taking_too_long_to_respond_try_again_later": "Serveren svarer for langsomt, prøv igen senere",
|
||||||
"server_received_too_many_requests_try_again_later": "Serveren modtog for mange forespørgsler, prøv igen senere.",
|
"server_received_too_many_requests_try_again_later": "Serveren modtog for mange forespørgsler, prøv igen senere.",
|
||||||
"there_is_a_server_error": "Der er en serverfejl",
|
"there_is_a_server_error": "Der er en serverfejl",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Der opstod en uventet fejl. Har du indtastet serverens URL korrekt?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Der opstod en uventet fejl. Har du indtastet serverens URL korrekt?",
|
||||||
"too_old_server_text": "Ikke Understøttet Jellyfin Server Opdaget",
|
"too_old_server_text": "Ikke Understøttet Jellyfin Server Opdaget",
|
||||||
"too_old_server_description": "Opdater venligst Jellyfin til den seneste version"
|
"too_old_server_description": "Opdater venligst Jellyfin til den seneste version"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Autoriser Hurtigforbindelse",
|
"authorize_button": "Autoriser Hurtigforbindelse",
|
||||||
"enter_the_quick_connect_code": "Indtast koden til hurtigforbindelse...",
|
"enter_the_quick_connect_code": "Indtast koden til hurtigforbindelse...",
|
||||||
"success": "Succes",
|
"success": "Succes",
|
||||||
"quick_connect_autorized": "Hurtigforbindelse autoriseret",
|
"quick_connect_authorized": "Hurtigforbindelse autoriseret",
|
||||||
"error": "Fejl",
|
"error": "Fejl",
|
||||||
"invalid_code": "Ugyldig kode",
|
"invalid_code": "Ugyldig kode",
|
||||||
"authorize": "Autoriser"
|
"authorize": "Autoriser"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Vis tilpassede menulinks",
|
"show_custom_menu_links": "Vis tilpassede menulinks",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
"hide_libraries": "Skjul biblioteker",
|
"hide_libraries": "Skjul biblioteker",
|
||||||
"select_liraries_you_want_to_hide": "Vælg de biblioteker, du ønsker at skjule fra fanen Bibliotek og startside sektionerne.",
|
"select_libraries_you_want_to_hide": "Vælg de biblioteker, du ønsker at skjule fra fanen Bibliotek og startside sektionerne.",
|
||||||
"disable_haptic_feedback": "Deaktiver haptisk feedback",
|
"disable_haptic_feedback": "Deaktiver haptisk feedback",
|
||||||
"default_quality": "Standard kvalitet",
|
"default_quality": "Standard kvalitet",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Enhedsforbrug: {{availableSpace}}%",
|
"device_usage": "Enhedsforbrug: {{availableSpace}}%",
|
||||||
"size_used": "{{used}} af {{total}} brugt",
|
"size_used": "{{used}} af {{total}} brugt",
|
||||||
"delete_all_downloaded_files": "Slet alle downloadede filer",
|
"delete_all_downloaded_files": "Slet alle downloadede filer",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Fejl",
|
"error": "Fejl",
|
||||||
"failed_to_get_stream_url": "Kunne ikke hente stream URL'en",
|
"failed_to_get_stream_url": "Kunne ikke hente stream URL'en",
|
||||||
"an_error_occured_while_playing_the_video": "Der opstod en fejl under afspilning af videoen. Tjek logfilerne i indstillinger.",
|
"an_error_occurred_while_playing_the_video": "Der opstod en fejl under afspilning af videoen. Tjek logfilerne i indstillinger.",
|
||||||
"client_error": "Klientfejl",
|
"client_error": "Klientfejl",
|
||||||
"could_not_create_stream_for_chromecast": "Kunne ikke oprette en stream til Chromecast",
|
"could_not_create_stream_for_chromecast": "Kunne ikke oprette en stream til Chromecast",
|
||||||
"message_from_server": "Besked fra server: {{message}}",
|
"message_from_server": "Besked fra server: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Verstanden",
|
"got_it": "Verstanden",
|
||||||
"connection_failed": "Verbindung fehlgeschlagen",
|
"connection_failed": "Verbindung fehlgeschlagen",
|
||||||
"could_not_connect_to_server": "Verbindung zum Server fehlgeschlagen. Bitte überprüf die URL und deine Netzwerkverbindung.",
|
"could_not_connect_to_server": "Verbindung zum Server fehlgeschlagen. Bitte überprüf die URL und deine Netzwerkverbindung.",
|
||||||
"an_unexpected_error_occured": "Ein unerwarteter Fehler ist aufgetreten",
|
"an_unexpected_error_occurred": "Ein unerwarteter Fehler ist aufgetreten",
|
||||||
"change_server": "Server wechseln",
|
"change_server": "Server wechseln",
|
||||||
"invalid_username_or_password": "Ungültiger Benutzername oder Passwort",
|
"invalid_username_or_password": "Ungültiger Benutzername oder Passwort",
|
||||||
"user_does_not_have_permission_to_log_in": "Benutzer hat keine Berechtigung, um sich anzumelden",
|
"user_does_not_have_permission_to_log_in": "Benutzer hat keine Berechtigung, um sich anzumelden",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Der Server benötigt zu lange, um zu antworten. Bitte versuch es später erneut",
|
"server_is_taking_too_long_to_respond_try_again_later": "Der Server benötigt zu lange, um zu antworten. Bitte versuch es später erneut",
|
||||||
"server_received_too_many_requests_try_again_later": "Der Server hat zu viele Anfragen erhalten. Bitte versuch es später erneut.",
|
"server_received_too_many_requests_try_again_later": "Der Server hat zu viele Anfragen erhalten. Bitte versuch es später erneut.",
|
||||||
"there_is_a_server_error": "Es gibt einen Serverfehler",
|
"there_is_a_server_error": "Es gibt einen Serverfehler",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Ein unerwarteter Fehler ist aufgetreten. Hast du die Server-URL korrekt eingegeben?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Ein unerwarteter Fehler ist aufgetreten. Hast du die Server-URL korrekt eingegeben?",
|
||||||
"too_old_server_text": "Nicht unterstützter Jellyfin Server entdeckt",
|
"too_old_server_text": "Nicht unterstützter Jellyfin Server entdeckt",
|
||||||
"too_old_server_description": "Bitte aktualisiere Jellyfin auf die neueste Version"
|
"too_old_server_description": "Bitte aktualisiere Jellyfin auf die neueste Version"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Quick Connect autorisieren",
|
"authorize_button": "Quick Connect autorisieren",
|
||||||
"enter_the_quick_connect_code": "Quick Connect-Code eingeben...",
|
"enter_the_quick_connect_code": "Quick Connect-Code eingeben...",
|
||||||
"success": "Erfolgreich verbunden",
|
"success": "Erfolgreich verbunden",
|
||||||
"quick_connect_autorized": "Quick Connect autorisiert",
|
"quick_connect_authorized": "Quick Connect autorisiert",
|
||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
"invalid_code": "Ungültiger Code",
|
"invalid_code": "Ungültiger Code",
|
||||||
"authorize": "Autorisieren"
|
"authorize": "Autorisieren"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Benutzerdefinierte Menülinks anzeigen",
|
"show_custom_menu_links": "Benutzerdefinierte Menülinks anzeigen",
|
||||||
"show_large_home_carousel": "Zeige große Startseiten-Übersicht (Beta)",
|
"show_large_home_carousel": "Zeige große Startseiten-Übersicht (Beta)",
|
||||||
"hide_libraries": "Bibliotheken ausblenden",
|
"hide_libraries": "Bibliotheken ausblenden",
|
||||||
"select_liraries_you_want_to_hide": "Bibliotheken auswählen die aus dem Bibliothekstab und der Startseite ausgeblendet werden sollen.",
|
"select_libraries_you_want_to_hide": "Bibliotheken auswählen die aus dem Bibliothekstab und der Startseite ausgeblendet werden sollen.",
|
||||||
"disable_haptic_feedback": "Haptisches Feedback deaktivieren",
|
"disable_haptic_feedback": "Haptisches Feedback deaktivieren",
|
||||||
"default_quality": "Standardqualität",
|
"default_quality": "Standardqualität",
|
||||||
"default_playback_speed": "Standard-Wiedergabegeschwindigkeit",
|
"default_playback_speed": "Standard-Wiedergabegeschwindigkeit",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Gerät {{availableSpace}}%",
|
"device_usage": "Gerät {{availableSpace}}%",
|
||||||
"size_used": "{{used}} von {{total}} genutzt",
|
"size_used": "{{used}} von {{total}} genutzt",
|
||||||
"delete_all_downloaded_files": "Alle heruntergeladenen Dateien löschen",
|
"delete_all_downloaded_files": "Alle heruntergeladenen Dateien löschen",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Musik-Cache",
|
"music_cache_title": "Musik-Cache",
|
||||||
"music_cache_description": "Beim Anhören Titel automatisch in den Cache laden um bessere Wiedergabe und Offline-Wiedergabe zu ermöglichen",
|
"music_cache_description": "Beim Anhören Titel automatisch in den Cache laden um bessere Wiedergabe und Offline-Wiedergabe zu ermöglichen",
|
||||||
"clear_music_cache": "Musik-Cache leeren",
|
"clear_music_cache": "Musik-Cache leeren",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
"failed_to_get_stream_url": "Fehler beim Abrufen der Stream-URL",
|
"failed_to_get_stream_url": "Fehler beim Abrufen der Stream-URL",
|
||||||
"an_error_occured_while_playing_the_video": "Ein Fehler ist beim Abspielen des Videos aufgetreten. Logs in den Einstellungen überprüfen.",
|
"an_error_occurred_while_playing_the_video": "Ein Fehler ist beim Abspielen des Videos aufgetreten. Logs in den Einstellungen überprüfen.",
|
||||||
"client_error": "Client-Fehler",
|
"client_error": "Client-Fehler",
|
||||||
"could_not_create_stream_for_chromecast": "Konnte keinen Stream für Chromecast erstellen",
|
"could_not_create_stream_for_chromecast": "Konnte keinen Stream für Chromecast erstellen",
|
||||||
"message_from_server": "Nachricht vom Server: {{message}}",
|
"message_from_server": "Nachricht vom Server: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Το Κατάλαβα",
|
"got_it": "Το Κατάλαβα",
|
||||||
"connection_failed": "Η Σύνδεση Απέτυχε",
|
"connection_failed": "Η Σύνδεση Απέτυχε",
|
||||||
"could_not_connect_to_server": "Δεν ήταν δυνατή η σύνδεση με τον διακομιστή. Παρακαλώ ελέγξτε τη διεύθυνση URL και τη σύνδεση δικτύου σας.",
|
"could_not_connect_to_server": "Δεν ήταν δυνατή η σύνδεση με τον διακομιστή. Παρακαλώ ελέγξτε τη διεύθυνση URL και τη σύνδεση δικτύου σας.",
|
||||||
"an_unexpected_error_occured": "Παρουσιάστηκε Απροσδόκητο Σφάλμα",
|
"an_unexpected_error_occurred": "Παρουσιάστηκε Απροσδόκητο Σφάλμα",
|
||||||
"change_server": "Αλλαγή Διακομιστή",
|
"change_server": "Αλλαγή Διακομιστή",
|
||||||
"invalid_username_or_password": "Μη έγκυρο όνομα χρήστη ή κωδικός πρόσβασης",
|
"invalid_username_or_password": "Μη έγκυρο όνομα χρήστη ή κωδικός πρόσβασης",
|
||||||
"user_does_not_have_permission_to_log_in": "Ο χρήστης δεν έχει άδεια να συνδεθεί",
|
"user_does_not_have_permission_to_log_in": "Ο χρήστης δεν έχει άδεια να συνδεθεί",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Ο διακομιστής καθυστερεί πάρα πολύ για να απαντήσει, δοκιμάστε ξανά αργότερα",
|
"server_is_taking_too_long_to_respond_try_again_later": "Ο διακομιστής καθυστερεί πάρα πολύ για να απαντήσει, δοκιμάστε ξανά αργότερα",
|
||||||
"server_received_too_many_requests_try_again_later": "Ο διακομιστής έλαβε πάρα πολλά αιτήματα, δοκιμάστε ξανά αργότερα.",
|
"server_received_too_many_requests_try_again_later": "Ο διακομιστής έλαβε πάρα πολλά αιτήματα, δοκιμάστε ξανά αργότερα.",
|
||||||
"there_is_a_server_error": "Υπάρχει ένα σφάλμα διακομιστή",
|
"there_is_a_server_error": "Υπάρχει ένα σφάλμα διακομιστή",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Παρουσιάστηκε μη αναμενόμενο σφάλμα. Εισαγάγετε σωστά το URL του διακομιστή?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Παρουσιάστηκε μη αναμενόμενο σφάλμα. Εισαγάγετε σωστά το URL του διακομιστή?",
|
||||||
"too_old_server_text": "Ανακαλύφθηκε Μη Υποστηριζόμενος Εξυπηρετητής Jellyfin",
|
"too_old_server_text": "Ανακαλύφθηκε Μη Υποστηριζόμενος Εξυπηρετητής Jellyfin",
|
||||||
"too_old_server_description": "Παρακαλούμε ενημερώστε το Jellyfin στην τελευταία έκδοση"
|
"too_old_server_description": "Παρακαλούμε ενημερώστε το Jellyfin στην τελευταία έκδοση"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Εξουσιοδότηση Γρήγορης Σύνδεσης",
|
"authorize_button": "Εξουσιοδότηση Γρήγορης Σύνδεσης",
|
||||||
"enter_the_quick_connect_code": "Εισάγετε τον κωδικό γρήγορης σύνδεσης...",
|
"enter_the_quick_connect_code": "Εισάγετε τον κωδικό γρήγορης σύνδεσης...",
|
||||||
"success": "Επιτυχία",
|
"success": "Επιτυχία",
|
||||||
"quick_connect_autorized": "Γρήγορη Σύνδεση Εξουσιοδοτήθηκε",
|
"quick_connect_authorized": "Γρήγορη Σύνδεση Εξουσιοδοτήθηκε",
|
||||||
"error": "Σφάλμα",
|
"error": "Σφάλμα",
|
||||||
"invalid_code": "Μη Έγκυρος Κωδικός",
|
"invalid_code": "Μη Έγκυρος Κωδικός",
|
||||||
"authorize": "Εξουσιοδότηση"
|
"authorize": "Εξουσιοδότηση"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Εμφάνιση Προσαρμοσμένων Συνδέσμων Μενού",
|
"show_custom_menu_links": "Εμφάνιση Προσαρμοσμένων Συνδέσμων Μενού",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
"hide_libraries": "Απόκρυψη Βιβλιοθηκών",
|
"hide_libraries": "Απόκρυψη Βιβλιοθηκών",
|
||||||
"select_liraries_you_want_to_hide": "Επιλέξτε τις βιβλιοθήκες που θέλετε να αποκρύψετε από την καρτέλα της Βιβλιοθήκης και τις ενότητες της αρχικής σελίδας.",
|
"select_libraries_you_want_to_hide": "Επιλέξτε τις βιβλιοθήκες που θέλετε να αποκρύψετε από την καρτέλα της Βιβλιοθήκης και τις ενότητες της αρχικής σελίδας.",
|
||||||
"disable_haptic_feedback": "Απενεργοποίηση Απτικής Ανατροφοδότησης",
|
"disable_haptic_feedback": "Απενεργοποίηση Απτικής Ανατροφοδότησης",
|
||||||
"default_quality": "Προεπιλεγμένη Ποιότητα",
|
"default_quality": "Προεπιλεγμένη Ποιότητα",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Device {{availableSpace}}%",
|
"device_usage": "Device {{availableSpace}}%",
|
||||||
"size_used": "{{used}} {{total}} Χρησιμοποιείται",
|
"size_used": "{{used}} {{total}} Χρησιμοποιείται",
|
||||||
"delete_all_downloaded_files": "Διαγραφή Όλων Των Ληφθέντων Αρχείων",
|
"delete_all_downloaded_files": "Διαγραφή Όλων Των Ληφθέντων Αρχείων",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Σφάλμα",
|
"error": "Σφάλμα",
|
||||||
"failed_to_get_stream_url": "Αποτυχία λήψης του URL ροής",
|
"failed_to_get_stream_url": "Αποτυχία λήψης του URL ροής",
|
||||||
"an_error_occured_while_playing_the_video": "Παρουσιάστηκε σφάλμα κατά την αναπαραγωγή του βίντεο. Ελέγξτε τα αρχεία καταγραφής στις ρυθμίσεις.",
|
"an_error_occurred_while_playing_the_video": "Παρουσιάστηκε σφάλμα κατά την αναπαραγωγή του βίντεο. Ελέγξτε τα αρχεία καταγραφής στις ρυθμίσεις.",
|
||||||
"client_error": "Σφάλμα Πελάτη",
|
"client_error": "Σφάλμα Πελάτη",
|
||||||
"could_not_create_stream_for_chromecast": "Αδυναμία δημιουργίας ροής για το Chromecast",
|
"could_not_create_stream_for_chromecast": "Αδυναμία δημιουργίας ροής για το Chromecast",
|
||||||
"message_from_server": "Μήνυμα από το διακομιστή: {{message}}",
|
"message_from_server": "Μήνυμα από το διακομιστή: {{message}}",
|
||||||
|
|||||||
@@ -174,10 +174,7 @@
|
|||||||
"network_already_added": "Network already added",
|
"network_already_added": "Network already added",
|
||||||
"no_wifi_connected": "Not connected to Wi-Fi",
|
"no_wifi_connected": "Not connected to Wi-Fi",
|
||||||
"permission_denied": "Location permission denied",
|
"permission_denied": "Location permission denied",
|
||||||
"permission_denied_explanation": "Location permission is required to detect the Wi-Fi network for auto-switching. Please enable it in Settings.",
|
"permission_denied_explanation": "Location permission is required to detect the Wi-Fi network for auto-switching. Please enable it in Settings."
|
||||||
"ssid_hidden": "SSID hidden",
|
|
||||||
"location_off_description": "Connected to Wi-Fi, but the network name is hidden. Enable device Location services and grant the app location permission to detect it.",
|
|
||||||
"open_location_settings": "Open Location settings"
|
|
||||||
},
|
},
|
||||||
"user_info": {
|
"user_info": {
|
||||||
"user_info_title": "User info",
|
"user_info_title": "User info",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Entendido",
|
"got_it": "Entendido",
|
||||||
"connection_failed": "Conexión fallida",
|
"connection_failed": "Conexión fallida",
|
||||||
"could_not_connect_to_server": "No se pudo conectar al servidor. Por favor comprueba la URL y tu conexión de red.",
|
"could_not_connect_to_server": "No se pudo conectar al servidor. Por favor comprueba la URL y tu conexión de red.",
|
||||||
"an_unexpected_error_occured": "Ha ocurrido un error inesperado",
|
"an_unexpected_error_occurred": "Ha ocurrido un error inesperado",
|
||||||
"change_server": "Cambiar servidor",
|
"change_server": "Cambiar servidor",
|
||||||
"invalid_username_or_password": "Usuario o contraseña inválidos",
|
"invalid_username_or_password": "Usuario o contraseña inválidos",
|
||||||
"user_does_not_have_permission_to_log_in": "El usuario no tiene permiso para iniciar sesión",
|
"user_does_not_have_permission_to_log_in": "El usuario no tiene permiso para iniciar sesión",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "El servidor está tardando mucho en responder, inténtalo de nuevo más tarde.",
|
"server_is_taking_too_long_to_respond_try_again_later": "El servidor está tardando mucho en responder, inténtalo de nuevo más tarde.",
|
||||||
"server_received_too_many_requests_try_again_later": "El servidor está recibiendo muchas peticiones, inténtalo de nuevo más tarde.",
|
"server_received_too_many_requests_try_again_later": "El servidor está recibiendo muchas peticiones, inténtalo de nuevo más tarde.",
|
||||||
"there_is_a_server_error": "Hay un error en el servidor",
|
"there_is_a_server_error": "Hay un error en el servidor",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Ha ocurrido un error inesperado. ¿Has introducido la URL correcta?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Ha ocurrido un error inesperado. ¿Has introducido la URL correcta?",
|
||||||
"too_old_server_text": "Servidor Jellyfin no soportado descubierto",
|
"too_old_server_text": "Servidor Jellyfin no soportado descubierto",
|
||||||
"too_old_server_description": "Por favor, actualiza Jellyfin a la última versión"
|
"too_old_server_description": "Por favor, actualiza Jellyfin a la última versión"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Autorizar conexión rápida",
|
"authorize_button": "Autorizar conexión rápida",
|
||||||
"enter_the_quick_connect_code": "Introduce el código de conexión rápida...",
|
"enter_the_quick_connect_code": "Introduce el código de conexión rápida...",
|
||||||
"success": "Hecho",
|
"success": "Hecho",
|
||||||
"quick_connect_autorized": "Conexión rápida autorizada",
|
"quick_connect_authorized": "Conexión rápida autorizada",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"invalid_code": "Código inválido",
|
"invalid_code": "Código inválido",
|
||||||
"authorize": "Autorizar"
|
"authorize": "Autorizar"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Mostrar enlaces de menú personalizados",
|
"show_custom_menu_links": "Mostrar enlaces de menú personalizados",
|
||||||
"show_large_home_carousel": "Mostrar carrusel del menú principal grande (beta)",
|
"show_large_home_carousel": "Mostrar carrusel del menú principal grande (beta)",
|
||||||
"hide_libraries": "Ocultar bibliotecas",
|
"hide_libraries": "Ocultar bibliotecas",
|
||||||
"select_liraries_you_want_to_hide": "Selecciona las bibliotecas que quieres ocultar de la pestaña Bibliotecas y de Inicio.",
|
"select_libraries_you_want_to_hide": "Selecciona las bibliotecas que quieres ocultar de la pestaña Bibliotecas y de Inicio.",
|
||||||
"disable_haptic_feedback": "Desactivar feedback háptico",
|
"disable_haptic_feedback": "Desactivar feedback háptico",
|
||||||
"default_quality": "Calidad por defecto",
|
"default_quality": "Calidad por defecto",
|
||||||
"default_playback_speed": "Velocidad de reproducción predeterminada",
|
"default_playback_speed": "Velocidad de reproducción predeterminada",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Dispositivo {{availableSpace}}%",
|
"device_usage": "Dispositivo {{availableSpace}}%",
|
||||||
"size_used": "{{used}} de {{total}} usado",
|
"size_used": "{{used}} de {{total}} usado",
|
||||||
"delete_all_downloaded_files": "Eliminar todos los archivos descargados",
|
"delete_all_downloaded_files": "Eliminar todos los archivos descargados",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Caché de música",
|
"music_cache_title": "Caché de música",
|
||||||
"music_cache_description": "Cachear automáticamente las canciones mientras escuchas una reproducción más suave y soporte sin conexión",
|
"music_cache_description": "Cachear automáticamente las canciones mientras escuchas una reproducción más suave y soporte sin conexión",
|
||||||
"clear_music_cache": "Borrar Caché de Música",
|
"clear_music_cache": "Borrar Caché de Música",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"failed_to_get_stream_url": "Error al obtener la URL del Steam",
|
"failed_to_get_stream_url": "Error al obtener la URL del Steam",
|
||||||
"an_error_occured_while_playing_the_video": "Ha ocurrido un error al reproducir el vídeo. Comprueba los registros en la configuración.",
|
"an_error_occurred_while_playing_the_video": "Ha ocurrido un error al reproducir el vídeo. Comprueba los registros en la configuración.",
|
||||||
"client_error": "Error del cliente",
|
"client_error": "Error del cliente",
|
||||||
"could_not_create_stream_for_chromecast": "No se pudo crear el Steam para Chromecast",
|
"could_not_create_stream_for_chromecast": "No se pudo crear el Steam para Chromecast",
|
||||||
"message_from_server": "Mensaje del servidor: {{message}}",
|
"message_from_server": "Mensaje del servidor: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Okei",
|
"got_it": "Okei",
|
||||||
"connection_failed": "Yhteys epäonnistui",
|
"connection_failed": "Yhteys epäonnistui",
|
||||||
"could_not_connect_to_server": "Yhteyttä palvelimeen ei voitu muodostaa. Tarkista URL-osoite ja verkkoyhteytesi.",
|
"could_not_connect_to_server": "Yhteyttä palvelimeen ei voitu muodostaa. Tarkista URL-osoite ja verkkoyhteytesi.",
|
||||||
"an_unexpected_error_occured": "Odottamaton virhe tapahtui",
|
"an_unexpected_error_occurred": "Odottamaton virhe tapahtui",
|
||||||
"change_server": "Vaihda palvelinta",
|
"change_server": "Vaihda palvelinta",
|
||||||
"invalid_username_or_password": "Virheellinen käyttäjätunnus tai salasana",
|
"invalid_username_or_password": "Virheellinen käyttäjätunnus tai salasana",
|
||||||
"user_does_not_have_permission_to_log_in": "Käyttäjällä ei ole oikeuksia kirjautua sisään",
|
"user_does_not_have_permission_to_log_in": "Käyttäjällä ei ole oikeuksia kirjautua sisään",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Palvelin reagoi liian hitaasti, yritä myöhemmin uudelleen",
|
"server_is_taking_too_long_to_respond_try_again_later": "Palvelin reagoi liian hitaasti, yritä myöhemmin uudelleen",
|
||||||
"server_received_too_many_requests_try_again_later": "Palvelin sai liian monta pyyntöä, yritä myöhemmin uudelleen.",
|
"server_received_too_many_requests_try_again_later": "Palvelin sai liian monta pyyntöä, yritä myöhemmin uudelleen.",
|
||||||
"there_is_a_server_error": "Palvelimessa on virhe",
|
"there_is_a_server_error": "Palvelimessa on virhe",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Odottamaton virhe tapahtui. Syötitkö palvelimen URL-osoitteen oikein?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Odottamaton virhe tapahtui. Syötitkö palvelimen URL-osoitteen oikein?",
|
||||||
"too_old_server_text": "Ei-tuettu Jellyfin-palvelin löydetty",
|
"too_old_server_text": "Ei-tuettu Jellyfin-palvelin löydetty",
|
||||||
"too_old_server_description": "Ole hyvä ja päivitä Jellyfin uusimpaan versioon"
|
"too_old_server_description": "Ole hyvä ja päivitä Jellyfin uusimpaan versioon"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Valtuuta pikayhdistys",
|
"authorize_button": "Valtuuta pikayhdistys",
|
||||||
"enter_the_quick_connect_code": "Syötä nopean yhteyden koodi...",
|
"enter_the_quick_connect_code": "Syötä nopean yhteyden koodi...",
|
||||||
"success": "Onnistui",
|
"success": "Onnistui",
|
||||||
"quick_connect_autorized": "Pikayhdistys valtuutettu",
|
"quick_connect_authorized": "Pikayhdistys valtuutettu",
|
||||||
"error": "Virhe",
|
"error": "Virhe",
|
||||||
"invalid_code": "Virheellinen koodi",
|
"invalid_code": "Virheellinen koodi",
|
||||||
"authorize": "Valtuuta"
|
"authorize": "Valtuuta"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Näytä mukautetut valikkolinkit",
|
"show_custom_menu_links": "Näytä mukautetut valikkolinkit",
|
||||||
"show_large_home_carousel": "Näytä suuri kotikaruselli (beta)",
|
"show_large_home_carousel": "Näytä suuri kotikaruselli (beta)",
|
||||||
"hide_libraries": "Piilota kirjastot",
|
"hide_libraries": "Piilota kirjastot",
|
||||||
"select_liraries_you_want_to_hide": "Valitse kirjastot, jotka haluat piilottaa Kirjasto-välilehdeltä ja etusivun osioista.",
|
"select_libraries_you_want_to_hide": "Valitse kirjastot, jotka haluat piilottaa Kirjasto-välilehdeltä ja etusivun osioista.",
|
||||||
"disable_haptic_feedback": "Poista haptinen palautteet käytöstä",
|
"disable_haptic_feedback": "Poista haptinen palautteet käytöstä",
|
||||||
"default_quality": "Oletuslaatu",
|
"default_quality": "Oletuslaatu",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Laitteen käyttö {{availableSpace}}%",
|
"device_usage": "Laitteen käyttö {{availableSpace}}%",
|
||||||
"size_used": "{{used}} / {{total}} käytössä",
|
"size_used": "{{used}} / {{total}} käytössä",
|
||||||
"delete_all_downloaded_files": "Poista kaikki ladatut tiedostot",
|
"delete_all_downloaded_files": "Poista kaikki ladatut tiedostot",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Virhe",
|
"error": "Virhe",
|
||||||
"failed_to_get_stream_url": "Lähetyksen URL-osoitteen haku epäonnistui",
|
"failed_to_get_stream_url": "Lähetyksen URL-osoitteen haku epäonnistui",
|
||||||
"an_error_occured_while_playing_the_video": "Videon toiston yhteydessä tapahtui virhe. Tarkista lokit asetuksista.",
|
"an_error_occurred_while_playing_the_video": "Videon toiston yhteydessä tapahtui virhe. Tarkista lokit asetuksista.",
|
||||||
"client_error": "Asiakkaan Virhe",
|
"client_error": "Asiakkaan Virhe",
|
||||||
"could_not_create_stream_for_chromecast": "Suoratoistoa ei voitu luoda Chromecastia varten",
|
"could_not_create_stream_for_chromecast": "Suoratoistoa ei voitu luoda Chromecastia varten",
|
||||||
"message_from_server": "Viesti palvelimelta: {{message}}",
|
"message_from_server": "Viesti palvelimelta: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "D'accord",
|
"got_it": "D'accord",
|
||||||
"connection_failed": "La connexion a échoué",
|
"connection_failed": "La connexion a échoué",
|
||||||
"could_not_connect_to_server": "Impossible de se connecter au serveur. Veuillez vérifier l’URL et votre connexion réseau.",
|
"could_not_connect_to_server": "Impossible de se connecter au serveur. Veuillez vérifier l’URL et votre connexion réseau.",
|
||||||
"an_unexpected_error_occured": "Une erreur inattendue s'est produite",
|
"an_unexpected_error_occurred": "Une erreur inattendue s'est produite",
|
||||||
"change_server": "Changer de serveur",
|
"change_server": "Changer de serveur",
|
||||||
"invalid_username_or_password": "Nom d'utilisateur ou mot de passe invalide",
|
"invalid_username_or_password": "Nom d'utilisateur ou mot de passe invalide",
|
||||||
"user_does_not_have_permission_to_log_in": "L'utilisateur n'a pas la permission de se connecter",
|
"user_does_not_have_permission_to_log_in": "L'utilisateur n'a pas la permission de se connecter",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Le serveur prend trop de temps à répondre, réessayez plus tard",
|
"server_is_taking_too_long_to_respond_try_again_later": "Le serveur prend trop de temps à répondre, réessayez plus tard",
|
||||||
"server_received_too_many_requests_try_again_later": "Le serveur a reçu trop de demandes, réessayez plus tard.",
|
"server_received_too_many_requests_try_again_later": "Le serveur a reçu trop de demandes, réessayez plus tard.",
|
||||||
"there_is_a_server_error": "Il y a une erreur de serveur",
|
"there_is_a_server_error": "Il y a une erreur de serveur",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Une erreur inattendue s’est produite. Avez-vous correctement saisi l’URL du serveur ?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Une erreur inattendue s’est produite. Avez-vous correctement saisi l’URL du serveur ?",
|
||||||
"too_old_server_text": "Serveur Jellyfin non pris en charge découvert",
|
"too_old_server_text": "Serveur Jellyfin non pris en charge découvert",
|
||||||
"too_old_server_description": "Veuillez mettre à jour Jellyfin vers la dernière version"
|
"too_old_server_description": "Veuillez mettre à jour Jellyfin vers la dernière version"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Autoriser une connexion rapide",
|
"authorize_button": "Autoriser une connexion rapide",
|
||||||
"enter_the_quick_connect_code": "Entrez le code de connexion rapide...",
|
"enter_the_quick_connect_code": "Entrez le code de connexion rapide...",
|
||||||
"success": "Succès",
|
"success": "Succès",
|
||||||
"quick_connect_autorized": "Connexion rapide autorisée",
|
"quick_connect_authorized": "Connexion rapide autorisée",
|
||||||
"error": "Erreur",
|
"error": "Erreur",
|
||||||
"invalid_code": "Code invalide",
|
"invalid_code": "Code invalide",
|
||||||
"authorize": "Autoriser"
|
"authorize": "Autoriser"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Afficher les liens personnalisés",
|
"show_custom_menu_links": "Afficher les liens personnalisés",
|
||||||
"show_large_home_carousel": "Afficher le grand carrousel d’accueil (bêta)",
|
"show_large_home_carousel": "Afficher le grand carrousel d’accueil (bêta)",
|
||||||
"hide_libraries": "Masquer les bibliothèques",
|
"hide_libraries": "Masquer les bibliothèques",
|
||||||
"select_liraries_you_want_to_hide": "Sélectionnez les bibliothèques que vous souhaitez masquer dans l'onglet Bibliothèque et les sections de la page d'accueil.",
|
"select_libraries_you_want_to_hide": "Sélectionnez les bibliothèques que vous souhaitez masquer dans l'onglet Bibliothèque et les sections de la page d'accueil.",
|
||||||
"disable_haptic_feedback": "Désactiver le retour haptique",
|
"disable_haptic_feedback": "Désactiver le retour haptique",
|
||||||
"default_quality": "Qualité par défaut",
|
"default_quality": "Qualité par défaut",
|
||||||
"default_playback_speed": "Vitesse de lecture par défaut",
|
"default_playback_speed": "Vitesse de lecture par défaut",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Appareil {{availableSpace}}%",
|
"device_usage": "Appareil {{availableSpace}}%",
|
||||||
"size_used": "{{used}} de {{total}} utilisés",
|
"size_used": "{{used}} de {{total}} utilisés",
|
||||||
"delete_all_downloaded_files": "Supprimer tous les fichiers téléchargés",
|
"delete_all_downloaded_files": "Supprimer tous les fichiers téléchargés",
|
||||||
|
"delete_all_downloaded_files_confirm": "Supprimer tous les fichiers téléchargés ?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Êtes-vous sûr de vouloir supprimer tous les fichiers téléchargés ? Cette action est irréversible.",
|
||||||
"music_cache_title": "Mise en cache de la musique",
|
"music_cache_title": "Mise en cache de la musique",
|
||||||
"music_cache_description": "Mettez automatiquement en cache les chansons au fur et à mesure que vous écoutez pour une lecture plus fluide et une prise en charge hors ligne",
|
"music_cache_description": "Mettez automatiquement en cache les chansons au fur et à mesure que vous écoutez pour une lecture plus fluide et une prise en charge hors ligne",
|
||||||
"clear_music_cache": "Vider le cache de la musique",
|
"clear_music_cache": "Vider le cache de la musique",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "Lecteur MPV",
|
"mpv_player_title": "Lecteur MPV",
|
||||||
"error": "Erreur",
|
"error": "Erreur",
|
||||||
"failed_to_get_stream_url": "Échec de l'obtention de l'URL du flux",
|
"failed_to_get_stream_url": "Échec de l'obtention de l'URL du flux",
|
||||||
"an_error_occured_while_playing_the_video": "Une erreur s’est produite lors de la lecture de la vidéo. Vérifiez les journaux dans les paramètres.",
|
"an_error_occurred_while_playing_the_video": "Une erreur s’est produite lors de la lecture de la vidéo. Vérifiez les journaux dans les paramètres.",
|
||||||
"client_error": "Erreur du client",
|
"client_error": "Erreur du client",
|
||||||
"could_not_create_stream_for_chromecast": "Impossible de créer un flux sur la Chromecast",
|
"could_not_create_stream_for_chromecast": "Impossible de créer un flux sur la Chromecast",
|
||||||
"message_from_server": "Message du serveur : {{message}}",
|
"message_from_server": "Message du serveur : {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "קיבלתי",
|
"got_it": "קיבלתי",
|
||||||
"connection_failed": "ההתחברות נכשלה",
|
"connection_failed": "ההתחברות נכשלה",
|
||||||
"could_not_connect_to_server": "לא היה ניתן להתחבר לשרת. אנא בדוק את הקישור ואת חיבור הרשת שלך.",
|
"could_not_connect_to_server": "לא היה ניתן להתחבר לשרת. אנא בדוק את הקישור ואת חיבור הרשת שלך.",
|
||||||
"an_unexpected_error_occured": "קרתה שגיאה לא צפויה",
|
"an_unexpected_error_occurred": "קרתה שגיאה לא צפויה",
|
||||||
"change_server": "החלף שרת",
|
"change_server": "החלף שרת",
|
||||||
"invalid_username_or_password": "שם משתמש או סיסמה שגויים",
|
"invalid_username_or_password": "שם משתמש או סיסמה שגויים",
|
||||||
"user_does_not_have_permission_to_log_in": "לחשבון זה אין גישה להתחבר",
|
"user_does_not_have_permission_to_log_in": "לחשבון זה אין גישה להתחבר",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "השרת לוקח יותר מדי זמן כדי להגיב, נסה שוב מאוחר יותר",
|
"server_is_taking_too_long_to_respond_try_again_later": "השרת לוקח יותר מדי זמן כדי להגיב, נסה שוב מאוחר יותר",
|
||||||
"server_received_too_many_requests_try_again_later": "השרת קיבל יותר מדי קריאות, נסה שוב מאוחר יותר",
|
"server_received_too_many_requests_try_again_later": "השרת קיבל יותר מדי קריאות, נסה שוב מאוחר יותר",
|
||||||
"there_is_a_server_error": "קרתה שגיאה בצד השרת",
|
"there_is_a_server_error": "קרתה שגיאה בצד השרת",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "קרתה שגיאה לא צפויה. האם כתובת השרת שהוקלדה נכונה?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "קרתה שגיאה לא צפויה. האם כתובת השרת שהוקלדה נכונה?",
|
||||||
"too_old_server_text": "נמצא שרת Jellyfin שלא נתמך",
|
"too_old_server_text": "נמצא שרת Jellyfin שלא נתמך",
|
||||||
"too_old_server_description": "אנא עדכן את גרסת ה-Jellyfin למעודכנת ביותר"
|
"too_old_server_description": "אנא עדכן את גרסת ה-Jellyfin למעודכנת ביותר"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "אשר התחברות מהירה",
|
"authorize_button": "אשר התחברות מהירה",
|
||||||
"enter_the_quick_connect_code": "הקלד את קוד ההתחברות המהירה...",
|
"enter_the_quick_connect_code": "הקלד את קוד ההתחברות המהירה...",
|
||||||
"success": "הצלחה",
|
"success": "הצלחה",
|
||||||
"quick_connect_autorized": "חיבור מהיר אושר",
|
"quick_connect_authorized": "חיבור מהיר אושר",
|
||||||
"error": "שגיאה",
|
"error": "שגיאה",
|
||||||
"invalid_code": "קוד שגוי",
|
"invalid_code": "קוד שגוי",
|
||||||
"authorize": "אשר"
|
"authorize": "אשר"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "הצג קישורים לתפריטים מותאמים אישית",
|
"show_custom_menu_links": "הצג קישורים לתפריטים מותאמים אישית",
|
||||||
"show_large_home_carousel": "הצג קרוסלה גדולה במסך הבית (בטא)",
|
"show_large_home_carousel": "הצג קרוסלה גדולה במסך הבית (בטא)",
|
||||||
"hide_libraries": "הסתר ספריות",
|
"hide_libraries": "הסתר ספריות",
|
||||||
"select_liraries_you_want_to_hide": "בחר את הספריות שתרצה להסתיר ממסך הספריות וגם ממסך הבית.",
|
"select_libraries_you_want_to_hide": "בחר את הספריות שתרצה להסתיר ממסך הספריות וגם ממסך הבית.",
|
||||||
"disable_haptic_feedback": "בטל משוב רטט",
|
"disable_haptic_feedback": "בטל משוב רטט",
|
||||||
"default_quality": "איכות ברירת מחדל",
|
"default_quality": "איכות ברירת מחדל",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "מכשיר {{availableSpace}}%",
|
"device_usage": "מכשיר {{availableSpace}}%",
|
||||||
"size_used": "השתמשת ב-{{used}} מתוך {{total}}",
|
"size_used": "השתמשת ב-{{used}} מתוך {{total}}",
|
||||||
"delete_all_downloaded_files": "מחק את כל הקבצים שהורדו",
|
"delete_all_downloaded_files": "מחק את כל הקבצים שהורדו",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "שגיאה",
|
"error": "שגיאה",
|
||||||
"failed_to_get_stream_url": "נכשל בהשגת קישור הזרם",
|
"failed_to_get_stream_url": "נכשל בהשגת קישור הזרם",
|
||||||
"an_error_occured_while_playing_the_video": "קרתה תקלה במהלך הניגון של הקובץ. בדוק את הלוגים בהגדרות.",
|
"an_error_occurred_while_playing_the_video": "קרתה תקלה במהלך הניגון של הקובץ. בדוק את הלוגים בהגדרות.",
|
||||||
"client_error": "שגיאת לקוח",
|
"client_error": "שגיאת לקוח",
|
||||||
"could_not_create_stream_for_chromecast": "נכשל ביצירת זרם עבור Chromecast",
|
"could_not_create_stream_for_chromecast": "נכשל ביצירת זרם עבור Chromecast",
|
||||||
"message_from_server": "הודעה מהשרת: {{message}}",
|
"message_from_server": "הודעה מהשרת: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Értettem",
|
"got_it": "Értettem",
|
||||||
"connection_failed": "Kapcsolódás Sikertelen",
|
"connection_failed": "Kapcsolódás Sikertelen",
|
||||||
"could_not_connect_to_server": "Nem sikerült csatlakozni a szerverhez. Kérjük, ellenőrizd az URL-t és a hálózati kapcsolatot.",
|
"could_not_connect_to_server": "Nem sikerült csatlakozni a szerverhez. Kérjük, ellenőrizd az URL-t és a hálózati kapcsolatot.",
|
||||||
"an_unexpected_error_occured": "Váratlan Hiba Történt",
|
"an_unexpected_error_occurred": "Váratlan Hiba Történt",
|
||||||
"change_server": "Szerverváltás",
|
"change_server": "Szerverváltás",
|
||||||
"invalid_username_or_password": "Érvénytelen Felhasználónév vagy Jelszó",
|
"invalid_username_or_password": "Érvénytelen Felhasználónév vagy Jelszó",
|
||||||
"user_does_not_have_permission_to_log_in": "A felhasználónak nincs jogosultsága a bejelentkezéshez",
|
"user_does_not_have_permission_to_log_in": "A felhasználónak nincs jogosultsága a bejelentkezéshez",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "A szerver túl sokáig válaszol, próbáld újra később",
|
"server_is_taking_too_long_to_respond_try_again_later": "A szerver túl sokáig válaszol, próbáld újra később",
|
||||||
"server_received_too_many_requests_try_again_later": "A szerver túl sok kérést kapott, próbáld újra később.",
|
"server_received_too_many_requests_try_again_later": "A szerver túl sok kérést kapott, próbáld újra később.",
|
||||||
"there_is_a_server_error": "Szerverhiba történt",
|
"there_is_a_server_error": "Szerverhiba történt",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Váratlan hiba történt. Helyesen adtad meg a szerver URL-jét?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Váratlan hiba történt. Helyesen adtad meg a szerver URL-jét?",
|
||||||
"too_old_server_text": "Nem Támogatott Jellyfin-szerver",
|
"too_old_server_text": "Nem Támogatott Jellyfin-szerver",
|
||||||
"too_old_server_description": "Frissítsd a Jellyfint a legújabb verzióra"
|
"too_old_server_description": "Frissítsd a Jellyfint a legújabb verzióra"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Gyorscsatlakozás Engedélyezése",
|
"authorize_button": "Gyorscsatlakozás Engedélyezése",
|
||||||
"enter_the_quick_connect_code": "Add meg a gyors csatlakozási kódot...",
|
"enter_the_quick_connect_code": "Add meg a gyors csatlakozási kódot...",
|
||||||
"success": "Siker",
|
"success": "Siker",
|
||||||
"quick_connect_autorized": "Gyorscsatlakozás Engedélyezve",
|
"quick_connect_authorized": "Gyorscsatlakozás Engedélyezve",
|
||||||
"error": "Hiba",
|
"error": "Hiba",
|
||||||
"invalid_code": "Érvénytelen Kód",
|
"invalid_code": "Érvénytelen Kód",
|
||||||
"authorize": "Engedélyezés"
|
"authorize": "Engedélyezés"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Egyéni Menülinkek Megjelenítése",
|
"show_custom_menu_links": "Egyéni Menülinkek Megjelenítése",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
"hide_libraries": "Könyvtárak Elrejtése",
|
"hide_libraries": "Könyvtárak Elrejtése",
|
||||||
"select_liraries_you_want_to_hide": "Válaszd ki azokat a könyvtárakat, amelyeket el szeretnél rejteni a Könyvtár fülön és a kezdőlapon.",
|
"select_libraries_you_want_to_hide": "Válaszd ki azokat a könyvtárakat, amelyeket el szeretnél rejteni a Könyvtár fülön és a kezdőlapon.",
|
||||||
"disable_haptic_feedback": "Haptikus Visszajelzés Letiltása",
|
"disable_haptic_feedback": "Haptikus Visszajelzés Letiltása",
|
||||||
"default_quality": "Alapértelmezett Minőség",
|
"default_quality": "Alapértelmezett Minőség",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Eszköz {{availableSpace}}%",
|
"device_usage": "Eszköz {{availableSpace}}%",
|
||||||
"size_used": "{{used}} / {{total}} Használatban",
|
"size_used": "{{used}} / {{total}} Használatban",
|
||||||
"delete_all_downloaded_files": "Minden Letöltött Fájl Törlése",
|
"delete_all_downloaded_files": "Minden Letöltött Fájl Törlése",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Hiba",
|
"error": "Hiba",
|
||||||
"failed_to_get_stream_url": "Nem sikerült lekérni a stream URL-t",
|
"failed_to_get_stream_url": "Nem sikerült lekérni a stream URL-t",
|
||||||
"an_error_occured_while_playing_the_video": "Hiba történt a videó lejátszása közben. Ellenőrizd a naplókat a beállításokban.",
|
"an_error_occurred_while_playing_the_video": "Hiba történt a videó lejátszása közben. Ellenőrizd a naplókat a beállításokban.",
|
||||||
"client_error": "Kliens Hiba",
|
"client_error": "Kliens Hiba",
|
||||||
"could_not_create_stream_for_chromecast": "A Chromecast stream létrehozása sikertelen volt",
|
"could_not_create_stream_for_chromecast": "A Chromecast stream létrehozása sikertelen volt",
|
||||||
"message_from_server": "Üzenet a szervertől: {{message}}",
|
"message_from_server": "Üzenet a szervertől: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Capito",
|
"got_it": "Capito",
|
||||||
"connection_failed": "Connessione fallita",
|
"connection_failed": "Connessione fallita",
|
||||||
"could_not_connect_to_server": "Impossibile connettersi al server. Controllare l'URL e la connessione di rete.",
|
"could_not_connect_to_server": "Impossibile connettersi al server. Controllare l'URL e la connessione di rete.",
|
||||||
"an_unexpected_error_occured": "Si è verificato un errore inaspettato",
|
"an_unexpected_error_occurred": "Si è verificato un errore inaspettato",
|
||||||
"change_server": "Cambiare il server",
|
"change_server": "Cambiare il server",
|
||||||
"invalid_username_or_password": "Nome utente o password non validi",
|
"invalid_username_or_password": "Nome utente o password non validi",
|
||||||
"user_does_not_have_permission_to_log_in": "L'utente non ha il permesso di accedere",
|
"user_does_not_have_permission_to_log_in": "L'utente non ha il permesso di accedere",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Il server sta impiegando troppo tempo per rispondere, riprovare più tardi",
|
"server_is_taking_too_long_to_respond_try_again_later": "Il server sta impiegando troppo tempo per rispondere, riprovare più tardi",
|
||||||
"server_received_too_many_requests_try_again_later": "Il server ha ricevuto troppe richieste, riprovare più tardi.",
|
"server_received_too_many_requests_try_again_later": "Il server ha ricevuto troppe richieste, riprovare più tardi.",
|
||||||
"there_is_a_server_error": "Si è verificato un errore del server",
|
"there_is_a_server_error": "Si è verificato un errore del server",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Si è verificato un errore imprevisto. L'URL del server è stato inserito correttamente?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Si è verificato un errore imprevisto. L'URL del server è stato inserito correttamente?",
|
||||||
"too_old_server_text": "Scoperto Server Jellyfin non supportato",
|
"too_old_server_text": "Scoperto Server Jellyfin non supportato",
|
||||||
"too_old_server_description": "Aggiorna Jellyfin all'ultima versione"
|
"too_old_server_description": "Aggiorna Jellyfin all'ultima versione"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Autorizza Connessione Rapida",
|
"authorize_button": "Autorizza Connessione Rapida",
|
||||||
"enter_the_quick_connect_code": "Inserisci il codice per la Connessione Rapida...",
|
"enter_the_quick_connect_code": "Inserisci il codice per la Connessione Rapida...",
|
||||||
"success": "Successo",
|
"success": "Successo",
|
||||||
"quick_connect_autorized": "Connessione Rapida autorizzata",
|
"quick_connect_authorized": "Connessione Rapida autorizzata",
|
||||||
"error": "Errore",
|
"error": "Errore",
|
||||||
"invalid_code": "Codice invalido",
|
"invalid_code": "Codice invalido",
|
||||||
"authorize": "Autorizza"
|
"authorize": "Autorizza"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Mostra i link del menu personalizzato",
|
"show_custom_menu_links": "Mostra i link del menu personalizzato",
|
||||||
"show_large_home_carousel": "Mostra Carosello Grande nella Home (beta)",
|
"show_large_home_carousel": "Mostra Carosello Grande nella Home (beta)",
|
||||||
"hide_libraries": "Nascondi Librerie",
|
"hide_libraries": "Nascondi Librerie",
|
||||||
"select_liraries_you_want_to_hide": "Selezionate le librerie che volete nascondere dalla scheda Libreria e dalle sezioni della pagina iniziale.",
|
"select_libraries_you_want_to_hide": "Selezionate le librerie che volete nascondere dalla scheda Libreria e dalle sezioni della pagina iniziale.",
|
||||||
"disable_haptic_feedback": "Disabilita il feedback aptico",
|
"disable_haptic_feedback": "Disabilita il feedback aptico",
|
||||||
"default_quality": "Qualità predefinita",
|
"default_quality": "Qualità predefinita",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Dispositivo {{availableSpace}}%",
|
"device_usage": "Dispositivo {{availableSpace}}%",
|
||||||
"size_used": "{{used}} di {{total}} usato",
|
"size_used": "{{used}} di {{total}} usato",
|
||||||
"delete_all_downloaded_files": "Cancella Tutti i File Scaricati",
|
"delete_all_downloaded_files": "Cancella Tutti i File Scaricati",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Precarica automaticamente i brani mentre ascolti per una riproduzione più fluida e il supporto offline",
|
"music_cache_description": "Precarica automaticamente i brani mentre ascolti per una riproduzione più fluida e il supporto offline",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Errore",
|
"error": "Errore",
|
||||||
"failed_to_get_stream_url": "Impossibile ottenere l'URL dello stream",
|
"failed_to_get_stream_url": "Impossibile ottenere l'URL dello stream",
|
||||||
"an_error_occured_while_playing_the_video": "Si è verificato un errore durante la riproduzione del video. Controllare i log nelle impostazioni.",
|
"an_error_occurred_while_playing_the_video": "Si è verificato un errore durante la riproduzione del video. Controllare i log nelle impostazioni.",
|
||||||
"client_error": "Errore del client",
|
"client_error": "Errore del client",
|
||||||
"could_not_create_stream_for_chromecast": "Impossibile creare uno stream per Chromecast",
|
"could_not_create_stream_for_chromecast": "Impossibile creare uno stream per Chromecast",
|
||||||
"message_from_server": "Messaggio dal server",
|
"message_from_server": "Messaggio dal server",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "了解",
|
"got_it": "了解",
|
||||||
"connection_failed": "接続に失敗しました",
|
"connection_failed": "接続に失敗しました",
|
||||||
"could_not_connect_to_server": "サーバーに接続できませんでした。URLとネットワーク接続を確認してください。",
|
"could_not_connect_to_server": "サーバーに接続できませんでした。URLとネットワーク接続を確認してください。",
|
||||||
"an_unexpected_error_occured": "予期しないエラーが発生しました",
|
"an_unexpected_error_occurred": "予期しないエラーが発生しました",
|
||||||
"change_server": "サーバーの変更",
|
"change_server": "サーバーの変更",
|
||||||
"invalid_username_or_password": "ユーザー名またはパスワードが無効です",
|
"invalid_username_or_password": "ユーザー名またはパスワードが無効です",
|
||||||
"user_does_not_have_permission_to_log_in": "ユーザーにログイン権限がありません",
|
"user_does_not_have_permission_to_log_in": "ユーザーにログイン権限がありません",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "サーバーの応答に時間がかかりすぎています。しばらくしてからもう一度お試しください。",
|
"server_is_taking_too_long_to_respond_try_again_later": "サーバーの応答に時間がかかりすぎています。しばらくしてからもう一度お試しください。",
|
||||||
"server_received_too_many_requests_try_again_later": "サーバーにリクエストが多すぎます。後でもう一度お試しください。",
|
"server_received_too_many_requests_try_again_later": "サーバーにリクエストが多すぎます。後でもう一度お試しください。",
|
||||||
"there_is_a_server_error": "サーバーエラーが発生しました",
|
"there_is_a_server_error": "サーバーエラーが発生しました",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "予期しないエラーが発生しました。サーバーのURLを正しく入力しましたか?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "予期しないエラーが発生しました。サーバーのURLを正しく入力しましたか?",
|
||||||
"too_old_server_text": "サポートされていないJellyfinサーバー発見",
|
"too_old_server_text": "サポートされていないJellyfinサーバー発見",
|
||||||
"too_old_server_description": "Jellyfinを最新バージョンにアップデートしてください"
|
"too_old_server_description": "Jellyfinを最新バージョンにアップデートしてください"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "クイックコネクトを承認する",
|
"authorize_button": "クイックコネクトを承認する",
|
||||||
"enter_the_quick_connect_code": "クイックコネクトコードを入力...",
|
"enter_the_quick_connect_code": "クイックコネクトコードを入力...",
|
||||||
"success": "成功しました",
|
"success": "成功しました",
|
||||||
"quick_connect_autorized": "クイックコネクトが承認されました",
|
"quick_connect_authorized": "クイックコネクトが承認されました",
|
||||||
"error": "エラー",
|
"error": "エラー",
|
||||||
"invalid_code": "無効なコードです",
|
"invalid_code": "無効なコードです",
|
||||||
"authorize": "承認"
|
"authorize": "承認"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "カスタムメニューのリンクを表示",
|
"show_custom_menu_links": "カスタムメニューのリンクを表示",
|
||||||
"show_large_home_carousel": "大きなヒーロー(Beta)",
|
"show_large_home_carousel": "大きなヒーロー(Beta)",
|
||||||
"hide_libraries": "ライブラリを非表示",
|
"hide_libraries": "ライブラリを非表示",
|
||||||
"select_liraries_you_want_to_hide": "ライブラリタブとホームページセクションから非表示にするライブラリを選択します。",
|
"select_libraries_you_want_to_hide": "ライブラリタブとホームページセクションから非表示にするライブラリを選択します。",
|
||||||
"disable_haptic_feedback": "触覚フィードバックを無効にする",
|
"disable_haptic_feedback": "触覚フィードバックを無効にする",
|
||||||
"default_quality": "デフォルトの品質",
|
"default_quality": "デフォルトの品質",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "デバイス {{availableSpace}}%",
|
"device_usage": "デバイス {{availableSpace}}%",
|
||||||
"size_used": "{{used}} / {{total}} 使用済み",
|
"size_used": "{{used}} / {{total}} 使用済み",
|
||||||
"delete_all_downloaded_files": "すべてのダウンロードファイルを削除",
|
"delete_all_downloaded_files": "すべてのダウンロードファイルを削除",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "エラー",
|
"error": "エラー",
|
||||||
"failed_to_get_stream_url": "ストリームURLを取得できませんでした",
|
"failed_to_get_stream_url": "ストリームURLを取得できませんでした",
|
||||||
"an_error_occured_while_playing_the_video": "動画の再生中にエラーが発生しました。設定でログを確認してください。",
|
"an_error_occurred_while_playing_the_video": "動画の再生中にエラーが発生しました。設定でログを確認してください。",
|
||||||
"client_error": "クライアントエラー",
|
"client_error": "クライアントエラー",
|
||||||
"could_not_create_stream_for_chromecast": "Chromecastのストリームを作成できませんでした",
|
"could_not_create_stream_for_chromecast": "Chromecastのストリームを作成できませんでした",
|
||||||
"message_from_server": "サーバーからのメッセージ",
|
"message_from_server": "サーバーからのメッセージ",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "성공",
|
"got_it": "성공",
|
||||||
"connection_failed": "연결 실패",
|
"connection_failed": "연결 실패",
|
||||||
"could_not_connect_to_server": "서버에 연결되지 않았습니다. URL과 네트워크 상태를 확인하세요.",
|
"could_not_connect_to_server": "서버에 연결되지 않았습니다. URL과 네트워크 상태를 확인하세요.",
|
||||||
"an_unexpected_error_occured": "예기치 않은 오류가 발생했습니다",
|
"an_unexpected_error_occurred": "예기치 않은 오류가 발생했습니다",
|
||||||
"change_server": "서버 변경",
|
"change_server": "서버 변경",
|
||||||
"invalid_username_or_password": "잘못된 아이디 혹은 비밀번호입니다",
|
"invalid_username_or_password": "잘못된 아이디 혹은 비밀번호입니다",
|
||||||
"user_does_not_have_permission_to_log_in": "로그인 하기 위한 권한이 없습니다",
|
"user_does_not_have_permission_to_log_in": "로그인 하기 위한 권한이 없습니다",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "서버 응답이 너무 느립니다. 나중에 다시 시도하세요",
|
"server_is_taking_too_long_to_respond_try_again_later": "서버 응답이 너무 느립니다. 나중에 다시 시도하세요",
|
||||||
"server_received_too_many_requests_try_again_later": "서버가 너무 많은 요청을 받았습니다. 나중에 다시 시도하세요.",
|
"server_received_too_many_requests_try_again_later": "서버가 너무 많은 요청을 받았습니다. 나중에 다시 시도하세요.",
|
||||||
"there_is_a_server_error": "서버 에러",
|
"there_is_a_server_error": "서버 에러",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "예기치 않은 오류가 발생했습니다. 서버 URL을 올바르게 입력하셨습니까?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "예기치 않은 오류가 발생했습니다. 서버 URL을 올바르게 입력하셨습니까?",
|
||||||
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
||||||
"too_old_server_description": "Please update Jellyfin to the latest version"
|
"too_old_server_description": "Please update Jellyfin to the latest version"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "퀵 커넥트 승인",
|
"authorize_button": "퀵 커넥트 승인",
|
||||||
"enter_the_quick_connect_code": "퀵 커넥트 코드 입력...",
|
"enter_the_quick_connect_code": "퀵 커넥트 코드 입력...",
|
||||||
"success": "성공",
|
"success": "성공",
|
||||||
"quick_connect_autorized": "퀵 커넥트 승인됨",
|
"quick_connect_authorized": "퀵 커넥트 승인됨",
|
||||||
"error": "오류",
|
"error": "오류",
|
||||||
"invalid_code": "유효하지 않은 코드",
|
"invalid_code": "유효하지 않은 코드",
|
||||||
"authorize": "승인"
|
"authorize": "승인"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "사용자 지정 메뉴 링크 표시",
|
"show_custom_menu_links": "사용자 지정 메뉴 링크 표시",
|
||||||
"show_large_home_carousel": "대형 홈 슬라이드 배너 표시 (베타)",
|
"show_large_home_carousel": "대형 홈 슬라이드 배너 표시 (베타)",
|
||||||
"hide_libraries": "라이브러리 숨기기",
|
"hide_libraries": "라이브러리 숨기기",
|
||||||
"select_liraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
|
"select_libraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
|
||||||
"disable_haptic_feedback": "Disable Haptic Feedback",
|
"disable_haptic_feedback": "Disable Haptic Feedback",
|
||||||
"default_quality": "Default Quality",
|
"default_quality": "Default Quality",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "디바이스 {{availableSpace}}%",
|
"device_usage": "디바이스 {{availableSpace}}%",
|
||||||
"size_used": "{{used}} of {{total}} Used",
|
"size_used": "{{used}} of {{total}} Used",
|
||||||
"delete_all_downloaded_files": "Delete All Downloaded Files",
|
"delete_all_downloaded_files": "Delete All Downloaded Files",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"failed_to_get_stream_url": "Failed to get the stream URL",
|
"failed_to_get_stream_url": "Failed to get the stream URL",
|
||||||
"an_error_occured_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
|
"an_error_occurred_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
|
||||||
"client_error": "Client Error",
|
"client_error": "Client Error",
|
||||||
"could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast",
|
"could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast",
|
||||||
"message_from_server": "Message from Server: {{message}}",
|
"message_from_server": "Message from Server: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Begrepen",
|
"got_it": "Begrepen",
|
||||||
"connection_failed": "Verbinding mislukt",
|
"connection_failed": "Verbinding mislukt",
|
||||||
"could_not_connect_to_server": "Kon niet verbinden met de server. Controleer de URL en je netwerkverbinding.",
|
"could_not_connect_to_server": "Kon niet verbinden met de server. Controleer de URL en je netwerkverbinding.",
|
||||||
"an_unexpected_error_occured": "Er is een onverwachte fout opgetreden",
|
"an_unexpected_error_occurred": "Er is een onverwachte fout opgetreden",
|
||||||
"change_server": "Server wijzigen",
|
"change_server": "Server wijzigen",
|
||||||
"invalid_username_or_password": "Onjuiste gebruikersnaam of wachtwoord",
|
"invalid_username_or_password": "Onjuiste gebruikersnaam of wachtwoord",
|
||||||
"user_does_not_have_permission_to_log_in": "Gebruiker heeft geen rechten om aan te melden",
|
"user_does_not_have_permission_to_log_in": "Gebruiker heeft geen rechten om aan te melden",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "De server doet er te lang over om te antwoorden, probeer later opnieuw",
|
"server_is_taking_too_long_to_respond_try_again_later": "De server doet er te lang over om te antwoorden, probeer later opnieuw",
|
||||||
"server_received_too_many_requests_try_again_later": "De server heeft te veel aanvragen ontvangen, probeer later opnieuw",
|
"server_received_too_many_requests_try_again_later": "De server heeft te veel aanvragen ontvangen, probeer later opnieuw",
|
||||||
"there_is_a_server_error": "Er is een serverfout",
|
"there_is_a_server_error": "Er is een serverfout",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Er is een onverwachte fout opgetreden. Heb je de server URL correct ingegeven?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Er is een onverwachte fout opgetreden. Heb je de server URL correct ingegeven?",
|
||||||
"too_old_server_text": "Niet-ondersteunde Jellyfin Server Ontdekt",
|
"too_old_server_text": "Niet-ondersteunde Jellyfin Server Ontdekt",
|
||||||
"too_old_server_description": "Werk Jellyfin bij naar de laatste versie"
|
"too_old_server_description": "Werk Jellyfin bij naar de laatste versie"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Snel Verbinden toestaan",
|
"authorize_button": "Snel Verbinden toestaan",
|
||||||
"enter_the_quick_connect_code": "Vul de Snel Verbinden code in...",
|
"enter_the_quick_connect_code": "Vul de Snel Verbinden code in...",
|
||||||
"success": "Succes",
|
"success": "Succes",
|
||||||
"quick_connect_autorized": "Snel Verbinden toegestaan",
|
"quick_connect_authorized": "Snel Verbinden toegestaan",
|
||||||
"error": "Fout",
|
"error": "Fout",
|
||||||
"invalid_code": "Ongeldige code",
|
"invalid_code": "Ongeldige code",
|
||||||
"authorize": "Toestaan"
|
"authorize": "Toestaan"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Aangepaste menulinks tonen",
|
"show_custom_menu_links": "Aangepaste menulinks tonen",
|
||||||
"show_large_home_carousel": "Toon grote carrousel op startpagina (bèta)",
|
"show_large_home_carousel": "Toon grote carrousel op startpagina (bèta)",
|
||||||
"hide_libraries": "Verberg Bibliotheken",
|
"hide_libraries": "Verberg Bibliotheken",
|
||||||
"select_liraries_you_want_to_hide": "Selecteer de bibliotheken die je wil verbergen van de Bibliotheektab en hoofdpagina onderdelen.",
|
"select_libraries_you_want_to_hide": "Selecteer de bibliotheken die je wil verbergen van de Bibliotheektab en hoofdpagina onderdelen.",
|
||||||
"disable_haptic_feedback": "Haptische feedback uitschakelen",
|
"disable_haptic_feedback": "Haptische feedback uitschakelen",
|
||||||
"default_quality": "Standaard kwaliteit",
|
"default_quality": "Standaard kwaliteit",
|
||||||
"default_playback_speed": "Standaard Afspeelsnelheid",
|
"default_playback_speed": "Standaard Afspeelsnelheid",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Toestel {{availableSpace}}%",
|
"device_usage": "Toestel {{availableSpace}}%",
|
||||||
"size_used": "{{used}} van {{total}} gebruikt",
|
"size_used": "{{used}} van {{total}} gebruikt",
|
||||||
"delete_all_downloaded_files": "Verwijder alle gedownloade bestanden",
|
"delete_all_downloaded_files": "Verwijder alle gedownloade bestanden",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Fout",
|
"error": "Fout",
|
||||||
"failed_to_get_stream_url": "De stream-URL kon niet worden verkregen",
|
"failed_to_get_stream_url": "De stream-URL kon niet worden verkregen",
|
||||||
"an_error_occured_while_playing_the_video": "Er is een fout opgetreden tijdens het afspelen van de video. Controleer de logs in de instellingen.",
|
"an_error_occurred_while_playing_the_video": "Er is een fout opgetreden tijdens het afspelen van de video. Controleer de logs in de instellingen.",
|
||||||
"client_error": "Fout van de client",
|
"client_error": "Fout van de client",
|
||||||
"could_not_create_stream_for_chromecast": "Kon geen stream maken voor Chromecast",
|
"could_not_create_stream_for_chromecast": "Kon geen stream maken voor Chromecast",
|
||||||
"message_from_server": "Bericht van de server",
|
"message_from_server": "Bericht van de server",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Har det",
|
"got_it": "Har det",
|
||||||
"connection_failed": "Tilkobling mislyktes",
|
"connection_failed": "Tilkobling mislyktes",
|
||||||
"could_not_connect_to_server": "Kunne ikke koble til serveren. Kontroller nettadressen og nettverkstilkoblingen.",
|
"could_not_connect_to_server": "Kunne ikke koble til serveren. Kontroller nettadressen og nettverkstilkoblingen.",
|
||||||
"an_unexpected_error_occured": "En uventet feil oppstod",
|
"an_unexpected_error_occurred": "En uventet feil oppstod",
|
||||||
"change_server": "Endre server",
|
"change_server": "Endre server",
|
||||||
"invalid_username_or_password": "Ugyldig brukernavn eller passord",
|
"invalid_username_or_password": "Ugyldig brukernavn eller passord",
|
||||||
"user_does_not_have_permission_to_log_in": "Brukeren har ikke tillatelse til å logge inn",
|
"user_does_not_have_permission_to_log_in": "Brukeren har ikke tillatelse til å logge inn",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Serveren tar for lang tid å svare, prøv igjen senere",
|
"server_is_taking_too_long_to_respond_try_again_later": "Serveren tar for lang tid å svare, prøv igjen senere",
|
||||||
"server_received_too_many_requests_try_again_later": "Serveren mottok for mange forespørsler, prøv igjen senere.",
|
"server_received_too_many_requests_try_again_later": "Serveren mottok for mange forespørsler, prøv igjen senere.",
|
||||||
"there_is_a_server_error": "Det er en serverfeil",
|
"there_is_a_server_error": "Det er en serverfeil",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Det oppstod en uventet feil. Sendte du inn URLen til serveren riktig?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Det oppstod en uventet feil. Sendte du inn URLen til serveren riktig?",
|
||||||
"too_old_server_text": "Ustøttet Jellyfin Server oppdaget",
|
"too_old_server_text": "Ustøttet Jellyfin Server oppdaget",
|
||||||
"too_old_server_description": "Vennligst oppdater Jellyfin til nyeste versjon"
|
"too_old_server_description": "Vennligst oppdater Jellyfin til nyeste versjon"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Autoriser rask tilkobling",
|
"authorize_button": "Autoriser rask tilkobling",
|
||||||
"enter_the_quick_connect_code": "Skriv inn hurtig-tilkobling kode...",
|
"enter_the_quick_connect_code": "Skriv inn hurtig-tilkobling kode...",
|
||||||
"success": "Vellykket",
|
"success": "Vellykket",
|
||||||
"quick_connect_autorized": "Hurtig tilkobling autorisert",
|
"quick_connect_authorized": "Hurtig tilkobling autorisert",
|
||||||
"error": "Feil",
|
"error": "Feil",
|
||||||
"invalid_code": "Ugyldig kode",
|
"invalid_code": "Ugyldig kode",
|
||||||
"authorize": "Autoriser"
|
"authorize": "Autoriser"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Vis tilpassede menylenker",
|
"show_custom_menu_links": "Vis tilpassede menylenker",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
"hide_libraries": "Skjul biblioteker",
|
"hide_libraries": "Skjul biblioteker",
|
||||||
"select_liraries_you_want_to_hide": "Velg bibliotekene du vil skjule deg for Biblioteket og avsnittene for hjemmesider.",
|
"select_libraries_you_want_to_hide": "Velg bibliotekene du vil skjule deg for Biblioteket og avsnittene for hjemmesider.",
|
||||||
"disable_haptic_feedback": "Deaktiver Haptisk tilbakemelding",
|
"disable_haptic_feedback": "Deaktiver Haptisk tilbakemelding",
|
||||||
"default_quality": "Standard kvalitet",
|
"default_quality": "Standard kvalitet",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Enhet {{availableSpace}}%",
|
"device_usage": "Enhet {{availableSpace}}%",
|
||||||
"size_used": "{{used}} av {{total}} er i bruk",
|
"size_used": "{{used}} av {{total}} er i bruk",
|
||||||
"delete_all_downloaded_files": "Slett alle nedlastede filer",
|
"delete_all_downloaded_files": "Slett alle nedlastede filer",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Feil",
|
"error": "Feil",
|
||||||
"failed_to_get_stream_url": "Kan ikke hente nettadressen for stream",
|
"failed_to_get_stream_url": "Kan ikke hente nettadressen for stream",
|
||||||
"an_error_occured_while_playing_the_video": "En feil oppstod under video. Sjekk loggene i innstillingene.",
|
"an_error_occurred_while_playing_the_video": "En feil oppstod under video. Sjekk loggene i innstillingene.",
|
||||||
"client_error": "Feil med annonsør",
|
"client_error": "Feil med annonsør",
|
||||||
"could_not_create_stream_for_chromecast": "Kan ikke opprette en strøm for Chromecast",
|
"could_not_create_stream_for_chromecast": "Kan ikke opprette en strøm for Chromecast",
|
||||||
"message_from_server": "Melding fra tjener: {{message}}",
|
"message_from_server": "Melding fra tjener: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Rozumiem",
|
"got_it": "Rozumiem",
|
||||||
"connection_failed": "Połączenie nieudane",
|
"connection_failed": "Połączenie nieudane",
|
||||||
"could_not_connect_to_server": "Nie można połączyć się z serwerem. Sprawdź adres URL oraz połączenie sieciowe.",
|
"could_not_connect_to_server": "Nie można połączyć się z serwerem. Sprawdź adres URL oraz połączenie sieciowe.",
|
||||||
"an_unexpected_error_occured": "Wystąpił nieoczekiwany błąd",
|
"an_unexpected_error_occurred": "Wystąpił nieoczekiwany błąd",
|
||||||
"change_server": "Zmień serwer",
|
"change_server": "Zmień serwer",
|
||||||
"invalid_username_or_password": "Nieprawidłowa nazwa użytkownika lub hasło",
|
"invalid_username_or_password": "Nieprawidłowa nazwa użytkownika lub hasło",
|
||||||
"user_does_not_have_permission_to_log_in": "Użytkownik nie ma uprawnień do logowania",
|
"user_does_not_have_permission_to_log_in": "Użytkownik nie ma uprawnień do logowania",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Serwer zbyt długo nie odpowiada – spróbuj ponownie później",
|
"server_is_taking_too_long_to_respond_try_again_later": "Serwer zbyt długo nie odpowiada – spróbuj ponownie później",
|
||||||
"server_received_too_many_requests_try_again_later": "Serwer otrzymał zbyt wiele żądań – spróbuj ponownie później.",
|
"server_received_too_many_requests_try_again_later": "Serwer otrzymał zbyt wiele żądań – spróbuj ponownie później.",
|
||||||
"there_is_a_server_error": "Wystąpił błąd serwera",
|
"there_is_a_server_error": "Wystąpił błąd serwera",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Wystąpił nieoczekiwany błąd. Czy wpisałeś poprawny adres URL?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Wystąpił nieoczekiwany błąd. Czy wpisałeś poprawny adres URL?",
|
||||||
"too_old_server_text": "Wykryto nieobsługiwany serwer Jellyfin",
|
"too_old_server_text": "Wykryto nieobsługiwany serwer Jellyfin",
|
||||||
"too_old_server_description": "Proszę zaktualizować Jellyfin do najnowszej wersji"
|
"too_old_server_description": "Proszę zaktualizować Jellyfin do najnowszej wersji"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Autoryzuj szybkie połączenie",
|
"authorize_button": "Autoryzuj szybkie połączenie",
|
||||||
"enter_the_quick_connect_code": "Wpisz kod szybkiego połączenia...",
|
"enter_the_quick_connect_code": "Wpisz kod szybkiego połączenia...",
|
||||||
"success": "Sukces",
|
"success": "Sukces",
|
||||||
"quick_connect_autorized": "Szybkie połączenie autoryzowane",
|
"quick_connect_authorized": "Szybkie połączenie autoryzowane",
|
||||||
"error": "Błąd",
|
"error": "Błąd",
|
||||||
"invalid_code": "Nieprawidłowy kod",
|
"invalid_code": "Nieprawidłowy kod",
|
||||||
"authorize": "Autoryzuj"
|
"authorize": "Autoryzuj"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Pokaż niestandardowe odnośniki w menu",
|
"show_custom_menu_links": "Pokaż niestandardowe odnośniki w menu",
|
||||||
"show_large_home_carousel": "Wyświetl Dużą Karuzelę na ekranie głównym (beta)",
|
"show_large_home_carousel": "Wyświetl Dużą Karuzelę na ekranie głównym (beta)",
|
||||||
"hide_libraries": "Ukryj biblioteki",
|
"hide_libraries": "Ukryj biblioteki",
|
||||||
"select_liraries_you_want_to_hide": "Wybierz biblioteki, które chcesz ukryć na karcie Biblioteka i w sekcjach strony głównej.",
|
"select_libraries_you_want_to_hide": "Wybierz biblioteki, które chcesz ukryć na karcie Biblioteka i w sekcjach strony głównej.",
|
||||||
"disable_haptic_feedback": "Wyłącz wibracje",
|
"disable_haptic_feedback": "Wyłącz wibracje",
|
||||||
"default_quality": "Domyślna jakość",
|
"default_quality": "Domyślna jakość",
|
||||||
"default_playback_speed": "Domyślna prędkość odtwarzania",
|
"default_playback_speed": "Domyślna prędkość odtwarzania",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Urządzenie {{availableSpace}}%",
|
"device_usage": "Urządzenie {{availableSpace}}%",
|
||||||
"size_used": "{{used}} z {{total}} wykorzystane",
|
"size_used": "{{used}} z {{total}} wykorzystane",
|
||||||
"delete_all_downloaded_files": "Usuń wszystkie pobrane pliki",
|
"delete_all_downloaded_files": "Usuń wszystkie pobrane pliki",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Bufor muzyki",
|
"music_cache_title": "Bufor muzyki",
|
||||||
"music_cache_description": "Automatycznie buforuj piosenki w trakcie słuchania dla płynniejszego odtwarzania i wsparcia offline",
|
"music_cache_description": "Automatycznie buforuj piosenki w trakcie słuchania dla płynniejszego odtwarzania i wsparcia offline",
|
||||||
"clear_music_cache": "Wyczyść bufor muzyki",
|
"clear_music_cache": "Wyczyść bufor muzyki",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Błąd",
|
"error": "Błąd",
|
||||||
"failed_to_get_stream_url": "Nie udało się pobrać adresu strumienia",
|
"failed_to_get_stream_url": "Nie udało się pobrać adresu strumienia",
|
||||||
"an_error_occured_while_playing_the_video": "Wystąpił błąd podczas odtwarzania wideo. Sprawdź logi w ustawieniach.",
|
"an_error_occurred_while_playing_the_video": "Wystąpił błąd podczas odtwarzania wideo. Sprawdź logi w ustawieniach.",
|
||||||
"client_error": "Błąd klienta",
|
"client_error": "Błąd klienta",
|
||||||
"could_not_create_stream_for_chromecast": "Nie udało się utworzyć strumienia dla Chromecasta",
|
"could_not_create_stream_for_chromecast": "Nie udało się utworzyć strumienia dla Chromecasta",
|
||||||
"message_from_server": "Wiadomość z serwera: {{message}}",
|
"message_from_server": "Wiadomość z serwera: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Tenho isso",
|
"got_it": "Tenho isso",
|
||||||
"connection_failed": "Falha na conexão",
|
"connection_failed": "Falha na conexão",
|
||||||
"could_not_connect_to_server": "Não foi possível conectar ao servidor. Verifique a URL e sua conexão de rede.",
|
"could_not_connect_to_server": "Não foi possível conectar ao servidor. Verifique a URL e sua conexão de rede.",
|
||||||
"an_unexpected_error_occured": "Ocorreu um erro inesperado",
|
"an_unexpected_error_occurred": "An unexpected error occurred",
|
||||||
"change_server": "Alterar Servidor",
|
"change_server": "Alterar Servidor",
|
||||||
"invalid_username_or_password": "Usuário ou senha inválidos",
|
"invalid_username_or_password": "Usuário ou senha inválidos",
|
||||||
"user_does_not_have_permission_to_log_in": "Usuário não tem permissão para fazer login",
|
"user_does_not_have_permission_to_log_in": "Usuário não tem permissão para fazer login",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "O servidor está demorando muito para responder, tente novamente mais tarde",
|
"server_is_taking_too_long_to_respond_try_again_later": "O servidor está demorando muito para responder, tente novamente mais tarde",
|
||||||
"server_received_too_many_requests_try_again_later": "O servidor recebeu muitos pedidos, tente novamente mais tarde.",
|
"server_received_too_many_requests_try_again_later": "O servidor recebeu muitos pedidos, tente novamente mais tarde.",
|
||||||
"there_is_a_server_error": "Existe um erro no servidor",
|
"there_is_a_server_error": "Existe um erro no servidor",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Ocorreu um erro inesperado. Você inseriu a URL do servidor corretamente?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?",
|
||||||
"too_old_server_text": "Servidor Jellyfin Descoberto Não Suportado",
|
"too_old_server_text": "Servidor Jellyfin Descoberto Não Suportado",
|
||||||
"too_old_server_description": "Por favor, atualize o Jellyfin para a versão mais recente"
|
"too_old_server_description": "Por favor, atualize o Jellyfin para a versão mais recente"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Autorizar conexão rápida",
|
"authorize_button": "Autorizar conexão rápida",
|
||||||
"enter_the_quick_connect_code": "Insira o código de conexão rápida...",
|
"enter_the_quick_connect_code": "Insira o código de conexão rápida...",
|
||||||
"success": "Sucesso",
|
"success": "Sucesso",
|
||||||
"quick_connect_autorized": "Acesso Rápido Autorizado",
|
"quick_connect_authorized": "Quick Connect authorized",
|
||||||
"error": "Erro",
|
"error": "Erro",
|
||||||
"invalid_code": "Código inválido",
|
"invalid_code": "Código inválido",
|
||||||
"authorize": "Autorizar"
|
"authorize": "Autorizar"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Mostrar Links de Menu Personalizado",
|
"show_custom_menu_links": "Mostrar Links de Menu Personalizado",
|
||||||
"show_large_home_carousel": "Mostrar Carrossel Grande (beta)",
|
"show_large_home_carousel": "Mostrar Carrossel Grande (beta)",
|
||||||
"hide_libraries": "Ocultar bibliotecas",
|
"hide_libraries": "Ocultar bibliotecas",
|
||||||
"select_liraries_you_want_to_hide": "Selecione as bibliotecas que você deseja ocultar da aba Biblioteca e seções da página inicial.",
|
"select_libraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
|
||||||
"disable_haptic_feedback": "Desativar o retorno tátil",
|
"disable_haptic_feedback": "Desativar o retorno tátil",
|
||||||
"default_quality": "Qualidade Padrão",
|
"default_quality": "Qualidade Padrão",
|
||||||
"default_playback_speed": "Velocidade padrão de reprodução",
|
"default_playback_speed": "Velocidade padrão de reprodução",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Dispositivo {{availableSpace}}%",
|
"device_usage": "Dispositivo {{availableSpace}}%",
|
||||||
"size_used": "{{used}} de {{total}} Utilizados",
|
"size_used": "{{used}} de {{total}} Utilizados",
|
||||||
"delete_all_downloaded_files": "Excluir todos os arquivos baixados",
|
"delete_all_downloaded_files": "Excluir todos os arquivos baixados",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Cache de Música",
|
"music_cache_title": "Cache de Música",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Limpar Cache de Música",
|
"clear_music_cache": "Limpar Cache de Música",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "ERRO",
|
"error": "ERRO",
|
||||||
"failed_to_get_stream_url": "Falha ao obter a URL de transmissão",
|
"failed_to_get_stream_url": "Falha ao obter a URL de transmissão",
|
||||||
"an_error_occured_while_playing_the_video": "Ocorreu um erro ao reproduzir o vídeo. Verifique os logs nas configurações.",
|
"an_error_occurred_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
|
||||||
"client_error": "Erro de Cliente",
|
"client_error": "Erro de Cliente",
|
||||||
"could_not_create_stream_for_chromecast": "Não foi possível criar um fluxo para o Chromecast",
|
"could_not_create_stream_for_chromecast": "Não foi possível criar um fluxo para o Chromecast",
|
||||||
"message_from_server": "Mensagem do Servidor: {{message}}",
|
"message_from_server": "Mensagem do Servidor: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Am înţeles",
|
"got_it": "Am înţeles",
|
||||||
"connection_failed": "Conectare eșuată",
|
"connection_failed": "Conectare eșuată",
|
||||||
"could_not_connect_to_server": "Nu s-a putut conecta la server. Verificați adresa URL și conexiunea la internet.",
|
"could_not_connect_to_server": "Nu s-a putut conecta la server. Verificați adresa URL și conexiunea la internet.",
|
||||||
"an_unexpected_error_occured": "A apărut o eroare neașteptată",
|
"an_unexpected_error_occurred": "A apărut o eroare neașteptată",
|
||||||
"change_server": "Schimba",
|
"change_server": "Schimba",
|
||||||
"invalid_username_or_password": "Nume de utilizator sau parolă greșită",
|
"invalid_username_or_password": "Nume de utilizator sau parolă greșită",
|
||||||
"user_does_not_have_permission_to_log_in": "Utilizatorul nu are permisiunea de a se conecta",
|
"user_does_not_have_permission_to_log_in": "Utilizatorul nu are permisiunea de a se conecta",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Serverul răspunde prea greu, încearcă din nou mai târziu.",
|
"server_is_taking_too_long_to_respond_try_again_later": "Serverul răspunde prea greu, încearcă din nou mai târziu.",
|
||||||
"server_received_too_many_requests_try_again_later": "Serverul a primit prea multe solicitări, încercați din nou mai târziu.",
|
"server_received_too_many_requests_try_again_later": "Serverul a primit prea multe solicitări, încercați din nou mai târziu.",
|
||||||
"there_is_a_server_error": "Eroare de server",
|
"there_is_a_server_error": "Eroare de server",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "A apărut o eroare neașteptată. Ați introdus corect adresa URL a serverului?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "A apărut o eroare neașteptată. Ați introdus corect adresa URL a serverului?",
|
||||||
"too_old_server_text": "Serverul Jellyfin Neacceptat Descoperit",
|
"too_old_server_text": "Serverul Jellyfin Neacceptat Descoperit",
|
||||||
"too_old_server_description": "Vă rugăm să actualizați Jellyfin la cea mai recentă versiune"
|
"too_old_server_description": "Vă rugăm să actualizați Jellyfin la cea mai recentă versiune"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Autorizare conectare rapidă",
|
"authorize_button": "Autorizare conectare rapidă",
|
||||||
"enter_the_quick_connect_code": "Introduceți codul pt. conectare rapidă...",
|
"enter_the_quick_connect_code": "Introduceți codul pt. conectare rapidă...",
|
||||||
"success": "Succes",
|
"success": "Succes",
|
||||||
"quick_connect_autorized": "Conectare rapidă autorizată",
|
"quick_connect_authorized": "Conectare rapidă autorizată",
|
||||||
"error": "Eroare",
|
"error": "Eroare",
|
||||||
"invalid_code": "Cod invalid.",
|
"invalid_code": "Cod invalid.",
|
||||||
"authorize": "Autorizează"
|
"authorize": "Autorizează"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Afișează link-uri personalizate în meniu",
|
"show_custom_menu_links": "Afișează link-uri personalizate în meniu",
|
||||||
"show_large_home_carousel": "Arată Caruselul Media Mare (beta)",
|
"show_large_home_carousel": "Arată Caruselul Media Mare (beta)",
|
||||||
"hide_libraries": "Ascunde bibliotecile",
|
"hide_libraries": "Ascunde bibliotecile",
|
||||||
"select_liraries_you_want_to_hide": "Selectează bibliotecile pe care dorești să le ascunzi din fila Bibliotecă și din secțiunile paginii principale.",
|
"select_libraries_you_want_to_hide": "Selectează bibliotecile pe care dorești să le ascunzi din fila Bibliotecă și din secțiunile paginii principale.",
|
||||||
"disable_haptic_feedback": "Dezactivează vibrațiile tactile",
|
"disable_haptic_feedback": "Dezactivează vibrațiile tactile",
|
||||||
"default_quality": "Calitate implicită",
|
"default_quality": "Calitate implicită",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Dispozitiv {{availableSpace}}%",
|
"device_usage": "Dispozitiv {{availableSpace}}%",
|
||||||
"size_used": "{{used}} din {{total}} folosit",
|
"size_used": "{{used}} din {{total}} folosit",
|
||||||
"delete_all_downloaded_files": "Ștergeți toate fișierele descărcate",
|
"delete_all_downloaded_files": "Ștergeți toate fișierele descărcate",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Eroare",
|
"error": "Eroare",
|
||||||
"failed_to_get_stream_url": "Nu s-a putut obține adresa URL a fluxului",
|
"failed_to_get_stream_url": "Nu s-a putut obține adresa URL a fluxului",
|
||||||
"an_error_occured_while_playing_the_video": "A apărut o eroare la redarea videoclipului. Verificați jurnalele în setări.",
|
"an_error_occurred_while_playing_the_video": "A apărut o eroare la redarea videoclipului. Verificați jurnalele în setări.",
|
||||||
"client_error": "Eroare client",
|
"client_error": "Eroare client",
|
||||||
"could_not_create_stream_for_chromecast": "Nu s-a putut crea un flux pentru Chromecast",
|
"could_not_create_stream_for_chromecast": "Nu s-a putut crea un flux pentru Chromecast",
|
||||||
"message_from_server": "Mesaj de la server: {{message}}",
|
"message_from_server": "Mesaj de la server: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Принято",
|
"got_it": "Принято",
|
||||||
"connection_failed": "Соединение не удалось",
|
"connection_failed": "Соединение не удалось",
|
||||||
"could_not_connect_to_server": "Не удалось подключиться к серверу. Пожалуйста, проверьте URL и ваше интернет-соединение.",
|
"could_not_connect_to_server": "Не удалось подключиться к серверу. Пожалуйста, проверьте URL и ваше интернет-соединение.",
|
||||||
"an_unexpected_error_occured": "Возникла непредвиденная ошибка",
|
"an_unexpected_error_occurred": "Возникла непредвиденная ошибка",
|
||||||
"change_server": "Поменять сервер",
|
"change_server": "Поменять сервер",
|
||||||
"invalid_username_or_password": "Неправильное имя пользователя или пароль",
|
"invalid_username_or_password": "Неправильное имя пользователя или пароль",
|
||||||
"user_does_not_have_permission_to_log_in": "Пользователь не имеет прав на вход",
|
"user_does_not_have_permission_to_log_in": "Пользователь не имеет прав на вход",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Сервер долго не отвечает, попробуйте позже",
|
"server_is_taking_too_long_to_respond_try_again_later": "Сервер долго не отвечает, попробуйте позже",
|
||||||
"server_received_too_many_requests_try_again_later": "Сервер получил слишком много запросов, попробуйте позже.",
|
"server_received_too_many_requests_try_again_later": "Сервер получил слишком много запросов, попробуйте позже.",
|
||||||
"there_is_a_server_error": "Возникла ошибка сервера",
|
"there_is_a_server_error": "Возникла ошибка сервера",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Возникла непредвиденная ошибка. Вы правильно ввели URL?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Возникла непредвиденная ошибка. Вы правильно ввели URL?",
|
||||||
"too_old_server_text": "Обнаружен неподдерживаемый сервер Jellyfin",
|
"too_old_server_text": "Обнаружен неподдерживаемый сервер Jellyfin",
|
||||||
"too_old_server_description": "Пожалуйста, обновите Jellyfin до последней версии"
|
"too_old_server_description": "Пожалуйста, обновите Jellyfin до последней версии"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Авторизовать через быстрое подключение",
|
"authorize_button": "Авторизовать через быстрое подключение",
|
||||||
"enter_the_quick_connect_code": "Введите код для быстрого подключения...",
|
"enter_the_quick_connect_code": "Введите код для быстрого подключения...",
|
||||||
"success": "Успех",
|
"success": "Успех",
|
||||||
"quick_connect_autorized": "Быстрое подключение авторизовано",
|
"quick_connect_authorized": "Быстрое подключение авторизовано",
|
||||||
"error": "Ошибка",
|
"error": "Ошибка",
|
||||||
"invalid_code": "Неверный код",
|
"invalid_code": "Неверный код",
|
||||||
"authorize": "Авторизовать"
|
"authorize": "Авторизовать"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Показать ссылки пользовательского меню",
|
"show_custom_menu_links": "Показать ссылки пользовательского меню",
|
||||||
"show_large_home_carousel": "Показывать большую карусель (beta)",
|
"show_large_home_carousel": "Показывать большую карусель (beta)",
|
||||||
"hide_libraries": "Скрыть библиотеки",
|
"hide_libraries": "Скрыть библиотеки",
|
||||||
"select_liraries_you_want_to_hide": "Выберите Библиотеки, которое хотите спрятать из вкладки Библиотеки и домашней страницы.",
|
"select_libraries_you_want_to_hide": "Выберите Библиотеки, которое хотите спрятать из вкладки Библиотеки и домашней страницы.",
|
||||||
"disable_haptic_feedback": "Отключить тактильную обратную связь",
|
"disable_haptic_feedback": "Отключить тактильную обратную связь",
|
||||||
"default_quality": "Качество по умолчанию",
|
"default_quality": "Качество по умолчанию",
|
||||||
"default_playback_speed": "Скорость воспроизведения по умолчанию",
|
"default_playback_speed": "Скорость воспроизведения по умолчанию",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Устройство {{availableSpace}}%",
|
"device_usage": "Устройство {{availableSpace}}%",
|
||||||
"size_used": "{{used}} из {{total}} использовано",
|
"size_used": "{{used}} из {{total}} использовано",
|
||||||
"delete_all_downloaded_files": "Удалить все загруженные файлы",
|
"delete_all_downloaded_files": "Удалить все загруженные файлы",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Кеш музыки",
|
"music_cache_title": "Кеш музыки",
|
||||||
"music_cache_description": "Автоматически кешировать песни по мере прослушивания для плавного воспроизведения и поддержки отсутствия интернета",
|
"music_cache_description": "Автоматически кешировать песни по мере прослушивания для плавного воспроизведения и поддержки отсутствия интернета",
|
||||||
"clear_music_cache": "Очистить кеш музыки",
|
"clear_music_cache": "Очистить кеш музыки",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Ошибка",
|
"error": "Ошибка",
|
||||||
"failed_to_get_stream_url": "Не удалось получить URL потока",
|
"failed_to_get_stream_url": "Не удалось получить URL потока",
|
||||||
"an_error_occured_while_playing_the_video": "Возникла Неожиданная ошибка во время воспроизведения. Проверьте логи в настройках.",
|
"an_error_occurred_while_playing_the_video": "Возникла Неожиданная ошибка во время воспроизведения. Проверьте логи в настройках.",
|
||||||
"client_error": "Ошибка клиента",
|
"client_error": "Ошибка клиента",
|
||||||
"could_not_create_stream_for_chromecast": "Не удалось создать поток для Chromecast",
|
"could_not_create_stream_for_chromecast": "Не удалось создать поток для Chromecast",
|
||||||
"message_from_server": "Сообщение от сервера: {{message}}",
|
"message_from_server": "Сообщение от сервера: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Koden har mottagits",
|
"got_it": "Koden har mottagits",
|
||||||
"connection_failed": "Anslutningen Misslyckades",
|
"connection_failed": "Anslutningen Misslyckades",
|
||||||
"could_not_connect_to_server": "Det gick inte att ansluta till servern. Kontrollera webbadressen och din nätverksanslutning.",
|
"could_not_connect_to_server": "Det gick inte att ansluta till servern. Kontrollera webbadressen och din nätverksanslutning.",
|
||||||
"an_unexpected_error_occured": "Ett Oväntat Fel Uppstod",
|
"an_unexpected_error_occurred": "Ett Oväntat Fel Uppstod",
|
||||||
"change_server": "Byt Server",
|
"change_server": "Byt Server",
|
||||||
"invalid_username_or_password": "Ogiltigt användarnamn eller lösenord",
|
"invalid_username_or_password": "Ogiltigt användarnamn eller lösenord",
|
||||||
"user_does_not_have_permission_to_log_in": "Användaren har inte behörighet att logga in",
|
"user_does_not_have_permission_to_log_in": "Användaren har inte behörighet att logga in",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Servern svarar för långsamt, försök igen senare",
|
"server_is_taking_too_long_to_respond_try_again_later": "Servern svarar för långsamt, försök igen senare",
|
||||||
"server_received_too_many_requests_try_again_later": "Servern har fått för många förfrågningar, försök igen senare.",
|
"server_received_too_many_requests_try_again_later": "Servern har fått för många förfrågningar, försök igen senare.",
|
||||||
"there_is_a_server_error": "Ett serverfel uppstod",
|
"there_is_a_server_error": "Ett serverfel uppstod",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Ett oväntat fel uppstod. Har du angett serverns URL korrekt?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Ett oväntat fel uppstod. Har du angett serverns URL korrekt?",
|
||||||
"too_old_server_text": "Jellyfin Servern Stöds Inte",
|
"too_old_server_text": "Jellyfin Servern Stöds Inte",
|
||||||
"too_old_server_description": "Var God Uppdatera Jellyfin Till Den Senaste Versionen"
|
"too_old_server_description": "Var God Uppdatera Jellyfin Till Den Senaste Versionen"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Godkänn snabbanslutning",
|
"authorize_button": "Godkänn snabbanslutning",
|
||||||
"enter_the_quick_connect_code": "Ange snabbanslutningskod...",
|
"enter_the_quick_connect_code": "Ange snabbanslutningskod...",
|
||||||
"success": "Snabbanslutning lyckades",
|
"success": "Snabbanslutning lyckades",
|
||||||
"quick_connect_autorized": "Snabbanslutning Godkänd",
|
"quick_connect_authorized": "Snabbanslutning Godkänd",
|
||||||
"error": "Fel",
|
"error": "Fel",
|
||||||
"invalid_code": "Ogiltig kod",
|
"invalid_code": "Ogiltig kod",
|
||||||
"authorize": "Autentisera"
|
"authorize": "Autentisera"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Visa anpassade menylänkar",
|
"show_custom_menu_links": "Visa anpassade menylänkar",
|
||||||
"show_large_home_carousel": "Visa toppbanner (beta)",
|
"show_large_home_carousel": "Visa toppbanner (beta)",
|
||||||
"hide_libraries": "Dölj bibliotek",
|
"hide_libraries": "Dölj bibliotek",
|
||||||
"select_liraries_you_want_to_hide": "Välj de bibliotek du vill dölja på fliken Bibliotek och på startsidan.",
|
"select_libraries_you_want_to_hide": "Välj de bibliotek du vill dölja på fliken Bibliotek och på startsidan.",
|
||||||
"disable_haptic_feedback": "Stäng av vibrationer",
|
"disable_haptic_feedback": "Stäng av vibrationer",
|
||||||
"default_quality": "Förvald Kvalitet",
|
"default_quality": "Förvald Kvalitet",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Telefon {{availableSpace}}%",
|
"device_usage": "Telefon {{availableSpace}}%",
|
||||||
"size_used": "{{used}} av {{total}} används",
|
"size_used": "{{used}} av {{total}} används",
|
||||||
"delete_all_downloaded_files": "Ta bort alla nerladdade filer",
|
"delete_all_downloaded_files": "Ta bort alla nerladdade filer",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Musikcache",
|
"music_cache_title": "Musikcache",
|
||||||
"music_cache_description": "Cacha automatiskt låtar när du lyssnar för smidigare uppspelning och offline-stöd",
|
"music_cache_description": "Cacha automatiskt låtar när du lyssnar för smidigare uppspelning och offline-stöd",
|
||||||
"clear_music_cache": "Rensa musikcache",
|
"clear_music_cache": "Rensa musikcache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Fel",
|
"error": "Fel",
|
||||||
"failed_to_get_stream_url": "Kunde inte hämta stream-URL",
|
"failed_to_get_stream_url": "Kunde inte hämta stream-URL",
|
||||||
"an_error_occured_while_playing_the_video": "Ett fel uppstod vid uppspelning av videon. Kontrollera loggarna i inställningarna.",
|
"an_error_occurred_while_playing_the_video": "Ett fel uppstod vid uppspelning av videon. Kontrollera loggarna i inställningarna.",
|
||||||
"client_error": "Klientfel",
|
"client_error": "Klientfel",
|
||||||
"could_not_create_stream_for_chromecast": "Kunde inte skapa stream för Chromecast",
|
"could_not_create_stream_for_chromecast": "Kunde inte skapa stream för Chromecast",
|
||||||
"message_from_server": "Meddelande från servern: {{message}}",
|
"message_from_server": "Meddelande från servern: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Got It",
|
"got_it": "Got It",
|
||||||
"connection_failed": "Connection Failed",
|
"connection_failed": "Connection Failed",
|
||||||
"could_not_connect_to_server": "Could not connect to the server. Please check the URL and your network connection.",
|
"could_not_connect_to_server": "Could not connect to the server. Please check the URL and your network connection.",
|
||||||
"an_unexpected_error_occured": "An Unexpected Error Occurred",
|
"an_unexpected_error_occurred": "An unexpected error occurred",
|
||||||
"change_server": "Change Server",
|
"change_server": "Change Server",
|
||||||
"invalid_username_or_password": "Invalid Username or Password",
|
"invalid_username_or_password": "Invalid Username or Password",
|
||||||
"user_does_not_have_permission_to_log_in": "ผู้ใช้ไม่มีสิทธิ์ในการเข้าสู่ระบบ",
|
"user_does_not_have_permission_to_log_in": "ผู้ใช้ไม่มีสิทธิ์ในการเข้าสู่ระบบ",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Server is taking too long to respond, try again later",
|
"server_is_taking_too_long_to_respond_try_again_later": "Server is taking too long to respond, try again later",
|
||||||
"server_received_too_many_requests_try_again_later": "Server received too many requests, try again later.",
|
"server_received_too_many_requests_try_again_later": "Server received too many requests, try again later.",
|
||||||
"there_is_a_server_error": "There is a server error",
|
"there_is_a_server_error": "There is a server error",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?",
|
||||||
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
||||||
"too_old_server_description": "Please update Jellyfin to the latest version"
|
"too_old_server_description": "Please update Jellyfin to the latest version"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Authorize Quick Connect",
|
"authorize_button": "Authorize Quick Connect",
|
||||||
"enter_the_quick_connect_code": "Enter the quick connect code...",
|
"enter_the_quick_connect_code": "Enter the quick connect code...",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"quick_connect_autorized": "Quick Connect Authorized",
|
"quick_connect_authorized": "Quick Connect authorized",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"invalid_code": "Invalid Code",
|
"invalid_code": "Invalid Code",
|
||||||
"authorize": "Authorize"
|
"authorize": "Authorize"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Show Custom Menu Links",
|
"show_custom_menu_links": "Show Custom Menu Links",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
"hide_libraries": "Hide Libraries",
|
"hide_libraries": "Hide Libraries",
|
||||||
"select_liraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
|
"select_libraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
|
||||||
"disable_haptic_feedback": "Disable Haptic Feedback",
|
"disable_haptic_feedback": "Disable Haptic Feedback",
|
||||||
"default_quality": "Default Quality",
|
"default_quality": "Default Quality",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Device {{availableSpace}}%",
|
"device_usage": "Device {{availableSpace}}%",
|
||||||
"size_used": "{{used}} of {{total}} Used",
|
"size_used": "{{used}} of {{total}} Used",
|
||||||
"delete_all_downloaded_files": "Delete All Downloaded Files",
|
"delete_all_downloaded_files": "Delete All Downloaded Files",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"failed_to_get_stream_url": "Failed to get the stream URL",
|
"failed_to_get_stream_url": "Failed to get the stream URL",
|
||||||
"an_error_occured_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
|
"an_error_occurred_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
|
||||||
"client_error": "Client Error",
|
"client_error": "Client Error",
|
||||||
"could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast",
|
"could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast",
|
||||||
"message_from_server": "Message from Server: {{message}}",
|
"message_from_server": "Message from Server: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "jIyaj",
|
"got_it": "jIyaj",
|
||||||
"connection_failed": "ngoQlaHbe'",
|
"connection_failed": "ngoQlaHbe'",
|
||||||
"could_not_connect_to_server": "SeHlaw veS Ho'Do'laHbe'. URL 'ej ret ghun mej.",
|
"could_not_connect_to_server": "SeHlaw veS Ho'Do'laHbe'. URL 'ej ret ghun mej.",
|
||||||
"an_unexpected_error_occured": "num ghIq Doch",
|
"an_unexpected_error_occurred": "num ghIq Doch",
|
||||||
"change_server": "Ho'Do' veS yIghoS",
|
"change_server": "Ho'Do' veS yIghoS",
|
||||||
"invalid_username_or_password": "tlhIngan pagh ngoq De' law'be'",
|
"invalid_username_or_password": "tlhIngan pagh ngoq De' law'be'",
|
||||||
"user_does_not_have_permission_to_log_in": "tlhIngan lut 'el je'laHbe'",
|
"user_does_not_have_permission_to_log_in": "tlhIngan lut 'el je'laHbe'",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Ho'Do' veS jachrup. pItlh yIHaD",
|
"server_is_taking_too_long_to_respond_try_again_later": "Ho'Do' veS jachrup. pItlh yIHaD",
|
||||||
"server_received_too_many_requests_try_again_later": "Ho'Do' veS lutlh ngeb petlh law'. pItlh yIHaD.",
|
"server_received_too_many_requests_try_again_later": "Ho'Do' veS lutlh ngeb petlh law'. pItlh yIHaD.",
|
||||||
"there_is_a_server_error": "Ho'Do' veS ghIq maS",
|
"there_is_a_server_error": "Ho'Do' veS ghIq maS",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "num ghIq Doch. URL mej Danej'a'?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "num ghIq Doch. URL mej Danej'a'?",
|
||||||
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
||||||
"too_old_server_description": "**Jellyfin chu'qu' Dotlh yIchoHmoH**"
|
"too_old_server_description": "**Jellyfin chu'qu' Dotlh yIchoHmoH**"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "parmaq ngoQ yIje'",
|
"authorize_button": "parmaq ngoQ yIje'",
|
||||||
"enter_the_quick_connect_code": "parmaq ngoQ De' yI'el...",
|
"enter_the_quick_connect_code": "parmaq ngoQ De' yI'el...",
|
||||||
"success": "Qapla'",
|
"success": "Qapla'",
|
||||||
"quick_connect_autorized": "parmaq ngoQ je'laH",
|
"quick_connect_authorized": "parmaq ngoQ je'laH",
|
||||||
"error": "ghIq",
|
"error": "ghIq",
|
||||||
"invalid_code": "De' law'be'",
|
"invalid_code": "De' law'be'",
|
||||||
"authorize": "yIje'"
|
"authorize": "yIje'"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "menuDaq ret teqlu' yInej",
|
"show_custom_menu_links": "menuDaq ret teqlu' yInej",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
"hide_libraries": "De'wI' bom yIQIj",
|
"hide_libraries": "De'wI' bom yIQIj",
|
||||||
"select_liraries_you_want_to_hide": "De'wI' bom Danej QIj yIwIv.",
|
"select_libraries_you_want_to_hide": "De'wI' bom Danej QIj yIwIv.",
|
||||||
"disable_haptic_feedback": "Qub quvHa' yIQIj",
|
"disable_haptic_feedback": "Qub quvHa' yIQIj",
|
||||||
"default_quality": "wa' luj",
|
"default_quality": "wa' luj",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "naDev {{availableSpace}}%",
|
"device_usage": "naDev {{availableSpace}}%",
|
||||||
"size_used": "{{used}} / {{total}} ram",
|
"size_used": "{{used}} / {{total}} ram",
|
||||||
"delete_all_downloaded_files": "Hoch Qaw' Doch yIQaw'",
|
"delete_all_downloaded_files": "Hoch Qaw' Doch yIQaw'",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "ghIq",
|
"error": "ghIq",
|
||||||
"failed_to_get_stream_url": "tlhol ret URL tu'laHbe'",
|
"failed_to_get_stream_url": "tlhol ret URL tu'laHbe'",
|
||||||
"an_error_occured_while_playing_the_video": "mu'tlhegh tlholDI' ghIq. menDaq De' qon mej.",
|
"an_error_occurred_while_playing_the_video": "mu'tlhegh tlholDI' ghIq. menDaq De' qon mej.",
|
||||||
"client_error": "lut 'el ghIq",
|
"client_error": "lut 'el ghIq",
|
||||||
"could_not_create_stream_for_chromecast": "Chromecast tlhol ret qonlaHbe'",
|
"could_not_create_stream_for_chromecast": "Chromecast tlhol ret qonlaHbe'",
|
||||||
"message_from_server": "Ho'Do' veS jach: {{message}}",
|
"message_from_server": "Ho'Do' veS jach: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Anlaşıldı",
|
"got_it": "Anlaşıldı",
|
||||||
"connection_failed": "Bağlantı başarısız",
|
"connection_failed": "Bağlantı başarısız",
|
||||||
"could_not_connect_to_server": "Sunucuya bağlanılamadı. Lütfen URL'yi ve ağ bağlantınızı kontrol edin.",
|
"could_not_connect_to_server": "Sunucuya bağlanılamadı. Lütfen URL'yi ve ağ bağlantınızı kontrol edin.",
|
||||||
"an_unexpected_error_occured": "Beklenmedik bir hata oluştu",
|
"an_unexpected_error_occurred": "Beklenmedik bir hata oluştu",
|
||||||
"change_server": "Sunucu değiştir",
|
"change_server": "Sunucu değiştir",
|
||||||
"invalid_username_or_password": "Geçersiz kullanıcı adı veya şifre",
|
"invalid_username_or_password": "Geçersiz kullanıcı adı veya şifre",
|
||||||
"user_does_not_have_permission_to_log_in": "Kullanıcının giriş yapma izni yok",
|
"user_does_not_have_permission_to_log_in": "Kullanıcının giriş yapma izni yok",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Sunucunun yanıt vermesi çok uzun sürüyor, lütfen daha sonra tekrar deneyin",
|
"server_is_taking_too_long_to_respond_try_again_later": "Sunucunun yanıt vermesi çok uzun sürüyor, lütfen daha sonra tekrar deneyin",
|
||||||
"server_received_too_many_requests_try_again_later": "Sunucu çok fazla istek aldı, lütfen daha sonra tekrar deneyin.",
|
"server_received_too_many_requests_try_again_later": "Sunucu çok fazla istek aldı, lütfen daha sonra tekrar deneyin.",
|
||||||
"there_is_a_server_error": "Sunucu hatası var",
|
"there_is_a_server_error": "Sunucu hatası var",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Beklenmedik bir hata oluştu. Sunucu URL'sini doğru girdiğinizden emin misiniz?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Beklenmedik bir hata oluştu. Sunucu URL'sini doğru girdiğinizden emin misiniz?",
|
||||||
"too_old_server_text": "Desteklenmeyen Jellyfin Sunucu sürümü bulundu.",
|
"too_old_server_text": "Desteklenmeyen Jellyfin Sunucu sürümü bulundu.",
|
||||||
"too_old_server_description": "Lütfen Jellyfin'i en son sürüme güncelleyin."
|
"too_old_server_description": "Lütfen Jellyfin'i en son sürüme güncelleyin."
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Hızlı Bağlantıyı Yetkilendir",
|
"authorize_button": "Hızlı Bağlantıyı Yetkilendir",
|
||||||
"enter_the_quick_connect_code": "Hızlı bağlantı kodunu girin...",
|
"enter_the_quick_connect_code": "Hızlı bağlantı kodunu girin...",
|
||||||
"success": "Başarılı",
|
"success": "Başarılı",
|
||||||
"quick_connect_autorized": "Hızlı Bağlantı Yetkilendirildi",
|
"quick_connect_authorized": "Hızlı Bağlantı Yetkilendirildi",
|
||||||
"error": "Hata",
|
"error": "Hata",
|
||||||
"invalid_code": "Geçersiz kod",
|
"invalid_code": "Geçersiz kod",
|
||||||
"authorize": "Yetkilendir"
|
"authorize": "Yetkilendir"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Özel Menü Bağlantılarını Göster",
|
"show_custom_menu_links": "Özel Menü Bağlantılarını Göster",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
"hide_libraries": "Kütüphaneleri Gizle",
|
"hide_libraries": "Kütüphaneleri Gizle",
|
||||||
"select_liraries_you_want_to_hide": "Kütüphane sekmesinden ve ana sayfa bölümlerinden gizlemek istediğiniz kütüphaneleri seçin.",
|
"select_libraries_you_want_to_hide": "Kütüphane sekmesinden ve ana sayfa bölümlerinden gizlemek istediğiniz kütüphaneleri seçin.",
|
||||||
"disable_haptic_feedback": "Dokunsal Geri Bildirimi Devre Dışı Bırak",
|
"disable_haptic_feedback": "Dokunsal Geri Bildirimi Devre Dışı Bırak",
|
||||||
"default_quality": "Varsayılan kalite",
|
"default_quality": "Varsayılan kalite",
|
||||||
"default_playback_speed": "Varsayılan Oynatma Hızı",
|
"default_playback_speed": "Varsayılan Oynatma Hızı",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Cihaz {{availableSpace}}%",
|
"device_usage": "Cihaz {{availableSpace}}%",
|
||||||
"size_used": "{{used}} / {{total}} kullanıldı",
|
"size_used": "{{used}} / {{total}} kullanıldı",
|
||||||
"delete_all_downloaded_files": "Tüm indirilen dosyaları sil",
|
"delete_all_downloaded_files": "Tüm indirilen dosyaları sil",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Müzik Ön Belleği",
|
"music_cache_title": "Müzik Ön Belleği",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Müzik Ön Belleğini Temizle",
|
"clear_music_cache": "Müzik Ön Belleğini Temizle",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Hata",
|
"error": "Hata",
|
||||||
"failed_to_get_stream_url": "Yayın URL'si alınamadı",
|
"failed_to_get_stream_url": "Yayın URL'si alınamadı",
|
||||||
"an_error_occured_while_playing_the_video": "Video oynatılırken bir hata oluştu. Ayarlardaki günlüklere bakın.",
|
"an_error_occurred_while_playing_the_video": "Video oynatılırken bir hata oluştu. Ayarlardaki günlüklere bakın.",
|
||||||
"client_error": "İstemci hatası",
|
"client_error": "İstemci hatası",
|
||||||
"could_not_create_stream_for_chromecast": "Chromecast için yayın oluşturulamadı",
|
"could_not_create_stream_for_chromecast": "Chromecast için yayın oluşturulamadı",
|
||||||
"message_from_server": "Sunucudan mesaj: {{message}}",
|
"message_from_server": "Sunucudan mesaj: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Готово",
|
"got_it": "Готово",
|
||||||
"connection_failed": "Помилка зʼєднання",
|
"connection_failed": "Помилка зʼєднання",
|
||||||
"could_not_connect_to_server": "Неможливо підʼєднатися до серверу. Будь ласка перевірте URL і ваше зʼєднання з мережею",
|
"could_not_connect_to_server": "Неможливо підʼєднатися до серверу. Будь ласка перевірте URL і ваше зʼєднання з мережею",
|
||||||
"an_unexpected_error_occured": "Сталася несподівана помилка",
|
"an_unexpected_error_occurred": "Сталася несподівана помилка",
|
||||||
"change_server": "Змінити сервер",
|
"change_server": "Змінити сервер",
|
||||||
"invalid_username_or_password": "Неправильні імʼя користувача або пароль",
|
"invalid_username_or_password": "Неправильні імʼя користувача або пароль",
|
||||||
"user_does_not_have_permission_to_log_in": "Користувач не маю дозволу на вхід",
|
"user_does_not_have_permission_to_log_in": "Користувач не маю дозволу на вхід",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Сервер відповідає занадто довго, будь-ласка спробуйте пізніше",
|
"server_is_taking_too_long_to_respond_try_again_later": "Сервер відповідає занадто довго, будь-ласка спробуйте пізніше",
|
||||||
"server_received_too_many_requests_try_again_later": "Сервер отримав забагато запитів, будь ласка спробуйте пізніше.",
|
"server_received_too_many_requests_try_again_later": "Сервер отримав забагато запитів, будь ласка спробуйте пізніше.",
|
||||||
"there_is_a_server_error": "Відбулася помилка на стороні сервера",
|
"there_is_a_server_error": "Відбулася помилка на стороні сервера",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Відбулася несподівана помилка. Чи введений URL сервера правильний?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Відбулася несподівана помилка. Чи введений URL сервера правильний?",
|
||||||
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
||||||
"too_old_server_description": "Please update Jellyfin to the latest version"
|
"too_old_server_description": "Please update Jellyfin to the latest version"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Авторизуйте Швидке Зʼєднання",
|
"authorize_button": "Авторизуйте Швидке Зʼєднання",
|
||||||
"enter_the_quick_connect_code": "Введіть код для швидкого зʼєднання...",
|
"enter_the_quick_connect_code": "Введіть код для швидкого зʼєднання...",
|
||||||
"success": "Успіх",
|
"success": "Успіх",
|
||||||
"quick_connect_autorized": "Швидке Зʼєднання авторизовано",
|
"quick_connect_authorized": "Швидке Зʼєднання авторизовано",
|
||||||
"error": "Помилка",
|
"error": "Помилка",
|
||||||
"invalid_code": "Не правильний код",
|
"invalid_code": "Не правильний код",
|
||||||
"authorize": "Авторизувати"
|
"authorize": "Авторизувати"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Показати користувацькі посилання меню",
|
"show_custom_menu_links": "Показати користувацькі посилання меню",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
"hide_libraries": "Сховати медіатеки",
|
"hide_libraries": "Сховати медіатеки",
|
||||||
"select_liraries_you_want_to_hide": "Виберіть медіатеки, що бажаєте приховати з вкладки Медіатека і з секції на головній сторінці.",
|
"select_libraries_you_want_to_hide": "Виберіть медіатеки, що бажаєте приховати з вкладки Медіатека і з секції на головній сторінці.",
|
||||||
"disable_haptic_feedback": "Вимкнути тактильний зворотний зв'язок",
|
"disable_haptic_feedback": "Вимкнути тактильний зворотний зв'язок",
|
||||||
"default_quality": "Якість за замовченням",
|
"default_quality": "Якість за замовченням",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Гаджет {{availableSpace}}%",
|
"device_usage": "Гаджет {{availableSpace}}%",
|
||||||
"size_used": "{{used}} з {{total}} використано",
|
"size_used": "{{used}} з {{total}} використано",
|
||||||
"delete_all_downloaded_files": "Видалити усі завантаженні файли",
|
"delete_all_downloaded_files": "Видалити усі завантаженні файли",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Помилка",
|
"error": "Помилка",
|
||||||
"failed_to_get_stream_url": "Не вдалося отримати URL-адресу потоку",
|
"failed_to_get_stream_url": "Не вдалося отримати URL-адресу потоку",
|
||||||
"an_error_occured_while_playing_the_video": "Під час відтворення відео сталася помилка. Перевірте журнал в налаштуваннях.",
|
"an_error_occurred_while_playing_the_video": "Під час відтворення відео сталася помилка. Перевірте журнал в налаштуваннях.",
|
||||||
"client_error": "Помилка клієнту",
|
"client_error": "Помилка клієнту",
|
||||||
"could_not_create_stream_for_chromecast": "Не вдалося створити потік для Chromecast",
|
"could_not_create_stream_for_chromecast": "Не вдалося створити потік для Chromecast",
|
||||||
"message_from_server": "Повідомлення від серверу: {{message}}",
|
"message_from_server": "Повідомлення від серверу: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Đã hiểu",
|
"got_it": "Đã hiểu",
|
||||||
"connection_failed": "Kết nối thất bại",
|
"connection_failed": "Kết nối thất bại",
|
||||||
"could_not_connect_to_server": "Không thể kết nối tới máy chủ. Vui lòng kiểm tra URL và kết nối mạng.",
|
"could_not_connect_to_server": "Không thể kết nối tới máy chủ. Vui lòng kiểm tra URL và kết nối mạng.",
|
||||||
"an_unexpected_error_occured": "Đã xảy ra lỗi không mong muốn",
|
"an_unexpected_error_occurred": "Đã xảy ra lỗi không mong muốn",
|
||||||
"change_server": "Đổi máy chủ",
|
"change_server": "Đổi máy chủ",
|
||||||
"invalid_username_or_password": "Tên đăng nhập hoặc mật khẩu không đúng",
|
"invalid_username_or_password": "Tên đăng nhập hoặc mật khẩu không đúng",
|
||||||
"user_does_not_have_permission_to_log_in": "Người dùng không có quyền đăng nhập",
|
"user_does_not_have_permission_to_log_in": "Người dùng không có quyền đăng nhập",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Máy chủ phản hồi quá lâu, vui lòng thử lại sau",
|
"server_is_taking_too_long_to_respond_try_again_later": "Máy chủ phản hồi quá lâu, vui lòng thử lại sau",
|
||||||
"server_received_too_many_requests_try_again_later": "Máy chủ nhận quá nhiều yêu cầu, vui lòng thử lại sau.",
|
"server_received_too_many_requests_try_again_later": "Máy chủ nhận quá nhiều yêu cầu, vui lòng thử lại sau.",
|
||||||
"there_is_a_server_error": "Đã xảy ra lỗi ở máy chủ",
|
"there_is_a_server_error": "Đã xảy ra lỗi ở máy chủ",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Đã xảy ra lỗi không mong muốn. Bạn có chắc đã nhập đúng URL máy chủ?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "Đã xảy ra lỗi không mong muốn. Bạn có chắc đã nhập đúng URL máy chủ?",
|
||||||
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
||||||
"too_old_server_description": "Please update Jellyfin to the latest version"
|
"too_old_server_description": "Please update Jellyfin to the latest version"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Cho phép Kết nối nhanh",
|
"authorize_button": "Cho phép Kết nối nhanh",
|
||||||
"enter_the_quick_connect_code": "Nhập mã kết nối nhanh...",
|
"enter_the_quick_connect_code": "Nhập mã kết nối nhanh...",
|
||||||
"success": "Thành công",
|
"success": "Thành công",
|
||||||
"quick_connect_autorized": "Kết nối nhanh đã được cho phép",
|
"quick_connect_authorized": "Kết nối nhanh đã được cho phép",
|
||||||
"error": "Lỗi",
|
"error": "Lỗi",
|
||||||
"invalid_code": "Mã không hợp lệ",
|
"invalid_code": "Mã không hợp lệ",
|
||||||
"authorize": "Cho phép"
|
"authorize": "Cho phép"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Hiện liên kết tùy chỉnh",
|
"show_custom_menu_links": "Hiện liên kết tùy chỉnh",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
"hide_libraries": "Ẩn thư viện",
|
"hide_libraries": "Ẩn thư viện",
|
||||||
"select_liraries_you_want_to_hide": "Chọn các thư viện muốn ẩn khỏi mục Thư viện và Trang chủ.",
|
"select_libraries_you_want_to_hide": "Chọn các thư viện muốn ẩn khỏi mục Thư viện và Trang chủ.",
|
||||||
"disable_haptic_feedback": "Tắt phản hồi rung",
|
"disable_haptic_feedback": "Tắt phản hồi rung",
|
||||||
"default_quality": "Chất lượng mặc định",
|
"default_quality": "Chất lượng mặc định",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Thiết bị sử dụng {{availableSpace}}%",
|
"device_usage": "Thiết bị sử dụng {{availableSpace}}%",
|
||||||
"size_used": "{{used}} / {{total}} đã dùng",
|
"size_used": "{{used}} / {{total}} đã dùng",
|
||||||
"delete_all_downloaded_files": "Xóa toàn bộ tập tin đã tải",
|
"delete_all_downloaded_files": "Xóa toàn bộ tập tin đã tải",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Lỗi",
|
"error": "Lỗi",
|
||||||
"failed_to_get_stream_url": "Không thể lấy URL phát trực tiếp",
|
"failed_to_get_stream_url": "Không thể lấy URL phát trực tiếp",
|
||||||
"an_error_occured_while_playing_the_video": "Có lỗi khi phát video. Xem nhật ký trong cài đặt.",
|
"an_error_occurred_while_playing_the_video": "Có lỗi khi phát video. Xem nhật ký trong cài đặt.",
|
||||||
"client_error": "Lỗi phía máy khách",
|
"client_error": "Lỗi phía máy khách",
|
||||||
"could_not_create_stream_for_chromecast": "Không thể tạo luồng cho Chromecast",
|
"could_not_create_stream_for_chromecast": "Không thể tạo luồng cho Chromecast",
|
||||||
"message_from_server": "Thông báo từ máy chủ: {{message}}",
|
"message_from_server": "Thông báo từ máy chủ: {{message}}",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"got_it": "Got It",
|
"got_it": "Got It",
|
||||||
"connection_failed": "Connection Failed",
|
"connection_failed": "Connection Failed",
|
||||||
"could_not_connect_to_server": "Could not connect to the server. Please check the URL and your network connection.",
|
"could_not_connect_to_server": "Could not connect to the server. Please check the URL and your network connection.",
|
||||||
"an_unexpected_error_occured": "An Unexpected Error Occurred",
|
"an_unexpected_error_occurred": "An unexpected error occurred",
|
||||||
"change_server": "Change Server",
|
"change_server": "Change Server",
|
||||||
"invalid_username_or_password": "Invalid Username or Password",
|
"invalid_username_or_password": "Invalid Username or Password",
|
||||||
"user_does_not_have_permission_to_log_in": "User does not have permission to log in",
|
"user_does_not_have_permission_to_log_in": "User does not have permission to log in",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Server is taking too long to respond, try again later",
|
"server_is_taking_too_long_to_respond_try_again_later": "Server is taking too long to respond, try again later",
|
||||||
"server_received_too_many_requests_try_again_later": "Server received too many requests, try again later.",
|
"server_received_too_many_requests_try_again_later": "Server received too many requests, try again later.",
|
||||||
"there_is_a_server_error": "There is a server error",
|
"there_is_a_server_error": "There is a server error",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?",
|
"an_unexpected_error_occurred_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?",
|
||||||
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
||||||
"too_old_server_description": "Please update Jellyfin to the latest version"
|
"too_old_server_description": "Please update Jellyfin to the latest version"
|
||||||
},
|
},
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
"authorize_button": "Authorize Quick Connect",
|
"authorize_button": "Authorize Quick Connect",
|
||||||
"enter_the_quick_connect_code": "Enter the quick connect code...",
|
"enter_the_quick_connect_code": "Enter the quick connect code...",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"quick_connect_autorized": "Quick Connect Authorized",
|
"quick_connect_authorized": "Quick Connect authorized",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"invalid_code": "Invalid Code",
|
"invalid_code": "Invalid Code",
|
||||||
"authorize": "Authorize"
|
"authorize": "Authorize"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
"show_custom_menu_links": "Show Custom Menu Links",
|
"show_custom_menu_links": "Show Custom Menu Links",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
"hide_libraries": "Hide Libraries",
|
"hide_libraries": "Hide Libraries",
|
||||||
"select_liraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
|
"select_libraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
|
||||||
"disable_haptic_feedback": "Disable Haptic Feedback",
|
"disable_haptic_feedback": "Disable Haptic Feedback",
|
||||||
"default_quality": "Default Quality",
|
"default_quality": "Default Quality",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
@@ -384,6 +384,8 @@
|
|||||||
"device_usage": "Device {{availableSpace}}%",
|
"device_usage": "Device {{availableSpace}}%",
|
||||||
"size_used": "{{used}} of {{total}} Used",
|
"size_used": "{{used}} of {{total}} Used",
|
||||||
"delete_all_downloaded_files": "Delete All Downloaded Files",
|
"delete_all_downloaded_files": "Delete All Downloaded Files",
|
||||||
|
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
|
||||||
|
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Music Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
"mpv_player_title": "MPV Player",
|
"mpv_player_title": "MPV Player",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"failed_to_get_stream_url": "Failed to get the stream URL",
|
"failed_to_get_stream_url": "Failed to get the stream URL",
|
||||||
"an_error_occured_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
|
"an_error_occurred_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
|
||||||
"client_error": "Client Error",
|
"client_error": "Client Error",
|
||||||
"could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast",
|
"could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast",
|
||||||
"message_from_server": "Message from Server: {{message}}",
|
"message_from_server": "Message from Server: {{message}}",
|
||||||
|
|||||||
Reference in New Issue
Block a user