From a5baa30e408e00df8e14bc42a6e8b6458adb90b3 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Thu, 3 Oct 2024 23:23:00 -0400 Subject: [PATCH] force aac main and he-aacv1 to be transcoded to mp3. save currently playing video meta data to global session --- components/ItemGrid/LoadVideoContentTask.bs | 7 ++--- components/video/VideoPlayerView.bs | 12 ++++++-- source/api/Items.bs | 33 +++++++++++++++++---- source/utils/session.bs | 20 ++++++++++++- 4 files changed, 60 insertions(+), 12 deletions(-) diff --git a/components/ItemGrid/LoadVideoContentTask.bs b/components/ItemGrid/LoadVideoContentTask.bs index 643df4fe1..cf1f32561 100644 --- a/components/ItemGrid/LoadVideoContentTask.bs +++ b/components/ItemGrid/LoadVideoContentTask.bs @@ -6,6 +6,7 @@ import "pkg:/source/utils/config.bs" import "pkg:/source/api/Image.bs" import "pkg:/source/api/userauth.bs" import "pkg:/source/utils/deviceCapabilities.bs" +import "pkg:/source/utils/session.bs" enum SubtitleSelection notset = -2 @@ -71,14 +72,14 @@ end function sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_stream_idx = 1 as integer, forceTranscoding = false as boolean) meta = ItemMetaData(video.id) - subtitle_idx = m.top.selectedSubtitleIndex - if not isValid(meta) video.errorMsg = "Error loading metadata" video.content = invalid return end if + session.video.Update(meta) + subtitle_idx = m.top.selectedSubtitleIndex videotype = LCase(meta.type) ' Check for any Live TV streams or Recordings coming from other places other than the TV Guide @@ -196,12 +197,10 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s } end if - ' 'TODO: allow user selection of subtitle track before playback initiated, for now set to no subtitles video.directPlaySupported = m.playbackInfo.MediaSources[0].SupportsDirectPlay fully_external = false - ' For h264/hevc video, Roku spec states that it supports specfic encoding levels ' The device can decode content with a Higher Encoding level but may play it back with certain ' artifacts. If the user preference is set, and the only reason the server says we need to diff --git a/components/video/VideoPlayerView.bs b/components/video/VideoPlayerView.bs index 4e82d085a..553d14b43 100644 --- a/components/video/VideoPlayerView.bs +++ b/components/video/VideoPlayerView.bs @@ -1,5 +1,6 @@ import "pkg:/source/utils/misc.bs" import "pkg:/source/utils/config.bs" +import "pkg:/source/utils/session.bs" import "pkg:/source/roku_modules/log/LogMixin.brs" sub init() @@ -102,6 +103,7 @@ sub handleItemSkipAction(action as string) ' If there is something next in the queue, play it if m.global.queueManager.callFunc("getPosition") < m.global.queueManager.callFunc("getCount") - 1 m.top.control = "stop" + session.video.Delete() m.global.sceneManager.callFunc("clearPreviousScene") m.global.queueManager.callFunc("moveForward") m.global.queueManager.callFunc("playQueue") @@ -114,6 +116,7 @@ sub handleItemSkipAction(action as string) ' If there is something previous in the queue, play it if m.global.queueManager.callFunc("getPosition") > 0 m.top.control = "stop" + session.video.Delete() m.global.sceneManager.callFunc("clearPreviousScene") m.global.queueManager.callFunc("moveBack") m.global.queueManager.callFunc("playQueue") @@ -626,10 +629,10 @@ sub onState(msg) else ' If an error was encountered, Display dialog showPlaybackErrorDialog(tr("Error During Playback")) + session.video.Delete() end if - ' Stop playback and exit player - m.top.control = "stop" + else if m.top.state = "playing" ' Check if next episode is available @@ -655,9 +658,11 @@ sub onState(msg) m.playbackTimer.control = "stop" ReportPlayback("stop") m.playReported = false + session.video.Delete() else if m.top.state = "finished" m.playbackTimer.control = "stop" ReportPlayback("finished") + session.video.Delete() else m.log.warning("Unhandled state", m.top.state, m.playReported, m.playFinished) end if @@ -716,6 +721,7 @@ sub bufferCheck(msg) ' Stop playback and exit player m.top.control = "stop" + session.video.Delete() end if end if @@ -785,6 +791,7 @@ function onKeyEvent(key as string, press as boolean) as boolean if key = "OK" and m.nextEpisodeButton.hasfocus() and not m.top.trickPlayBar.visible m.top.control = "stop" m.top.state = "finished" + session.video.Delete() hideNextEpisodeButton() return true else @@ -859,6 +866,7 @@ function onKeyEvent(key as string, press as boolean) as boolean if key = "back" m.top.control = "stop" + session.video.Delete() end if return false diff --git a/source/api/Items.bs b/source/api/Items.bs index f4262dc29..2a9f6c72b 100644 --- a/source/api/Items.bs +++ b/source/api/Items.bs @@ -1,4 +1,5 @@ import "pkg:/source/api/sdk.bs" +import "pkg:/source/utils/misc.bs" function ItemGetPlaybackInfo(id as string, startTimeTicks = 0 as longinteger) params = { @@ -13,9 +14,6 @@ function ItemGetPlaybackInfo(id as string, startTimeTicks = 0 as longinteger) end function function ItemPostPlaybackInfo(id as string, mediaSourceId = "" as string, audioTrackIndex = -1 as integer, subtitleTrackIndex = -1 as integer, startTimeTicks = 0 as longinteger) - body = { - "DeviceProfile": getDeviceProfile() - } params = { "UserId": m.global.session.user.id, "StartTimeTicks": startTimeTicks, @@ -25,6 +23,7 @@ function ItemPostPlaybackInfo(id as string, mediaSourceId = "" as string, audioT "MaxStaticBitrate": "140000000", "SubtitleStreamIndex": subtitleTrackIndex } + deviceProfile = getDeviceProfile() ' Note: Jellyfin v10.9+ now remuxs LiveTV and does not allow DirectPlay anymore. ' Because of this, we need to tell the server "EnableDirectPlay = false" so that we receive the @@ -38,11 +37,35 @@ function ItemPostPlaybackInfo(id as string, mediaSourceId = "" as string, audioT params.EnableDirectPlay = false end if - if audioTrackIndex > -1 then params.AudioStreamIndex = audioTrackIndex + if audioTrackIndex > -1 + params.AudioStreamIndex = audioTrackIndex + + ' force the server to transcode AAC profiles we don't support to MP3 instead of the usual AAC + ' TODO: Remove this after server adds support for transcoding AAC from one profile to another + selectedAudioStream = m.global.session.video.json.MediaStreams[audioTrackIndex] + + if LCase(selectedAudioStream.Codec) = "aac" + if LCase(selectedAudioStream.Profile) = "main" or LCase(selectedAudioStream.Profile) = "he-aac" + for each rule in deviceProfile.TranscodingProfiles + if rule.Container = "ts" or rule.Container = "mp4" + if rule.AudioCodec = "aac" + rule.AudioCodec = "mp3" + else if rule.AudioCodec.Left(4) = "aac," + rule.AudioCodec = mid(rule.AudioCodec, 5) + + if rule.AudioCodec.Left(3) <> "mp3" + rule.AudioCodec = "mp3," + rule.AudioCodec + end if + end if + end if + end for + end if + end if + end if req = APIRequest(Substitute("Items/{0}/PlaybackInfo", id), params) req.SetRequest("POST") - return postJson(req, FormatJson(body)) + return postJson(req, FormatJson({ "DeviceProfile": deviceProfile })) end function ' Search across all libraries diff --git a/source/utils/session.bs b/source/utils/session.bs index 497ff310b..c82cc71e7 100644 --- a/source/utils/session.bs +++ b/source/utils/session.bs @@ -15,6 +15,9 @@ namespace session Policy: {}, settings: {}, lastRunVersion: invalid + }, + video: { + json: {} } } }) @@ -30,7 +33,7 @@ namespace session ' Update one value from the global session array (m.global.session) sub Update(key as string, value = {} as object) ' validate parameters - if key = "" or (key <> "user" and key <> "server") or value = invalid + if key = "" or (key <> "user" and key <> "server" and key <> "video") or value = invalid print "Error in session.Update(): Invalid parameters provided" return end if @@ -429,4 +432,19 @@ namespace session end sub end namespace end namespace + + namespace video + ' Return the global video session array to it's default state + sub Delete() + session.Update("video", { json: {} }) + end sub + + ' Update the global video session array (m.global.session.video) + sub Update(videoMetaData as object) + if videoMetaData = invalid then return + + session.video.Delete() + session.Update("video", videoMetaData) + end sub + end namespace end namespace