From 161ea539f07d78a66b70b0e54853816259a54a44 Mon Sep 17 00:00:00 2001
From: WardPearce
Date: Wed, 18 Feb 2026 04:17:19 +1300
Subject: [PATCH] Started to seperate player out to more components
---
.../src/lib/components/player/Player.svelte | 458 +-----------------
.../components/player/settings/Airplay.svelte | 29 ++
.../player/settings/CaptionSettings.svelte | 41 ++
.../player/settings/FullscreenToggle.svelte | 16 +
.../lib/components/player/settings/Pip.svelte | 14 +
.../player/settings/Settings.svelte | 186 +++++++
materialious/src/lib/css/global.css | 6 +
materialious/src/lib/player/android.ts | 95 ++++
materialious/src/lib/player/index.ts | 91 ++++
9 files changed, 502 insertions(+), 434 deletions(-)
create mode 100644 materialious/src/lib/components/player/settings/Airplay.svelte
create mode 100644 materialious/src/lib/components/player/settings/CaptionSettings.svelte
create mode 100644 materialious/src/lib/components/player/settings/FullscreenToggle.svelte
create mode 100644 materialious/src/lib/components/player/settings/Pip.svelte
create mode 100644 materialious/src/lib/components/player/settings/Settings.svelte
create mode 100644 materialious/src/lib/player/android.ts
diff --git a/materialious/src/lib/components/player/Player.svelte b/materialious/src/lib/components/player/Player.svelte
index 5355a102..afe7d5b3 100644
--- a/materialious/src/lib/components/player/Player.svelte
+++ b/materialious/src/lib/components/player/Player.svelte
@@ -3,11 +3,8 @@
import { getBestThumbnail, ImageCache } from '$lib/images';
import { videoLength } from '$lib/numbers';
import { generateChapterWebVTT, type ParsedDescription, type Timestamp } from '$lib/description';
- import { SystemBars, SystemBarsStyle, SystemBarType } from '@capacitor/core';
import { Capacitor } from '@capacitor/core';
- import { ScreenOrientation, type ScreenOrientationResult } from '@capacitor/screen-orientation';
import { error, type Page } from '@sveltejs/kit';
- import ISO6391 from 'iso-639-1';
import Mousetrap from 'mousetrap';
import { CapacitorMusicControls } from 'capacitor-music-controls-plugin';
import shaka from 'shaka-player/dist/shaka-player.ui';
@@ -23,13 +20,10 @@
invidiousInstanceStore,
isAndroidTvStore,
playerAlwaysLoopStore,
- playerAndroidLockOrientation,
- playerAndroidPauseOnNetworkChange,
playerAutoPlayStore,
playerCCByDefault,
playerDefaultLanguage,
playerDefaultPlaybackSpeed,
- playerDefaultQualityStore,
playerPreferredVolumeStore,
playerProxyVideosStore,
playerSavePlaybackPositionStore,
@@ -49,14 +43,14 @@
import {
goToNextVideo,
goToPreviousVideo,
- playbackRates,
- playerDoubleTapSeek
+ playerDoubleTapSeek,
+ restoreDefaultLanguage,
+ restoreQualityPreference,
+ toggleSubtitles
} from '$lib/player/index';
import { manifestDomainInclusion } from '$lib/player/manifest';
import { injectSabr } from '$lib/player/sabr';
import type { SabrStreamingAdapter } from 'googlevideo/sabr-streaming-adapter';
- import { SvelteSet } from 'svelte/reactivity';
- import { Network, type ConnectionStatus } from '@capacitor/network';
import { fade } from 'svelte/transition';
import { addToast } from '$lib/components/Toast.svelte';
import { getPublicEnv, isMobile, isUnrestrictedPlatform, isYTBackend, truncate } from '$lib/misc';
@@ -67,6 +61,12 @@
type TimelineThumbnail
} from '$lib/player/thumbnails';
import { isOwnBackend } from '$lib/shared';
+ import Settings from './settings/Settings.svelte';
+ import CaptionSettings from './settings/CaptionSettings.svelte';
+ import Airplay from './settings/Airplay.svelte';
+ import Pip from './settings/Pip.svelte';
+ import FullscreenToggle from './settings/FullscreenToggle.svelte';
+ import { AndroidPlayer } from '$lib/player/android';
interface Props {
data: { video: VideoPlay; content: ParsedDescription; playlistId: string | null };
@@ -99,11 +99,10 @@
music_offtopic: $_('layout.sponsors.musicOffTopic')
};
- let originalOrigination: ScreenOrientationResult | undefined;
let watchProgressInterval: ReturnType;
let showVideoRetry = $state(false);
- let androidInitialNetworkStatus: ConnectionStatus | undefined;
+ let androidPlayer: AndroidPlayer;
let player: shaka.Player;
let sabrAdapter: SabrStreamingAdapter | null;
@@ -114,11 +113,6 @@
let playerMaxKnownTime = $state(data.video.lengthSeconds);
let playerIsBuffering = $state(true);
let playerVolume = $state($playerPreferredVolumeStore);
- let playerSettings: 'quality' | 'speed' | 'language' | 'root' = $state('root');
- let playerTextTracks: shaka.extern.TextTrack[] | undefined = $state(undefined);
- let playerCurrentVideoTrack: shaka.extern.VideoTrack | undefined = $state(undefined);
- let playerCurrentAudioTrack: shaka.extern.AudioTrack | undefined = $state(undefined);
- let playerLoop = $state($playerAlwaysLoopStore);
let playerTimelineTimeHover: number = $state(0);
let playerVideoEndTimePretty: string = $state('');
let playerBufferedTo: number = $state(0);
@@ -206,43 +200,6 @@
return `${percent}%`;
}
- function restoreDefaultLanguage() {
- if (!$playerDefaultLanguage || $playerDefaultLanguage === 'original') {
- const languageAndRole = player
- .getAudioLanguagesAndRoles()
- .find(({ role }) => role === 'main');
- if (languageAndRole !== undefined) {
- player.selectAudioLanguage(languageAndRole.language);
- return;
- }
- } else if ($playerDefaultLanguage) {
- const audioLanguages = player.getAudioLanguages();
- const langCode = ISO6391.getCode($playerDefaultLanguage);
-
- for (const audioLanguage of audioLanguages) {
- if (audioLanguage.startsWith(langCode)) {
- player.selectAudioLanguage(audioLanguage);
- break;
- }
- }
- }
- }
-
- function filterUniqueAudioTracks(tracks: shaka.extern.AudioTrack[]): shaka.extern.AudioTrack[] {
- const uniqueTracks: shaka.extern.AudioTrack[] = [];
- const seen = new SvelteSet();
-
- for (const track of tracks) {
- const identifier = `${track.language}-${track.label || 'No Label'}`;
- if (!seen.has(identifier)) {
- seen.add(identifier);
- uniqueTracks.push(track);
- }
- }
-
- return uniqueTracks;
- }
-
function saveVolumePreference() {
if (!playerElement) return;
playerVolume = playerElement.volume;
@@ -255,93 +212,6 @@
playerVolume = playerElement.volume;
}
- function restoreQualityPreference() {
- const numericValue = parseInt($playerDefaultQualityStore, 10);
-
- if (isNaN(numericValue)) {
- player.configure({ abr: { enabled: true } });
- return;
- }
-
- // Get video-only variant tracks
- const tracks = player.getVariantTracks().filter((t) => t.height !== null);
-
- // Sort by resolution descending
- const sortedTracks = tracks.sort((a, b) => (b.height as number) - (a.height as number));
-
- // Try exact match
- let selectedTrack = sortedTracks.find((t) => t.height === numericValue);
-
- // Try next best (lower than target)
- if (!selectedTrack) {
- selectedTrack = sortedTracks.find((t) => (t.height as number) < numericValue);
- }
-
- // Try next higher
- if (!selectedTrack) {
- selectedTrack = sortedTracks.find((t) => (t.height as number) > numericValue);
- }
-
- if (selectedTrack) {
- player.configure({ abr: { enabled: false } });
- player.selectVariantTrack(selectedTrack, true);
- } else {
- player.configure({ abr: { enabled: true } });
- }
- }
-
- async function onAndroidFullscreenChange() {
- const videoFormats = data.video.adaptiveFormats.filter((format) =>
- format.type.startsWith('video/')
- );
-
- const isFullScreen = !!document.fullscreenElement;
-
- if (isFullScreen) {
- // Ensure bar color is black while in fullscreen
- await SystemBars.setStyle({ style: SystemBarsStyle.Light });
- await SystemBars.hide({
- bar: SystemBarType.NavigationBar
- });
- await SystemBars.hide({
- bar: SystemBarType.StatusBar
- });
- } else {
- await setStatusBarColor();
- }
-
- if (!$playerAndroidLockOrientation) return;
-
- if (isFullScreen && videoFormats[0].resolution) {
- const widthHeight = videoFormats[0].resolution.split('x');
-
- if (widthHeight.length !== 2) return;
-
- if (Number(widthHeight[0]) > Number(widthHeight[1])) {
- await ScreenOrientation.lock({ orientation: 'landscape' });
- } else {
- await ScreenOrientation.lock({ orientation: 'portrait' });
- }
- } else {
- await ScreenOrientation.lock({
- orientation: (originalOrigination as ScreenOrientationResult).type
- });
- }
- }
-
- async function androidHandleRotate() {
- if (
- Capacitor.getPlatform() !== 'android' ||
- data.video.adaptiveFormats.length === 0 ||
- $isAndroidTvStore
- )
- return;
-
- originalOrigination = await ScreenOrientation.orientation();
-
- document.addEventListener('fullscreenchange', onAndroidFullscreenChange);
- }
-
function skipSegment(segment: Segment) {
if (!playerElement) return;
@@ -423,50 +293,8 @@
return false;
}
- function toggleSubtitles() {
- const isVisible = player.isTextTrackVisible();
- if (isVisible) {
- player.setTextTrackVisibility(false);
- } else {
- let langCode: string;
- if ($playerDefaultLanguage === 'original') {
- const languageAndRole = player
- .getAudioLanguagesAndRoles()
- .find(({ role }) => role === 'main');
-
- if (!languageAndRole) {
- return;
- }
-
- langCode = languageAndRole.language;
- } else {
- const defaultLanguage = get(playerDefaultLanguage);
- langCode = ISO6391.getCode(defaultLanguage);
- }
-
- const tracks = player.getTextTracks();
-
- const subtitleTrack = tracks.find((track) => track.language === langCode);
-
- if (subtitleTrack) {
- player.selectTextTrack(subtitleTrack);
- player.setTextTrackVisibility(true);
- }
- }
- }
-
page.subscribe((pageUpdate) => loadTimeFromUrl(pageUpdate));
- function setActiveVideoTrack() {
- const videoTracks = player.getVideoTracks();
- playerCurrentVideoTrack = videoTracks.find((track) => track.active);
- }
-
- function setActiveAudioTrack() {
- const audioTracks = player.getAudioTracks();
- playerCurrentAudioTrack = audioTracks.find((track) => track.active);
- }
-
function toggleFullscreen() {
if (document.fullscreenElement) {
document.exitFullscreen();
@@ -491,14 +319,12 @@
setupSponsorSkip();
player.addEventListener('loaded', () => {
- restoreQualityPreference();
- restoreDefaultLanguage();
- setActiveVideoTrack();
- setActiveAudioTrack();
+ restoreQualityPreference(player);
+ restoreDefaultLanguage(player);
updateVideoPlayerHeight();
if ($playerCCByDefault) {
- toggleSubtitles();
+ toggleSubtitles(player);
}
});
@@ -696,12 +522,7 @@
});
playerElement = document.getElementById('player') as HTMLMediaElement;
- // Enable AirPlay if supported
- if (hasWebkitShowPlaybackTargetPicker(playerElement)) {
- playerElement.setAttribute('x-webkit-airplay', 'allow');
- }
-
- playerElement.loop = playerLoop;
+ playerElement.loop = $playerAlwaysLoopStore;
if ($playerState) {
playerState.set({ ...$playerState, playerElement: playerElement });
@@ -715,13 +536,14 @@
window.addEventListener('resize', updateVideoPlayerHeight);
updateVideoPlayerHeight();
+ androidPlayer = new AndroidPlayer(data.video, playerElement);
+
await player.attach(playerElement);
player?.addEventListener('error', (event) => {
const error = (event as CustomEvent).detail as shaka.util.Error;
console.error('Player error:', error);
});
-
player.getNetworkingEngine()?.registerResponseFilter((type, response) => {
if (
type !== shaka.net.NetworkingEngine.RequestType.SEGMENT ||
@@ -750,20 +572,7 @@
playerElement?.addEventListener('volumechange', saveVolumePreference);
- await androidHandleRotate();
-
if (Capacitor.getPlatform() === 'android') {
- androidInitialNetworkStatus = await Network.getStatus();
-
- Network.addListener('networkStatusChange', (networkStatus) => {
- if (
- androidInitialNetworkStatus?.connectionType !== networkStatus.connectionType &&
- $playerAndroidPauseOnNetworkChange
- ) {
- playerElement?.pause();
- }
- });
-
await CapacitorMusicControls.create({
track: data.video.title,
artist: data.video.author,
@@ -914,7 +723,7 @@
}
Mousetrap.bind('c', () => {
- toggleSubtitles();
+ toggleSubtitles(player);
showPlayerUI();
return false;
});
@@ -1040,22 +849,8 @@
// Update video player height again on video loaded.
updateVideoPlayerHeight();
-
- playerTextTracks = player.getTextTracks();
});
- function hasWebkitShowPlaybackTargetPicker(
- el: HTMLMediaElement
- ): el is HTMLMediaElement & { webkitShowPlaybackTargetPicker: () => void } {
- return typeof (el as any).webkitShowPlaybackTargetPicker === 'function';
- }
-
- function handleAirPlayClick() {
- if (playerElement && hasWebkitShowPlaybackTargetPicker(playerElement)) {
- playerElement.webkitShowPlaybackTargetPicker();
- }
- }
-
async function getLastPlayPos(): Promise {
if (loadTimeFromUrl($page) || !$playerSavePlaybackPositionStore) return 0;
@@ -1252,21 +1047,9 @@
onDestroy(async () => {
if (Capacitor.getPlatform() === 'android') {
- if (!$isAndroidTvStore) {
- document.removeEventListener('fullscreenchange', onAndroidFullscreenChange);
-
- if (originalOrigination) {
- await ScreenOrientation.lock({
- orientation: originalOrigination.type
- });
- }
- }
-
- await Network.removeAllListeners();
-
await setStatusBarColor();
-
await CapacitorMusicControls.destroy();
+ await androidPlayer.destroy();
}
try {
@@ -1467,194 +1250,11 @@
{/if}
{#if !$isAndroidTvStore}
- {#if playerTextTracks && playerTextTracks.length > 0 && !data.video.liveNow}
-
- {/if}
-
- {#if playerElement && hasWebkitShowPlaybackTargetPicker(playerElement)}
-
- {/if}
- {#if document.pictureInPictureEnabled}
-
- {/if}
-
+
+
+
+
+
{/if}
@@ -1700,10 +1300,6 @@
padding: 10px;
}
- #player-controls span {
- clip-path: none;
- }
-
#player-tap-controls-area {
touch-action: manipulation;
}
@@ -1835,12 +1431,6 @@
cursor: not-allowed;
}
- menu.mobile {
- transform: scale(1) translateY(-40%) translateX(0);
- width: 300px;
- height: 200px;
- }
-
.contain-video {
max-height: 80vh;
max-width: calc(80vh * 16 / 9);
diff --git a/materialious/src/lib/components/player/settings/Airplay.svelte b/materialious/src/lib/components/player/settings/Airplay.svelte
new file mode 100644
index 00000000..def8e0ea
--- /dev/null
+++ b/materialious/src/lib/components/player/settings/Airplay.svelte
@@ -0,0 +1,29 @@
+
+
+{#if playerElement && hasWebkitShowPlaybackTargetPicker(playerElement)}
+
+{/if}
diff --git a/materialious/src/lib/components/player/settings/CaptionSettings.svelte b/materialious/src/lib/components/player/settings/CaptionSettings.svelte
new file mode 100644
index 00000000..512cc532
--- /dev/null
+++ b/materialious/src/lib/components/player/settings/CaptionSettings.svelte
@@ -0,0 +1,41 @@
+
+
+{#if playerTextTracks && playerTextTracks.length > 0 && !video.liveNow}
+
+{/if}
diff --git a/materialious/src/lib/components/player/settings/FullscreenToggle.svelte b/materialious/src/lib/components/player/settings/FullscreenToggle.svelte
new file mode 100644
index 00000000..d4c65578
--- /dev/null
+++ b/materialious/src/lib/components/player/settings/FullscreenToggle.svelte
@@ -0,0 +1,16 @@
+
+
+
diff --git a/materialious/src/lib/components/player/settings/Pip.svelte b/materialious/src/lib/components/player/settings/Pip.svelte
new file mode 100644
index 00000000..805c628a
--- /dev/null
+++ b/materialious/src/lib/components/player/settings/Pip.svelte
@@ -0,0 +1,14 @@
+
+
+{#if document.pictureInPictureEnabled}
+
+{/if}
diff --git a/materialious/src/lib/components/player/settings/Settings.svelte b/materialious/src/lib/components/player/settings/Settings.svelte
new file mode 100644
index 00000000..98425d7b
--- /dev/null
+++ b/materialious/src/lib/components/player/settings/Settings.svelte
@@ -0,0 +1,186 @@
+
+
+
diff --git a/materialious/src/lib/css/global.css b/materialious/src/lib/css/global.css
index 16f69699..b6ffae70 100644
--- a/materialious/src/lib/css/global.css
+++ b/materialious/src/lib/css/global.css
@@ -133,6 +133,12 @@ menu {
border: none !important;
}
+menu.player-settings {
+ transform: scale(1) translateY(-40%) translateX(0) !important;
+ width: 300px !important;
+ height: 200px !important;
+}
+
@media screen and (max-width: 1000px) {
menu.mobile {
position: fixed !important;
diff --git a/materialious/src/lib/player/android.ts b/materialious/src/lib/player/android.ts
new file mode 100644
index 00000000..70587c9f
--- /dev/null
+++ b/materialious/src/lib/player/android.ts
@@ -0,0 +1,95 @@
+import type { VideoPlay } from '$lib/api/model';
+import { playerAndroidLockOrientation, playerAndroidPauseOnNetworkChange } from '$lib/store';
+import { setStatusBarColor } from '$lib/theme';
+import { get } from 'svelte/store';
+import { Capacitor, SystemBars, SystemBarsStyle, SystemBarType } from '@capacitor/core';
+import { ScreenOrientation, type ScreenOrientationResult } from '@capacitor/screen-orientation';
+import { Network, type ConnectionStatus } from '@capacitor/network';
+
+export class AndroidPlayer {
+ video: VideoPlay;
+ originalOrigination: ScreenOrientationResult | undefined;
+ initialNetworkStatus: ConnectionStatus | undefined;
+ playerElement: HTMLMediaElement | undefined;
+
+ constructor(video: VideoPlay, playerElement: HTMLMediaElement | undefined) {
+ this.video = video;
+ this.playerElement = playerElement;
+
+ if (Capacitor.getPlatform() !== 'android') return;
+
+ ScreenOrientation.orientation().then((originalOrigination) => {
+ this.originalOrigination = originalOrigination;
+ });
+ Network.getStatus().then((initialNetworkStatus) => {
+ this.initialNetworkStatus = initialNetworkStatus;
+ });
+
+ document.addEventListener('fullscreenchange', this.onFullscreenChange);
+
+ Network.addListener('networkStatusChange', (networkStatus) => {
+ if (
+ this.initialNetworkStatus?.connectionType !== networkStatus.connectionType &&
+ get(playerAndroidPauseOnNetworkChange)
+ ) {
+ this.playerElement?.pause();
+ }
+ });
+ }
+
+ async destroy() {
+ if (Capacitor.getPlatform() !== 'android') return;
+
+ document.removeEventListener('fullscreenchange', this.onFullscreenChange);
+
+ if (this.originalOrigination) {
+ await ScreenOrientation.lock({
+ orientation: this.originalOrigination.type
+ });
+ }
+
+ await Network.removeAllListeners();
+ }
+
+ async onFullscreenChange() {
+ if (Capacitor.getPlatform() !== 'android') return;
+
+ const videoFormats = this.video.adaptiveFormats.filter((format) =>
+ format.type.startsWith('video/')
+ );
+
+ const isFullScreen = !!document.fullscreenElement;
+
+ if (isFullScreen) {
+ // Ensure bar color is black while in fullscreen
+ await SystemBars.setStyle({ style: SystemBarsStyle.Light });
+ await SystemBars.hide({
+ bar: SystemBarType.NavigationBar
+ });
+ await SystemBars.hide({
+ bar: SystemBarType.StatusBar
+ });
+ } else {
+ await setStatusBarColor();
+ }
+
+ if (!get(playerAndroidLockOrientation)) return;
+
+ if (isFullScreen && videoFormats[0].resolution) {
+ const widthHeight = videoFormats[0].resolution.split('x');
+
+ if (widthHeight.length !== 2) return;
+
+ if (Number(widthHeight[0]) > Number(widthHeight[1])) {
+ await ScreenOrientation.lock({ orientation: 'landscape' });
+ } else {
+ await ScreenOrientation.lock({ orientation: 'portrait' });
+ }
+ } else {
+ if (this.originalOrigination)
+ await ScreenOrientation.lock({
+ orientation: this.originalOrigination.type
+ });
+ }
+ }
+}
diff --git a/materialious/src/lib/player/index.ts b/materialious/src/lib/player/index.ts
index de96dc57..4dc41653 100644
--- a/materialious/src/lib/player/index.ts
+++ b/materialious/src/lib/player/index.ts
@@ -3,6 +3,8 @@ import type { PlaylistPageVideo, VideoPlay } from '$lib/api/model';
import {
isAndroidTvStore,
playerAutoplayNextByDefaultStore,
+ playerDefaultLanguage,
+ playerDefaultQualityStore,
playerPlaylistHistory,
playlistSettingsStore,
syncPartyConnectionsStore
@@ -11,6 +13,8 @@ import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { loadEntirePlaylist } from '$lib/playlist';
import { unsafeRandomItem } from '$lib/misc';
+import type shaka from 'shaka-player/dist/shaka-player.ui';
+import ISO6391 from 'iso-639-1';
export interface PlayerEvent {
type: 'pause' | 'seek' | 'change-video' | 'play' | 'playlist' | 'goto';
@@ -104,3 +108,90 @@ export async function goToNextVideo(video: VideoPlay, playlistId: string | null)
);
}
}
+
+export function restoreQualityPreference(player: shaka.Player) {
+ const numericValue = parseInt(get(playerDefaultQualityStore), 10);
+
+ if (isNaN(numericValue)) {
+ player.configure({ abr: { enabled: true } });
+ return;
+ }
+
+ // Get video-only variant tracks
+ const tracks = player.getVariantTracks().filter((t) => t.height !== null);
+
+ // Sort by resolution descending
+ const sortedTracks = tracks.sort((a, b) => (b.height as number) - (a.height as number));
+
+ // Try exact match
+ let selectedTrack = sortedTracks.find((t) => t.height === numericValue);
+
+ // Try next best (lower than target)
+ if (!selectedTrack) {
+ selectedTrack = sortedTracks.find((t) => (t.height as number) < numericValue);
+ }
+
+ // Try next higher
+ if (!selectedTrack) {
+ selectedTrack = sortedTracks.find((t) => (t.height as number) > numericValue);
+ }
+
+ if (selectedTrack) {
+ player.configure({ abr: { enabled: false } });
+ player.selectVariantTrack(selectedTrack, true);
+ } else {
+ player.configure({ abr: { enabled: true } });
+ }
+}
+
+export function restoreDefaultLanguage(player: shaka.Player) {
+ if (!get(playerDefaultLanguage) || get(playerDefaultLanguage) === 'original') {
+ const languageAndRole = player.getAudioLanguagesAndRoles().find(({ role }) => role === 'main');
+ if (languageAndRole !== undefined) {
+ player.selectAudioLanguage(languageAndRole.language);
+ return;
+ }
+ } else if (get(playerDefaultLanguage)) {
+ const audioLanguages = player.getAudioLanguages();
+ const langCode = ISO6391.getCode(get(playerDefaultLanguage));
+
+ for (const audioLanguage of audioLanguages) {
+ if (audioLanguage.startsWith(langCode)) {
+ player.selectAudioLanguage(audioLanguage);
+ break;
+ }
+ }
+ }
+}
+
+export function toggleSubtitles(player: shaka.Player) {
+ const isVisible = player.isTextTrackVisible();
+ if (isVisible) {
+ player.setTextTrackVisibility(false);
+ } else {
+ let langCode: string;
+ if (get(playerDefaultLanguage) === 'original') {
+ const languageAndRole = player
+ .getAudioLanguagesAndRoles()
+ .find(({ role }) => role === 'main');
+
+ if (!languageAndRole) {
+ return;
+ }
+
+ langCode = languageAndRole.language;
+ } else {
+ const defaultLanguage = get(playerDefaultLanguage);
+ langCode = ISO6391.getCode(defaultLanguage);
+ }
+
+ const tracks = player.getTextTracks();
+
+ const subtitleTrack = tracks.find((track) => track.language === langCode);
+
+ if (subtitleTrack) {
+ player.selectTextTrack(subtitleTrack);
+ player.setTextTrackVisibility(true);
+ }
+ }
+}