diff --git a/src/renderer/components/PlayerSettings/PlayerSettings.vue b/src/renderer/components/PlayerSettings/PlayerSettings.vue
index d7d10d51b1251..5a3dae3b23a87 100644
--- a/src/renderer/components/PlayerSettings/PlayerSettings.vue
+++ b/src/renderer/components/PlayerSettings/PlayerSettings.vue
@@ -102,6 +102,14 @@
:icon="['fas', 'photo-film']"
@change="updateDefaultQuality"
/>
+
{
+ if (index === 0) {
+ return t('Settings.Player Settings.Default Language.System Language')
+ }
+
+ return new Intl.DisplayNames('en', { type: 'language', languageDisplay: 'standard' }).of(language) ?? language
+})
+
+/** @type {import('vue').ComputedRef} */
+const defaultAudioLanguage = computed(() => store.getters.getDefaultAudioLanguage)
+
+/**
+ * @param {string} value
+ */
+function updateDefaultAudioLanguage(value) {
+ store.dispatch('updateDefaultAudioLanguage', value)
+}
+
const PLAYBACK_RATE_INTERVAL_VALUES = ['0.1', '0.25', '0.5', '1']
/** @type {import('vue').ComputedRef} */
diff --git a/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js b/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js
index acf452532b528..6e15dadb78b68 100644
--- a/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js
+++ b/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js
@@ -290,6 +290,33 @@ export default defineComponent({
return parseInt(value)
})
+ /** @type {import('vue').ComputedRef} */
+ const defaultAudioLanguage = computed(() => store.getters.getDefaultAudioLanguage)
+
+ /** @type {import('vue').ComputedRef} */
+ const preferredAudioLanguage = computed(() => {
+ return defaultAudioLanguage.value === 'system' ? locale.value : defaultAudioLanguage.value
+ })
+
+ /**
+ * Filters the given variants to the user's preferred audio language, falling back to the main audio track.
+ * @param {shaka.extern.Variant[]} variants
+ * @returns {shaka.extern.Variant[]}
+ */
+ function filterVariantsByPreferredLanguage(variants) {
+ const languageVariants = variants.filter((variant) => {
+ return shaka.util.LanguageUtils.areLanguageCompatible(preferredAudioLanguage.value, variant.language)
+ })
+
+ if (languageVariants.length > 0) {
+ const mainVariants = languageVariants.filter(variant => variant.audioRoles.includes('main'))
+ return mainVariants.length > 0 ? mainVariants : languageVariants
+ }
+
+ const mainVariants = variants.filter(variant => variant.audioRoles.includes('main'))
+ return mainVariants.length > 0 ? mainVariants : variants
+ }
+
/** @type {import('vue').ComputedRef} */
const enterFullscreenOnDisplayRotate = computed(() => {
return store.getters.getEnterFullscreenOnDisplayRotate
@@ -634,7 +661,11 @@ export default defineComponent({
// Electron doesn't like YouTube's vp9 VR video streams and throws:
// "CHUNK_DEMUXER_ERROR_APPEND_FAILED: Projection element is incomplete; ProjectionPoseYaw required."
// So use the AV1 and h264 codecs instead which it doesn't reject
- preferredVideoCodecs: typeof props.vrProjection === 'string' ? ['av01', 'avc1'] : []
+ preferredVideoCodecs: typeof props.vrProjection === 'string' ? ['av01', 'avc1'] : [],
+
+ // The default language to prefer for the audio track.
+ // 'system' uses FreeTube's active locale, which shaka-player matches by prefix.
+ preferredAudioLanguage: preferredAudioLanguage.value
}
}
@@ -1462,12 +1493,7 @@ export default defineComponent({
if (label) {
variants = variants.filter(variant => variant.label === label)
} else if (hasMultipleAudioTracks.value) {
- // default audio track
- const filteredVariants = variants.filter(variant => variant.audioRoles.includes('main'))
- // Sometimes there is nothing marked as main, don't filter in this case
- if (filteredVariants.length > 0) {
- variants = filteredVariants
- }
+ variants = filterVariantsByPreferredLanguage(variants)
}
const isPortrait = variants[0].height > variants[0].width
@@ -2906,8 +2932,7 @@ export default defineComponent({
let variants = player.getVariantTracks()
if (hasMultipleAudioTracks.value) {
- // default audio track
- variants = variants.filter(variant => variant.audioRoles.includes('main'))
+ variants = filterVariantsByPreferredLanguage(variants)
}
const highestBandwidth = Math.max(...variants.map(variant => variant.audioBandwidth))
@@ -3164,12 +3189,7 @@ export default defineComponent({
if (label) {
variants = variants.filter(variant => variant.label === label)
} else if (variants.length > 1) {
- // default audio track
- const filteredVariants = variants.filter(variant => variant.audioRoles.includes('main'))
- // Sometimes there is nothing marked as main, don't filter in this case
- if (filteredVariants.length > 0) {
- variants = filteredVariants
- }
+ variants = filterVariantsByPreferredLanguage(variants)
}
let chosenVariant
diff --git a/src/renderer/store/modules/settings.js b/src/renderer/store/modules/settings.js
index 694be8ff0be32..996c65932d19a 100644
--- a/src/renderer/store/modules/settings.js
+++ b/src/renderer/store/modules/settings.js
@@ -168,6 +168,7 @@ const state = {
mainColor: 'Red',
secColor: 'Blue',
defaultAutoplayInterruptionIntervalHours: 3,
+ defaultAudioLanguage: 'system',
defaultCaptionSettings: '{}',
defaultInterval: 5,
defaultPlayback: 1,
diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml
index 6dafdfcf6bbb3..93cb7e497dbf3 100644
--- a/static/locales/en-US.yaml
+++ b/static/locales/en-US.yaml
@@ -479,6 +479,9 @@ Settings:
1440p: 1440p
4k: 4k
8k: 8k
+ Default Language:
+ Default Language: Default Audio Language
+ System Language: Use System Locale
Screenshot:
Enable: Enable Screenshot
Mode: Screenshot Mode