diff --git a/package-lock.json b/package-lock.json
index 7bd2bd6ea..1f87bbd55 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -36,7 +36,7 @@
"react-i18next": "^15.1.3",
"react-is": "18.3.1",
"spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
- "stremio-translations": "github:Stremio/stremio-translations#f666d9a97cafa5aa150878b5c51a2896b5f4f1b2",
+ "stremio-translations": "github:Stremio/stremio-translations#a0f50634202f748a57907b645d2cd92fbaa479dd",
"url": "0.11.4",
"use-long-press": "^3.2.0"
},
@@ -13372,8 +13372,9 @@
},
"node_modules/stremio-translations": {
"version": "1.44.9",
- "resolved": "git+ssh://git@github.com/Stremio/stremio-translations.git#f666d9a97cafa5aa150878b5c51a2896b5f4f1b2",
- "integrity": "sha512-SzaIGUMqQuMAq58sI9L/RKSs5O4eF8VKPMqnWFddBSg/tZOU9xuNYqjRPKT07cp8MRfzzGQmCKMByozTYfjdIA=="
+ "resolved": "git+ssh://git@github.com/Stremio/stremio-translations.git#a0f50634202f748a57907b645d2cd92fbaa479dd",
+ "integrity": "sha512-JJpd1JJet3T6/VTNdZ2NZ7uvHJ4zkuyqo5BnTcDGqLVNO/OpicGqKhZjE4WGSgmuhsfPBU8T0ICCfzKu2xpvKg==",
+ "license": "MIT"
},
"node_modules/string_decoder": {
"version": "1.1.1",
diff --git a/package.json b/package.json
index d1709095e..8a4665615 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
"react-i18next": "^15.1.3",
"react-is": "18.3.1",
"spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
- "stremio-translations": "github:Stremio/stremio-translations#f666d9a97cafa5aa150878b5c51a2896b5f4f1b2",
+ "stremio-translations": "github:Stremio/stremio-translations#a0f50634202f748a57907b645d2cd92fbaa479dd",
"url": "0.11.4",
"use-long-press": "^3.2.0"
},
diff --git a/src/routes/Player/AudioMenu/AudioMenu.less b/src/routes/Player/AudioMenu/AudioMenu.less
new file mode 100644
index 000000000..1887a1e7c
--- /dev/null
+++ b/src/routes/Player/AudioMenu/AudioMenu.less
@@ -0,0 +1,60 @@
+.audio-menu {
+ display: flex;
+ flex-direction: row;
+
+ .container {
+ flex: none;
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ width: 16rem;
+
+ .header {
+ flex: none;
+ align-self: stretch;
+ padding: 1.5rem 2rem;
+ font-weight: 700;
+ color: var(--primary-foreground-color);
+ }
+
+ .list {
+ flex: 1;
+ align-self: stretch;
+ overflow-y: auto;
+ padding: 0 1rem;
+ padding-bottom: 0.5rem;
+
+ .option {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ height: 3.5rem;
+ padding: 0 1.5rem;
+ margin-bottom: 0.5rem;
+ border-radius: var(--border-radius);
+
+ &:global(.selected), &:hover {
+ background-color: var(--overlay-color);
+ }
+
+ .label {
+ flex: 1;
+ max-height: 2.4em;
+ font-size: 1.1rem;
+ color: var(--primary-foreground-color);
+ text-wrap: nowrap;
+ text-overflow: ellipsis;
+ }
+
+ .icon {
+ flex: none;
+ width: 0.5rem;
+ height: 0.5rem;
+ border-radius: 100%;
+ margin-left: 1rem;
+ background-color: var(--secondary-accent-color);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/routes/Player/AudioMenu/AudioMenu.tsx b/src/routes/Player/AudioMenu/AudioMenu.tsx
new file mode 100644
index 000000000..71bdb68ed
--- /dev/null
+++ b/src/routes/Player/AudioMenu/AudioMenu.tsx
@@ -0,0 +1,66 @@
+import React, { MouseEvent, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+import classNames from 'classnames';
+import { Button, languageNames } from 'stremio/common';
+import styles from './AudioMenu.less';
+
+type Props = {
+ className: string,
+ selectedAudioTrackId: string | null,
+ audioTracks: AudioTrack[],
+ onAudioTrackSelected: (id: string) => void,
+};
+
+const AudioMenu = ({ className, selectedAudioTrackId, audioTracks, onAudioTrackSelected }: Props) => {
+ const { t } = useTranslation();
+
+ const onAudioTrackClick = useCallback(({ currentTarget }: MouseEvent) => {
+ const id = currentTarget.getAttribute('data-id')!;
+ onAudioTrackSelected && onAudioTrackSelected(id);
+ }, [onAudioTrackSelected]);
+
+ const onMouseDown = (event: MouseEvent) => {
+ // @ts-expect-error: Property 'audioMenuClosePrevented' does not exist on type 'MouseEvent'.
+ event.nativeEvent.audioMenuClosePrevented = true;
+ };
+
+ return (
+
+
+
+ { t('AUDIO_TRACKS') }
+
+
+ {
+ audioTracks.map(({ id, label, lang }, index) => (
+
+ ))
+ }
+
+
+
+ );
+};
+
+export default AudioMenu;
diff --git a/src/routes/Player/AudioMenu/index.ts b/src/routes/Player/AudioMenu/index.ts
new file mode 100644
index 000000000..265490872
--- /dev/null
+++ b/src/routes/Player/AudioMenu/index.ts
@@ -0,0 +1,2 @@
+import AudioMenu from './AudioMenu';
+export default AudioMenu;
diff --git a/src/routes/Player/ControlBar/ControlBar.js b/src/routes/Player/ControlBar/ControlBar.js
index 1d1330539..a5fe1adf9 100644
--- a/src/routes/Player/ControlBar/ControlBar.js
+++ b/src/routes/Player/ControlBar/ControlBar.js
@@ -35,6 +35,7 @@ const ControlBar = ({
onVolumeChangeRequested,
onSeekRequested,
onToggleSubtitlesMenu,
+ onToggleAudioMenu,
onToggleSpeedMenu,
onToggleSideDrawer,
onToggleOptionsMenu,
@@ -47,6 +48,9 @@ const ControlBar = ({
const onSubtitlesButtonMouseDown = React.useCallback((event) => {
event.nativeEvent.subtitlesMenuClosePrevented = true;
}, []);
+ const onAudioButtonMouseDown = React.useCallback((event) => {
+ event.nativeEvent.audioMenuClosePrevented = true;
+ }, []);
const onSpeedButtonMouseDown = React.useCallback((event) => {
event.nativeEvent.speedMenuClosePrevented = true;
}, []);
@@ -150,9 +154,12 @@ const ControlBar = ({
-