mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-20 01:58:22 +00:00
Compare commits
1 Commits
refactor-c
...
I10n_crowd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ee43de305 |
@@ -1,8 +1,5 @@
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import type { PlaybackProgressInfo } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { getPlaystateApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { Platform } from "react-native";
|
||||
import { Pressable } from "react-native-gesture-handler";
|
||||
import GoogleCast, {
|
||||
@@ -13,7 +10,6 @@ import GoogleCast, {
|
||||
useMediaStatus,
|
||||
useRemoteMediaClient,
|
||||
} from "react-native-google-cast";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { RoundButton } from "./RoundButton";
|
||||
|
||||
export function Chromecast({
|
||||
@@ -28,10 +24,6 @@ export function Chromecast({
|
||||
const sessionManager = GoogleCast.getSessionManager();
|
||||
const discoveryManager = GoogleCast.getDiscoveryManager();
|
||||
const mediaStatus = useMediaStatus();
|
||||
const api = useAtomValue(apiAtom);
|
||||
const user = useAtomValue(userAtom);
|
||||
|
||||
const lastReportedProgressRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -44,53 +36,6 @@ export function Chromecast({
|
||||
})();
|
||||
}, [client, devices, castDevice, sessionManager, discoveryManager]);
|
||||
|
||||
// Report video progress to Jellyfin server
|
||||
useEffect(() => {
|
||||
if (
|
||||
!api ||
|
||||
!user?.Id ||
|
||||
!mediaStatus ||
|
||||
!mediaStatus.mediaInfo?.contentId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const streamPosition = mediaStatus.streamPosition || 0;
|
||||
|
||||
// Report every 10 seconds
|
||||
if (Math.abs(streamPosition - lastReportedProgressRef.current) < 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentId = mediaStatus.mediaInfo.contentId;
|
||||
const positionTicks = Math.floor(streamPosition * 10000000);
|
||||
const isPaused = mediaStatus.playerState === "paused";
|
||||
const streamUrl = mediaStatus.mediaInfo.contentUrl || "";
|
||||
const isTranscoding = streamUrl.includes("m3u8");
|
||||
|
||||
const progressInfo: PlaybackProgressInfo = {
|
||||
ItemId: contentId,
|
||||
PositionTicks: positionTicks,
|
||||
IsPaused: isPaused,
|
||||
PlayMethod: isTranscoding ? "Transcode" : "DirectStream",
|
||||
PlaySessionId: contentId,
|
||||
};
|
||||
|
||||
getPlaystateApi(api)
|
||||
.reportPlaybackProgress({ playbackProgressInfo: progressInfo })
|
||||
.then(() => {
|
||||
lastReportedProgressRef.current = streamPosition;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed to report Chromecast progress:", error);
|
||||
});
|
||||
}, [
|
||||
api,
|
||||
user?.Id,
|
||||
mediaStatus?.streamPosition,
|
||||
mediaStatus?.mediaInfo?.contentId,
|
||||
]);
|
||||
|
||||
// Android requires the cast button to be present for startDiscovery to work
|
||||
const AndroidCastButton = useCallback(
|
||||
() =>
|
||||
|
||||
@@ -167,17 +167,16 @@ final class MPVLayerRenderer {
|
||||
// Use AVFoundation video output - required for PiP support
|
||||
checkError(mpv_set_option_string(handle, "vo", "avfoundation"))
|
||||
|
||||
// Enable composite OSD mode - renders subtitles directly onto video frames using GPU
|
||||
// This is better for PiP as subtitles are baked into the video
|
||||
// NOTE: Must be set BEFORE the #if targetEnvironment check or tvOS will freeze on player exit
|
||||
checkError(mpv_set_option_string(handle, "avfoundation-composite-osd", "yes"))
|
||||
|
||||
// Hardware decoding with VideoToolbox
|
||||
// On simulator, use software decoding since VideoToolbox is not available
|
||||
// On device, use VideoToolbox with software fallback enabled
|
||||
#if targetEnvironment(simulator)
|
||||
checkError(mpv_set_option_string(handle, "hwdec", "no"))
|
||||
#else
|
||||
// Only enable composite OSD mode on real device (OSD is not supported in simulator).
|
||||
// This renders subtitles directly onto video frames using the GPU, which is better for PiP since subtitles are baked into the video.
|
||||
checkError(mpv_set_option_string(handle, "avfoundation-composite-osd", "yes"))
|
||||
|
||||
checkError(mpv_set_option_string(handle, "hwdec", "videotoolbox"))
|
||||
#endif
|
||||
checkError(mpv_set_option_string(handle, "hwdec-codecs", "all"))
|
||||
|
||||
@@ -39,39 +39,39 @@
|
||||
"please_login_again": "Su sesión guardada ha caducado. Por favor, inicie sesión de nuevo.",
|
||||
"remove_saved_login": "Eliminar inicio de sesión guardado",
|
||||
"remove_saved_login_description": "Esto eliminará tus credenciales guardadas para este servidor. Tendrás que volver a introducir tu nombre de usuario y contraseña la próxima vez.",
|
||||
"accounts_count": "{{count}} accounts",
|
||||
"select_account": "Select Account",
|
||||
"add_account": "Add Account",
|
||||
"remove_account_description": "This will remove the saved credentials for {{username}}."
|
||||
"accounts_count": "{{count}} cuentas",
|
||||
"select_account": "Seleccione una cuenta",
|
||||
"add_account": "Añadir cuenta",
|
||||
"remove_account_description": "Esto eliminará las credenciales guardadas para {{username}}."
|
||||
},
|
||||
"save_account": {
|
||||
"title": "Save Account",
|
||||
"save_for_later": "Save this account",
|
||||
"security_option": "Security Option",
|
||||
"no_protection": "No protection",
|
||||
"no_protection_desc": "Quick login without authentication",
|
||||
"pin_code": "PIN code",
|
||||
"pin_code_desc": "4-digit PIN required when switching",
|
||||
"password": "Re-enter password",
|
||||
"password_desc": "Password required when switching",
|
||||
"save_button": "Save",
|
||||
"cancel_button": "Cancel"
|
||||
"title": "Guardar Cuenta",
|
||||
"save_for_later": "Guardar esta cuenta",
|
||||
"security_option": "Opciones de seguridad",
|
||||
"no_protection": "Sin Protección",
|
||||
"no_protection_desc": "Inicio de sesión rápido sin autenticación",
|
||||
"pin_code": "Código PIN",
|
||||
"pin_code_desc": "PIN de 4 dígitos requerido al cambiar",
|
||||
"password": "Vuelva a introducir la contraseña",
|
||||
"password_desc": "Contraseña requerida al cambiar",
|
||||
"save_button": "Guardar",
|
||||
"cancel_button": "Cancelar"
|
||||
},
|
||||
"pin": {
|
||||
"enter_pin": "Enter PIN",
|
||||
"enter_pin_for": "Enter PIN for {{username}}",
|
||||
"enter_4_digits": "Enter 4 digits",
|
||||
"invalid_pin": "Invalid PIN",
|
||||
"setup_pin": "Set Up PIN",
|
||||
"confirm_pin": "Confirm PIN",
|
||||
"pins_dont_match": "PINs don't match",
|
||||
"forgot_pin": "Forgot PIN?",
|
||||
"forgot_pin_desc": "Your saved credentials will be removed"
|
||||
"enter_pin": "Introduce el PIN",
|
||||
"enter_pin_for": "Introduzca el PIN para {{username}}",
|
||||
"enter_4_digits": "Introduce 4 dígitos",
|
||||
"invalid_pin": "PIN inválido",
|
||||
"setup_pin": "Configurar PIN",
|
||||
"confirm_pin": "Confirmar PIN",
|
||||
"pins_dont_match": "Los códigos PIN no coinciden",
|
||||
"forgot_pin": "¿Olvidó el PIN?",
|
||||
"forgot_pin_desc": "Sus credenciales guardadas serán eliminadas"
|
||||
},
|
||||
"password": {
|
||||
"enter_password": "Enter Password",
|
||||
"enter_password_for": "Enter password for {{username}}",
|
||||
"invalid_password": "Invalid password"
|
||||
"enter_password": "Introduzca la contraseña",
|
||||
"enter_password_for": "Introduzca la contraseña para {{username}}",
|
||||
"invalid_password": "Contraseña inválida"
|
||||
},
|
||||
"home": {
|
||||
"checking_server_connection": "Comprobando conexión con el servidor...",
|
||||
@@ -124,32 +124,32 @@
|
||||
"hide_remote_session_button": "Ocultar botón de sesión remota"
|
||||
},
|
||||
"network": {
|
||||
"title": "Network",
|
||||
"local_network": "Local Network",
|
||||
"auto_switch_enabled": "Auto-switch when at home",
|
||||
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
|
||||
"local_url": "Local URL",
|
||||
"local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
|
||||
"title": "Cadena",
|
||||
"local_network": "Red local",
|
||||
"auto_switch_enabled": "Cambiar automáticamente en casa",
|
||||
"auto_switch_description": "Cambiar automáticamente a la URL local cuando se conecta a la WiFi de casa",
|
||||
"local_url": "URL local",
|
||||
"local_url_hint": "Introduzca la dirección de su servidor local (por ejemplo, http://192.168.1.100:8096)",
|
||||
"local_url_placeholder": "http://192.168.1.100:8096",
|
||||
"home_wifi_networks": "Home WiFi Networks",
|
||||
"add_current_network": "Add \"{{ssid}}\"",
|
||||
"not_connected_to_wifi": "Not connected to WiFi",
|
||||
"no_networks_configured": "No networks configured",
|
||||
"add_network_hint": "Add your home WiFi network to enable auto-switching",
|
||||
"current_wifi": "Current WiFi",
|
||||
"using_url": "Using",
|
||||
"local": "Local URL",
|
||||
"remote": "Remote URL",
|
||||
"not_connected": "Not connected",
|
||||
"current_server": "Current Server",
|
||||
"remote_url": "Remote URL",
|
||||
"active_url": "Active URL",
|
||||
"not_configured": "Not configured",
|
||||
"network_added": "Network added",
|
||||
"network_already_added": "Network already added",
|
||||
"no_wifi_connected": "Not connected to WiFi",
|
||||
"permission_denied": "Location permission denied",
|
||||
"permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
|
||||
"home_wifi_networks": "Redes WiFi domésticas",
|
||||
"add_current_network": "Añadir \"{{ssid}}\"",
|
||||
"not_connected_to_wifi": "No está conectado a WiFi",
|
||||
"no_networks_configured": "No hay redes configuradas",
|
||||
"add_network_hint": "Añade tu red WiFi doméstica para activar el cambio automático",
|
||||
"current_wifi": "WiFi actual",
|
||||
"using_url": "Utilizando",
|
||||
"local": "URL local",
|
||||
"remote": "URL Remota",
|
||||
"not_connected": "Sin conexión",
|
||||
"current_server": "Servidor actual",
|
||||
"remote_url": "URL Remota",
|
||||
"active_url": "URL Activa",
|
||||
"not_configured": "Sin configurar",
|
||||
"network_added": "Red añadida",
|
||||
"network_already_added": "Red ya añadida",
|
||||
"no_wifi_connected": "Sin conexión a WiFi",
|
||||
"permission_denied": "Permiso de ubicación denegado",
|
||||
"permission_denied_explanation": "Se necesita el permiso de ubicación para detectar la red WiFi para cambiar automáticamente. Por favor, actívala en Configuración."
|
||||
},
|
||||
"user_info": {
|
||||
"user_info_title": "Información de usuario",
|
||||
@@ -195,12 +195,12 @@
|
||||
"none": "Ninguno",
|
||||
"language": "Idioma",
|
||||
"transcode_mode": {
|
||||
"title": "Audio Transcoding",
|
||||
"description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
|
||||
"title": "Transcodificación de audio",
|
||||
"description": "Controla cómo el audio envolvente (7.1, TrueHD, DTS-HD) es manejado",
|
||||
"auto": "Auto",
|
||||
"stereo": "Force Stereo",
|
||||
"5_1": "Allow 5.1",
|
||||
"passthrough": "Passthrough"
|
||||
"stereo": "Forzar salida estéreo",
|
||||
"5_1": "Permitir 5.1",
|
||||
"passthrough": "Directo"
|
||||
}
|
||||
},
|
||||
"subtitles": {
|
||||
@@ -259,16 +259,16 @@
|
||||
"hardware_decode_description": "Utilizar la aceleración de hardware para la decodificación de vídeo. Deshabilite si experimenta problemas de reproducción."
|
||||
},
|
||||
"vlc_subtitles": {
|
||||
"title": "VLC Subtitle Settings",
|
||||
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
|
||||
"text_color": "Text Color",
|
||||
"background_color": "Background Color",
|
||||
"background_opacity": "Background Opacity",
|
||||
"outline_color": "Outline Color",
|
||||
"outline_opacity": "Outline Opacity",
|
||||
"outline_thickness": "Outline Thickness",
|
||||
"bold": "Bold Text",
|
||||
"margin": "Bottom Margin"
|
||||
"title": "Configuración de subtítulos VLC",
|
||||
"hint": "Personalizar la apariencia de los subtítulos para el reproductor VLC. Los cambios tendrán efecto en la próxima reproducción.",
|
||||
"text_color": "Color del texto",
|
||||
"background_color": "Color del fondo",
|
||||
"background_opacity": "Opacidad del fondo",
|
||||
"outline_color": "Color del contorno",
|
||||
"outline_opacity": "Opacidad del contorno",
|
||||
"outline_thickness": "Grosor del contorno",
|
||||
"bold": "Texto en negrita",
|
||||
"margin": "Margen inferior"
|
||||
},
|
||||
"video_player": {
|
||||
"title": "Reproductor de vídeo",
|
||||
@@ -300,13 +300,13 @@
|
||||
"VLC_4": "VLC 4 (Experimental + PiP)"
|
||||
},
|
||||
"show_custom_menu_links": "Mostrar enlaces de menú personalizados",
|
||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||
"show_large_home_carousel": "Mostrar carrusel del menú principal grande (beta)",
|
||||
"hide_libraries": "Ocultar bibliotecas",
|
||||
"select_liraries_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",
|
||||
"default_quality": "Calidad por defecto",
|
||||
"default_playback_speed": "Velocidad de reproducción predeterminada",
|
||||
"auto_play_next_episode": "Auto-play Next Episode",
|
||||
"auto_play_next_episode": "Reproducir automáticamente el siguiente episodio",
|
||||
"max_auto_play_episode_count": "Máximo número de episodios de Auto Play",
|
||||
"disabled": "Deshabilitado"
|
||||
},
|
||||
@@ -317,10 +317,10 @@
|
||||
"title": "Música",
|
||||
"playback_title": "Reproducir",
|
||||
"playback_description": "Configurar cómo se reproduce la música.",
|
||||
"prefer_downloaded": "Prefer Downloaded Songs",
|
||||
"prefer_downloaded": "Preferir las canciones descargadas",
|
||||
"caching_title": "Almacenando en caché",
|
||||
"caching_description": "Automatically cache upcoming tracks for smoother playback.",
|
||||
"lookahead_enabled": "Enable Look-Ahead Caching",
|
||||
"caching_description": "Cachear automáticamente las próximas canciones para una reproducción más suave.",
|
||||
"lookahead_enabled": "Activar el look-Ahead Cache",
|
||||
"lookahead_count": "",
|
||||
"max_cache_size": "Tamaño máximo del caché"
|
||||
},
|
||||
@@ -399,7 +399,7 @@
|
||||
"size_used": "{{used}} de {{total}} usado",
|
||||
"delete_all_downloaded_files": "Eliminar todos los archivos descargados",
|
||||
"music_cache_title": "Caché de música",
|
||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||
"music_cache_description": "Cachear automáticamente las canciones mientras escuchas una reproducción más suave y soporte sin conexión",
|
||||
"enable_music_cache": "Activar Caché de Música",
|
||||
"clear_music_cache": "Borrar Caché de Música",
|
||||
"music_cache_size": "Caché {{Tamaño}}",
|
||||
@@ -504,10 +504,10 @@
|
||||
"delete": "Borrar",
|
||||
"ok": "Aceptar",
|
||||
"remove": "Eliminar",
|
||||
"next": "Next",
|
||||
"back": "Back",
|
||||
"continue": "Continue",
|
||||
"verifying": "Verifying..."
|
||||
"next": "Siguiente",
|
||||
"back": "Atrás",
|
||||
"continue": "Continuar",
|
||||
"verifying": "Verificando..."
|
||||
},
|
||||
"search": {
|
||||
"search": "Buscar...",
|
||||
@@ -753,8 +753,8 @@
|
||||
"downloaded": "Descargado",
|
||||
"downloading": "Descargando...",
|
||||
"cached": "En caché",
|
||||
"delete_download": "Delete Download",
|
||||
"delete_cache": "Remove from Cache",
|
||||
"delete_download": "Eliminar descarga",
|
||||
"delete_cache": "Borrar del caché",
|
||||
"go_to_artist": "Ir al artista",
|
||||
"go_to_album": "Ir al álbum",
|
||||
"add_to_favorites": "Añadir a Favoritos",
|
||||
|
||||
@@ -305,8 +305,8 @@
|
||||
"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.",
|
||||
"disable_haptic_feedback": "Désactiver le retour haptique",
|
||||
"default_quality": "Qualité par défaut",
|
||||
"default_playback_speed": "Default Playback Speed",
|
||||
"auto_play_next_episode": "Auto-play Next Episode",
|
||||
"default_playback_speed": "Vitesse de lecture par défaut",
|
||||
"auto_play_next_episode": "Lecture automatique de l'épisode suivant",
|
||||
"max_auto_play_episode_count": "Nombre d'épisodes en lecture automatique max",
|
||||
"disabled": "Désactivé"
|
||||
},
|
||||
@@ -314,15 +314,15 @@
|
||||
"downloads_title": "Téléchargements"
|
||||
},
|
||||
"music": {
|
||||
"title": "Music",
|
||||
"playback_title": "Playback",
|
||||
"playback_description": "Configure how music is played.",
|
||||
"prefer_downloaded": "Prefer Downloaded Songs",
|
||||
"caching_title": "Caching",
|
||||
"caching_description": "Automatically cache upcoming tracks for smoother playback.",
|
||||
"lookahead_enabled": "Enable Look-Ahead Caching",
|
||||
"lookahead_count": "Tracks to Pre-cache",
|
||||
"max_cache_size": "Max Cache Size"
|
||||
"title": "Musique",
|
||||
"playback_title": "Lecture",
|
||||
"playback_description": "Configurer le mode de lecture de la musique.",
|
||||
"prefer_downloaded": "Supprimer toutes les musiques téléchargées",
|
||||
"caching_title": "Mise en cache",
|
||||
"caching_description": "Mettre automatiquement en cache les pistes à venir pour une lecture plus fluide.",
|
||||
"lookahead_enabled": "Activer la mise en cache guidée",
|
||||
"lookahead_count": "Pistes à pré-mettre en cache",
|
||||
"max_cache_size": "Taille max de cache"
|
||||
},
|
||||
"plugins": {
|
||||
"plugins_title": "Plugins",
|
||||
@@ -357,19 +357,19 @@
|
||||
"save_button": "Enregistrer",
|
||||
"toasts": {
|
||||
"saved": "Enregistré",
|
||||
"refreshed": "Settings refreshed from server"
|
||||
"refreshed": "Paramètres actualisés depuis le serveur"
|
||||
},
|
||||
"refresh_from_server": "Refresh Settings from Server"
|
||||
"refresh_from_server": "Rafraîchir les paramètres depuis le serveur"
|
||||
},
|
||||
"streamystats": {
|
||||
"enable_streamystats": "Enable Streamystats",
|
||||
"disable_streamystats": "Disable Streamystats",
|
||||
"enable_search": "Use for Search",
|
||||
"enable_streamystats": "Activer Streamystats",
|
||||
"disable_streamystats": "Désactiver Streamystats",
|
||||
"enable_search": "Utiliser pour la recherche",
|
||||
"url": "URL",
|
||||
"server_url_placeholder": "http(s)://streamystats.example.com",
|
||||
"streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
|
||||
"read_more_about_streamystats": "Read More About Streamystats.",
|
||||
"save_button": "Save",
|
||||
"streamystats_search_hint": "Entrez l'URL de votre serveur Streamystats. L'URL doit inclure http ou https et éventuellement le port.",
|
||||
"read_more_about_streamystats": "En savoir plus sur Streamystats.",
|
||||
"save_button": "Enregistrer",
|
||||
"save": "Enregistrer",
|
||||
"features_title": "Fonctionnalités",
|
||||
"home_sections_title": "Sections de la page d´accueil",
|
||||
@@ -572,7 +572,7 @@
|
||||
"genres": "Genres",
|
||||
"years": "Années",
|
||||
"sort_by": "Trier par",
|
||||
"filter_by": "Filter By",
|
||||
"filter_by": "Filtrer par",
|
||||
"sort_order": "Ordre de tri",
|
||||
"tags": "Tags"
|
||||
}
|
||||
@@ -719,25 +719,25 @@
|
||||
"favorites": "Favoris"
|
||||
},
|
||||
"music": {
|
||||
"title": "Music",
|
||||
"title": "Musique",
|
||||
"tabs": {
|
||||
"suggestions": "Suggestions",
|
||||
"albums": "Albums",
|
||||
"artists": "Artists",
|
||||
"artists": "Artistes",
|
||||
"playlists": "Playlists",
|
||||
"tracks": "tracks"
|
||||
"tracks": "morceaux"
|
||||
},
|
||||
"filters": {
|
||||
"all": "All"
|
||||
"all": "Toutes"
|
||||
},
|
||||
"recently_added": "Recently Added",
|
||||
"recently_played": "Recently Played",
|
||||
"frequently_played": "Frequently Played",
|
||||
"explore": "Explore",
|
||||
"top_tracks": "Top Tracks",
|
||||
"play": "Play",
|
||||
"shuffle": "Shuffle",
|
||||
"play_top_tracks": "Play Top Tracks",
|
||||
"recently_added": "Ajoutés récemment",
|
||||
"recently_played": "Récemment joué",
|
||||
"frequently_played": "Fréquemment joué",
|
||||
"explore": "Explorez",
|
||||
"top_tracks": "Top chansons",
|
||||
"play": "Lecture",
|
||||
"shuffle": "Aléatoire",
|
||||
"play_top_tracks": "Jouer les pistes les plus populaires",
|
||||
"no_suggestions": "No suggestions available",
|
||||
"no_albums": "No albums found",
|
||||
"no_artists": "No artists found",
|
||||
|
||||
Reference in New Issue
Block a user