LG Webos - wrong audio track

Server Version#: 1.43.2.10687
Player Version#: 5.94.1

Hello,

I have a problem with audio track selection. This problem occurs only on WebOs, when I use iPad app, all works perfectly.

I have a file with French audio track and English audio track. When I select French the audio I hear is English and vice versa.

Is there anything I can do to have correct audio track selection?

Thx in advance

Hello,

I’m having exactly the same problem on WebOS as well.

Best regards

I’m having the exact same issue since a few days.

I found that by disabling the support of EAC3 in the settings of Plex kind-of fix it, but it force the Plex server to transcode the EAC3 to AAC, which is not ideal.

I don’t know how you have find this, but thx, it’s working before eventually a patch.

Thanks for reply :victory_hand:

Well.. the latest version for Plex on my TV is from 2024, I don’t expect a patch anytime soon, unless it can be fix server-side.

I found this by trying everything. I found that AC3 doesn’t have this issue, only EAC3

Edit : But it seems to break AAC audio. With that tricks, when there is an AAC and EAC3 audio, it will only plays the EAC3, French/English will both play English

Edit 2 : Disabling “allow direct play” in the settings seems to fix it more. I had issues qui some type of audio that would any play 1 language

I instrumented this end‑to‑end and found the actual root cause — in the webOS app’s own track‑selection code, down to the exact function. Sharing the full chain because it explains every symptom in this thread, including why it looks random across files.

Setup: rooted LG (2019, webOS 4.5). I attached Chrome DevTools to the live Plex app (it’s the app.plex.tv/tv-v5-webos web build running in webOS Chromium) and simultaneously tailed LG’s media‑pipeline log (starfish-media-pipeline), so I can see the exact track each layer selects.

Repro file: 2 audio tracks — #1 Russian AC3 5.1, #2 English E‑AC3 5.1 (Atmos) (an Amazon WEB‑DL). Direct Play. Selecting English plays Russian and vice‑versa.

Everything below the app is correct

LG’s pipeline maps track index → audio faithfully (Atmos flag as ground truth, since only the English track is Atmos):

SELECT_TRACK audio index 0  →  audio path: NONE   (Russian)
SELECT_TRACK audio index 1  →  audio path: ATMOS  (English)

The HTML audioTracks list is also correctly ordered: idx0 {language:"ru"}, idx1 {language:"en"}.

But with English selected in Plex’s menu, the app enables audioTracks[0] (Russian). Force‑enabling audioTracks[1] from the DevTools console makes the pipeline switch instantly (SELECT_TRACK index 1) and the correct language plays. So the player is innocent — the app hands it the wrong index.

The bug in the app code

HTMLPlayer._selectAudioTrack (in packages_app_components_player_HTMLPlayer_tsx.js) delegates to a webOS‑specific helper injected from getAppConfig_ts.js. Deobfuscated:

function getSelectedAudioStreamIndex(serverStreams, elementTracks) {
  // (filters truehd/dca/pcm when list lengths differ — not relevant here)
  const r = serverStreams.findIndex(s => s.selected);

  // sanity check: per-position language match, EXACT string equality
  const ok = serverStreams.every((s, i) => {
    const elemLang = elementTracks[i].language;
    if (!(s.languageCode || (elemLang && elemLang !== "und"))) return true;
    return s.languageCode === elemLang
        || (!!s.languageCode && (s.languageTag || isoMap[s.languageCode]) === elemLang);
  });

  return ok ? r : elementTracks.length - r - 1;   // ← check fails ⇒ MIRROR the index
}

If any position fails the language comparison, the app assumes the element’s track list is reversed (presumably a workaround for some old webOS firmware) and enables the mirrored entry.

My file’s English track carries the Matroska LanguageIETF tag en-US (typical of Amazon WEB‑DLs), so PMS reports languageTag: "en-US" — but the webOS media element normalizes audioTracks[i].language to "en". The exact‑equality check fails on that position, the helper takes the “reversed” branch, and with English selected (r = 1) it returns 2 − 1 − 1 = 0 → Russian. Selecting Russian returns 1 → English. A perfect swap, both ways.

