From d9355ac3aa175e9184db64e42c62a934aab933a0 Mon Sep 17 00:00:00 2001 From: Oliver Marriott Date: Tue, 10 Mar 2026 23:51:57 +1100 Subject: [PATCH 1/3] Force AAC transcode when streaming mka+opus to desktop client Matroska audio containers (aka mka files) with Opus codec streams inside were unplayable on the desktop client because hls.js was unable to decode the stream, resulting in an infinitely "spinning" play button. When configuring a stream, we now check for the opus codec and force AAC transcoding. Matroska containers support other codecs besides Opus, eg: mp3, which do not require transcoding and work fine before this patch, which is why we check for opus in codecsToForceAAC instead of AudioMimeType.MKA in mimeTypesToForceAAC. The AudioMimeType.OPUS mimetype is already marked as requiring transcoding but since its inside a container this check does not evaluate to true, we must check the codec explicitly. --- server/objects/Stream.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/objects/Stream.js b/server/objects/Stream.js index 5aa013e8e..70361463f 100644 --- a/server/objects/Stream.js +++ b/server/objects/Stream.js @@ -73,7 +73,7 @@ class Stream extends EventEmitter { return [AudioMimeType.FLAC, AudioMimeType.OPUS, AudioMimeType.WMA, AudioMimeType.AIFF, AudioMimeType.WEBM, AudioMimeType.WEBMA, AudioMimeType.AWB, AudioMimeType.CAF] } get codecsToForceAAC() { - return ['alac', 'ac3', 'eac3'] + return ['alac', 'ac3', 'eac3', 'opus'] } get userToken() { return this.user.token From 522b9735e22dabc111eb47cb5d5e814bc1c1c351 Mon Sep 17 00:00:00 2001 From: Oliver Marriott Date: Thu, 9 Apr 2026 20:49:48 +1000 Subject: [PATCH 2/3] Add `audio/(x-)matroska` to client player MIME types to avoid transcode Firefox, at least, supports playing `matroska/audio` containers natively but the client was not checking for support. Clients that do not support playing `matroska/audio` containers will fallback to transcoding. --- client/players/LocalAudioPlayer.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/players/LocalAudioPlayer.js b/client/players/LocalAudioPlayer.js index 7fc17e7aa..377818089 100644 --- a/client/players/LocalAudioPlayer.js +++ b/client/players/LocalAudioPlayer.js @@ -46,7 +46,14 @@ export default class LocalAudioPlayer extends EventEmitter { this.player.addEventListener('loadedmetadata', this.evtLoadedMetadata.bind(this)) this.player.addEventListener('timeupdate', this.evtTimeupdate.bind(this)) - var mimeTypes = ['audio/flac', 'audio/mpeg', 'audio/mp4', 'audio/ogg', 'audio/aac', 'audio/x-ms-wma', 'audio/x-aiff', 'audio/webm'] + var mimeTypes = [ + 'audio/flac', 'audio/mpeg', 'audio/mp4', 'audio/ogg', 'audio/aac', + 'audio/x-ms-wma', 'audio/x-aiff', 'audio/webm', + // `audio/matroska` is the correct mimetype, but at least as of 2026-04-09, + // the detected mimetype for matroska files by the server is `audio/x-matroska`. + // ref: https://www.iana.org/assignments/media-types/media-types.xhtml + 'audio/matroska', 'audio/x-matroska' + ] var mimeTypeCanPlayMap = {} mimeTypes.forEach((mt) => { var canPlay = this.player.canPlayType(mt) From 94c426bd971a40771e8a3503af3eafd01cc89c73 Mon Sep 17 00:00:00 2001 From: advplyr Date: Fri, 10 Apr 2026 16:42:39 -0500 Subject: [PATCH 3/3] Update comments on matroska --- client/players/LocalAudioPlayer.js | 16 +++++++++++----- server/utils/constants.js | 2 ++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/client/players/LocalAudioPlayer.js b/client/players/LocalAudioPlayer.js index 377818089..a0384d54d 100644 --- a/client/players/LocalAudioPlayer.js +++ b/client/players/LocalAudioPlayer.js @@ -47,12 +47,18 @@ export default class LocalAudioPlayer extends EventEmitter { this.player.addEventListener('timeupdate', this.evtTimeupdate.bind(this)) var mimeTypes = [ - 'audio/flac', 'audio/mpeg', 'audio/mp4', 'audio/ogg', 'audio/aac', - 'audio/x-ms-wma', 'audio/x-aiff', 'audio/webm', - // `audio/matroska` is the correct mimetype, but at least as of 2026-04-09, - // the detected mimetype for matroska files by the server is `audio/x-matroska`. + 'audio/flac', + 'audio/mpeg', + 'audio/mp4', + 'audio/ogg', + 'audio/aac', + 'audio/x-ms-wma', + 'audio/x-aiff', + 'audio/webm', + // `audio/matroska` is the correct mimetype, but the server still uses `audio/x-matroska` // ref: https://www.iana.org/assignments/media-types/media-types.xhtml - 'audio/matroska', 'audio/x-matroska' + 'audio/matroska', + 'audio/x-matroska' ] var mimeTypeCanPlayMap = {} mimeTypes.forEach((mt) => { diff --git a/server/utils/constants.js b/server/utils/constants.js index cc5217f41..925035e17 100644 --- a/server/utils/constants.js +++ b/server/utils/constants.js @@ -48,6 +48,8 @@ module.exports.AudioMimeType = { AIF: 'audio/x-aiff', WEBM: 'audio/webm', WEBMA: 'audio/webm', + // TODO: Switch to `audio/matroska`? marked as deprecated in IANA registry + // ref: https://datatracker.ietf.org/doc/html/rfc9559 MKA: 'audio/x-matroska', AWB: 'audio/amr-wb', CAF: 'audio/x-caf',