I replayed this exact logic in the live page against the real server metadata: langCheckPassed: false, indexAppWouldEnable: 0. Matches the observed behavior bit‑for‑bit. (The helper even logs "Unexpected number of audio tracks available on media element" in a related path — searchable in the source.)

The real trigger (verified)

It is not about codecs. I tested three multi‑track files, all Direct Play:

File Audio Language tags (PMS languageTag vs element) Result
AC3 rus + E‑AC3 eng (Atmos) mixed ru/ru ✓, en-US/en ✗ swapped
AC3 rus + E‑AC3 eng (Atmos), different release mixed ru/ru ✓, en/en ✓ correct
AC3 rus + AC3 rus + E‑AC3 eng mixed all ✓ correct

Same codec mix, opposite outcomes — the only difference is the container’s IETF language tag. The trigger is: any Direct‑Played multi‑track file where one stream’s BCP‑47 tag doesn’t exactly equal the element’s normalized language (en-US vs en, regional variants, missing/und mismatches). That’s why this bug looks file‑dependent and random across release groups, and why it survives reinstalls and different PMS versions.

Why the known workarounds work

  • Disable “Allow Direct Play” / disable E‑AC3 (earlier in this thread): forces a transcode to a single audio stream — with one element track the helper is skipped entirely (tracks[0].enabled = true unconditionally). Same reason the desktop web client is unaffected.
  • Fixing the file’s tag also fixes playback: mkvpropedit file.mkv --edit track:a2 --set language-ietf=en (seconds, no remux) makes the check pass and the correct track play.

The ask

The fix is one line in the webOS client: compare primary subtags (languageTag.split("-")[0]) instead of exact strings — or better, drop the guess‑reversal heuristic and match element tracks by language directly. The audioStreamID sent to the server is already correct; only the local audioTracks.enabled mapping is mirrored. Relevant because the webOS app has been frozen for many of us, and en-US‑tagged WEB‑DLs are extremely common.

Happy to share the DevTools dumps, pipeline SELECT_TRACK logs, and PMS stream JSON if it helps repro.

Nice find! Let’s hope something from Plex staff can read that and fix it for the LG WebOS Plex Client!

Because the allow direct play is starting to create some otheer issues, like some Plex buffering and then crashing

With your investigation, do you know if the .js you deobfuscated is “built-in” the WebOS Plex client or is it downloaded from a remote server?

This could explain as to why, since the last update is from 2024, the behavior changed recently.

Ok, it’s the upgrade from 1.43.1.10611-1e34174b1 to 1.43.2.10687-563d026ea that break it.
Here is the test I made, I create a new Plex installation (new account, everything brand new), from 1.43.1

services:
  plex:
    image: plexinc/pms-docker:1.43.1.10611-1e34174b1
    container_name: plex
    network_mode: host
    pull_policy: if_not_present
    environment:
      - PLEX_UID=1000
      - PLEX_GID=1000
      - VERSION=latest
      - TZ=America/Montreal
      - PLEX_CLAIM="THE_CLAIN"
      - HOME=/config
      - CHANGE_CONFIG_DIR_OWNERSHIP=true
      - ADVERTISE_IP="http://192.168.1.xxx:32400/"
    volumes:
      - "/app/plex/config:/config"
      - "/app/plex/data:/data"
    tmpfs:
      - /transcode:size=8G,mode=1777,uid=1000,gid=1000
    restart: unless-stopped

I added one problematic TV Show.
Connected the same LG TV to that new Plex server. The audio was fine, it played in french, no issue.

I exited from the TV the Plex Application. Stopped Plex server, upgraded to 1.43.2.10687-563d026ea.
Relaunched the application on the LG, now the audio is in english instead of french.

I tried rollbacking that new installation to 1.43.1 and the issue remains. So it’s an update to the data in the database the cause the issue.

Ok I found it, the data is updated in the database from ‘fr’ to ‘fr-FR’.

The data in 1.43.1 :

The data in 1.43.2 :

So the WebOS is unable to found the right audio track.

From what I found, the WebOS Plex client is mostly not a fully native app. On LG WebOS, it’s essentially a web app running inside WebOS’s built-in browser runtime, which is basically an older Chromium-based environment.

The native part is fairly limited and mainly handles playback through LG’s native video player. The rest of the UI logic is basically the Plex web app, which can be loaded and updated remotely.

So yes, the deobfuscated JavaScript appears to be part of that web layer rather than something permanently baked into the native client binary. That would absolutely explain why the behavior changed recently even though the last store update was back in 2024.

In fact, you can open it from anywhere: https://app.plex.tv/tv-v5-webos#/

I hope so too. I tried to show them the exact place where it needs to be fixed, so it should really be just a couple of lines of code.

Ok so if it’s only a webview for an app, it should be pretty simple for them to fix that since it wouldn’t required updating the app itself

Same problem here! When I play a mkv with a TrueHD with Atmos track, Plex got all the audio tracks wrong, a strange thing for me it play the stereo from the French track (AAC) and the surround sound from the English track (TrueHD) very strange behaviour!

Really hope there will be a fix soon!

The last server update (beta) didn’t fix this bug

This is a web client bug, it cannot be fixed on the server

@BigWheel could you please take a look? It seems the fix is clear

Follow-up: a second, distinct webOS audio-selection bug — merged multi-version items

This is a different bug with a different trigger that lives in the same function area (HTMLPlayer._selectAudioTrack). I instrumented it end-to-end the same way — Chrome DevTools on the live app.plex.tv/tv-v5-webos build plus the Plex DB — and traced it to the exact call. The prior bug was about language tags; this one is about merged Versions, and it can short-circuit the language logic entirely.

Setup: LG 49SM9000PLA, webOS 4.10.0; PMS 1.43.2.10687; LG Plex app cdp-30 5.2.1. Repro content: Spider-Noir S01 as a merged multi-version item — each episode has two release files combined into one entry: a FLUX version (1 audio track, English) and a B&W version (2 audio tracks, Italian + English).

In plain English (for anyone hitting this)

If you have two copies of the same movie or episode in Plex and Plex has merged them into a single entry with multiple “Versions,” the audio picker can stop working on one of those copies. You open the audio menu, choose Italian, the menu shows Italian selected — but the sound stays English. It never actually changes.

Why it happens: when you play a merged item, the app needs a list of “which audio tracks this file has.” Because of a bug, it reads that list from the wrong copy — the first/default Version — instead of the copy you’re actually watching. The two copies have a different number of audio tracks (here 1 vs 2), the lists don’t line up, and the app gives up and just plays the default (English) track no matter what you pick.

It’s not your TV’s fault, and it’s not a sound-file problem. Single-copy items switch audio fine in both directions. It only happens on merged items.

Workaround

  1. Remove the extra version from the library so the item has only one Version.
  2. Disable “Allow Direct Play” (or lower max quality) so the server transcodes to a single audio track — the app can’t pick the wrong index. Same universal workaround as before.
For Plex devs — full technical detail

Trigger: a merged multi-version metadata_item whose played version has a different audio-stream count than version index 0. Test item: FLUX = 1 audio (en/eac3); B&W = 2 audio (it+en, native element order [en, it]).

Symptom: Direct-Playing the B&W (2-track) version, the picker is ignored — audio stays pinned to English:

Unexpected number of audio tracks available on media element. Found: 2, expected: 1

Proof the native API is fine: in DevTools, audioTracks[1].enabled = true on the <video> element switched the decoder to Italian instantly — no reload, no transcode. The native list [en, it] is correct; only the index the app computes is wrong.

Root cause. _selectAudioTrack (HTMLPlayer.tsx, module 29074) builds its server-stream list from the default version, not the playing one:

_selectAudioTrack() {
  const tracks = this.mediaElement?.audioTracks;          // native AudioTrackList — correct: [en, it]
  const playable = this._playable;
  if (!tracks?.length || !playable?.isDirectPlay
      || playable.playableType !== PlayableType.Video) return;

  // BUG: no media-version index passed → reads version 0's streams (the FLUX copy)
  const serverStreams = getStreamsOfType(playable.metadataItem, StreamType.Audio);   // = (0,f.bp)(...)
  if (!serverStreams.length) return;

  const elementTracks = [...tracks];
  if (elementTracks.length === 1) { elementTracks[0].enabled = true; return; }

  const idx = this._getSelectedAudioStreamIndex
    ? this._getSelectedAudioStreamIndex(serverStreams, elementTracks)
    : serverStreams.findIndex(s => s.selected);
  elementTracks.forEach((t, i) => { t.enabled = (i === idx); });
}

grep: var i=(0,f.bp)(n.metadataItem,m.jx.Audio);

f.bp (f = n(90606)) is the shared helper D:

// f.bp — "get streams of a type from an item"
function getStreamsOfType(item, streamType, mediaIndex = 0) {   // ← 3rd arg, defaults to 0
  if (!item.mediaItems) return [];
  const version = item.mediaItems[mediaIndex];                  // version 0 unless told otherwise
  if (!version?.parts) return [];
  return (version.parts[0].streams || []).filter(s => s.streamType === streamType);
}

verbatim: function D(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(!e.mediaItems)return[];var i=e.mediaItems[n];return null!=i&&i.parts?(i.parts[0].streams||[]).filter(function(e){return e.streamType===t}):[]}

With no index it returns mediaItems[0] = FLUX (1 audio) while the element plays mediaItems[1] = B&W (2 audio) — hence expected: 1 against a 2-track file. (metadataItem is the full multi-version array, the same one appInitializer indexes via mediaItems[t.mediaIndex].)

Downstream (stock code). Lengths differ → getSelectedAudioStreamIndex (getAppConfig.ts, the #939218 function) short-circuits:

function getSelectedAudioStreamIndex(serverStreams, elementTracks) {
  const r = serverStreams.findIndex(s => s.selected);

  if (serverStreams.length !== elementTracks.length) {
    log.warn(`Unexpected number of audio tracks... Found: ${elementTracks.length}, expected: ${serverStreams.length}`);
    return r;                       // ← BUG PATH (1 !== 2): skips the language match + mirror below
  }

  const aligned = serverStreams.every((s, i) => {              // normal path — only when lists line up
    const elemLang = elementTracks[i].language;
    if (!(s.languageCode || (elemLang && elemLang !== "und"))) return true;
    return s.languageCode === elemLang
        || (!!s.languageCode && (s.languageTag || isoMap[s.languageCode]) === elemLang);
  });
  return aligned ? r : elementTracks.length - r - 1;   // ← the #939218 mirror lives here
}

So audioTracks[r].enabled → ~track 0 = English, regardless of the pick. The language/mirror code never runs — the defect is the wrong input handed upstream.

Proposed fix — source from the playing version (playable.decision.choice.{mediaIndex,part}, already read in PlaybackSessionController/appInitializer):

const part = playable.decision?.choice?.part;
const serverStreams = part?.streams
  ? part.streams.filter(s => s.id != null && s.streamType === StreamType.Audio)  // same predicate as PSC._getStreamsOfType
  : getStreamsOfType(playable.metadataItem, StreamType.Audio);                    // fallback

Minified drop-in:

var p=n.decision&&n.decision.choice&&n.decision.choice.part;
var i=p&&p.streams?p.streams.filter(function(e){return null!=e.id&&e.streamType===m.jx.Audio}):(0,f.bp)(n.metadataItem,m.jx.Audio);

(Or minimal — pass the index: (0,f.bp)(n.metadataItem,m.jx.Audio,(n.decision&&n.decision.choice?n.decision.choice.mediaIndex:0)||0).) After it, lists match and the existing language path runs — also re-enabling the en-US/#939218 handling the mismatch was short-circuiting.

TL;DR: _selectAudioTrack reads the audio list from version 0 instead of the playing version; source it from playable.decision.choice.part.streams.