diff --git a/Makefile b/Makefile
index bd07230e7..b22a6d4da 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
# If you want to get_images, you'll also need convert from ImageMagick
##########################################################################
-VERSION := 2.1.4
+VERSION := 2.1.5
## usage
diff --git a/components/ItemGrid/GridItem.bs b/components/ItemGrid/GridItem.bs
index b98ddcc64..9f7a62ae1 100644
--- a/components/ItemGrid/GridItem.bs
+++ b/components/ItemGrid/GridItem.bs
@@ -20,9 +20,6 @@ sub init()
m.checkmark.width = 90
m.checkmark.height = 60
- m.itemText.translation = [0, m.itemPoster.height + 7]
- m.itemText.visible = m.gridTitles = "showalways"
-
' Add some padding space when Item Titles are always showing
if m.itemText.visible then m.itemText.maxWidth = 250
@@ -38,6 +35,9 @@ sub init()
end if
end if
+ m.itemText.translation = [0, m.itemPoster.height + 7]
+ m.itemText.visible = m.gridTitles = "showalways"
+
end sub
sub itemContentChanged()
diff --git a/components/ItemGrid/ItemGrid.bs b/components/ItemGrid/ItemGrid.bs
index 21789b45e..759bacd7e 100644
--- a/components/ItemGrid/ItemGrid.bs
+++ b/components/ItemGrid/ItemGrid.bs
@@ -6,6 +6,7 @@ import "pkg:/source/roku_modules/log/LogMixin.brs"
sub init()
m.log = log.Logger("ItemGrid")
+ m.log.debug("start init()")
m.options = m.top.findNode("options")
m.showItemCount = m.global.session.user.settings["itemgrid.showItemCount"]
@@ -70,6 +71,7 @@ sub init()
m.resetGrid = m.global.session.user.settings["itemgrid.reset"]
m.top.gridTitles = m.global.session.user.settings["itemgrid.gridTitles"]
+ m.log.debug("end init()")
end sub
'Genre Item Selected
@@ -79,6 +81,7 @@ end sub
'Load initial set of Data
sub loadInitialItems()
+ m.log.debug("start loadInitialItems()")
m.loadItemsTask.control = "stop"
startLoadingSpinner()
@@ -235,6 +238,7 @@ sub loadInitialItems()
startLoadingSpinner(false)
m.loadItemsTask.control = "RUN"
SetUpOptions()
+ m.log.debug("end loadInitialItems()")
end sub
' Set Movies view, sort, and filter options
@@ -445,6 +449,7 @@ end sub
'Handle loaded data, and add to Grid
sub ItemDataLoaded(msg)
+ m.log.debug("start ItemDataLoaded()")
itemData = msg.GetData()
m.loadItemsTask.unobserveField("content")
m.loadItemsTask.content = []
@@ -501,22 +506,28 @@ sub ItemDataLoaded(msg)
end if
stopLoadingSpinner()
+ m.log.debug("end ItemDataLoaded()")
end sub
'Set Background Image
sub SetBackground(backgroundUri as string)
-
+ m.log.debug("start SetBackground()", backgroundUri, m.swapAnimation.state, m.newBackdrop.loadStatus)
'If a new image is being loaded, or transitioned to, store URL to load next
- if m.swapAnimation.state <> "stopped" or m.newBackdrop.loadStatus = "loading"
- m.queuedBGUri = backgroundUri
- return
+ if not m.top.alphaActive
+ if m.swapAnimation.state <> "stopped" or m.newBackdrop.loadStatus = "loading"
+ m.log.debug("caching new background URI")
+ m.queuedBGUri = backgroundUri
+ return
+ end if
end if
m.newBackdrop.uri = backgroundUri
+ m.log.debug("end SetBackground()")
end sub
'Handle new item being focused
sub onItemFocused()
+ m.log.debug("start onItemFocused()", m.itemGrid.currFocusRow, m.itemGrid.itemFocused)
focusedRow = m.itemGrid.currFocusRow
@@ -525,7 +536,7 @@ sub onItemFocused()
updateTitle()
' If no selected item, set background to parent backdrop
- if itemInt = -1
+ if itemInt = -1 or focusedRow = -1
return
end if
@@ -538,6 +549,7 @@ sub onItemFocused()
if focusedRow >= m.loadedRows - 5 and m.loadeditems < m.loadItemsTask.totalRecordCount
loadMoreData()
end if
+ m.log.debug("end onItemFocused()")
end sub
'When Image Loading Status changes
@@ -558,6 +570,7 @@ sub swapDone()
'If there is another one to load
if m.newBackdrop.uri <> m.queuedBGUri and m.queuedBGUri <> ""
+ m.log.debug("Loading queued backdrop image", m.queuedBGUri)
SetBackground(m.queuedBGUri)
m.queuedBGUri = ""
end if
@@ -566,6 +579,7 @@ end sub
'Load next set of items
sub loadMoreData()
+ m.log.debug("start loadMoreData()")
if m.Loading = true then return
startLoadingSpinner(false)
@@ -573,6 +587,7 @@ sub loadMoreData()
m.loadItemsTask.startIndex = m.loadedItems
m.loadItemsTask.observeField("content", "ItemDataLoaded")
m.loadItemsTask.control = "RUN"
+ m.log.debug("end loadMoreData()")
end sub
'Item Selected
@@ -767,6 +782,20 @@ function getItemFocused()
return invalid
end function
+sub alphaActiveChanged()
+ m.log.debug("start alphaActiveChanged()", m.top.alphaActive)
+
+ if m.top.alphaActive
+ ' fade into an empty backdrop
+ m.swapAnimation.state = "stop"
+ m.queuedBGUri = ""
+ ' use a 1px image because we can't use the animation to fade into a blank uri string
+ SetBackground("pkg:/images/1px-262626.png")
+ end if
+
+ m.log.debug("end alphaActiveChanged()")
+end sub
+
function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
@@ -833,11 +862,14 @@ function onKeyEvent(key as string, press as boolean) as boolean
return true
end if
else if key = "left" and topGrp.isinFocusChain() and m.alpha.visible
+ m.log.debug("Now entering alpha menu")
m.top.alphaActive = true
topGrp.setFocus(false)
m.alphaMenu.setFocus(true)
+
return true
else if key = "right" and m.alpha.isinFocusChain()
+ m.log.debug("Now leaving alpha menu")
m.top.alphaActive = false
m.alphaMenu.setFocus(false)
topGrp.setFocus(true)
diff --git a/components/ItemGrid/ItemGrid.xml b/components/ItemGrid/ItemGrid.xml
index bea6a14a6..74b8665c6 100644
--- a/components/ItemGrid/ItemGrid.xml
+++ b/components/ItemGrid/ItemGrid.xml
@@ -33,7 +33,7 @@
-
+
diff --git a/components/PlaystateTask.bs b/components/PlaystateTask.bs
index fc06ae3e1..556ab52f2 100644
--- a/components/PlaystateTask.bs
+++ b/components/PlaystateTask.bs
@@ -8,7 +8,7 @@ end sub
sub PlaystateUpdate()
if m.top.status = "start"
url = "Sessions/Playing"
- else if m.top.status = "stop"
+ else if m.top.status = "stop" or m.top.status = "finished"
url = "Sessions/Playing/Stopped"
else if m.top.status = "update"
url = "Sessions/Playing/Progress"
diff --git a/components/data/ChannelData.xml b/components/data/ChannelData.xml
index b7a2feb51..2d0667b58 100644
--- a/components/data/ChannelData.xml
+++ b/components/data/ChannelData.xml
@@ -1,6 +1,7 @@
+
diff --git a/components/video/VideoPlayerView.bs b/components/video/VideoPlayerView.bs
index be0d32be2..b662cfcf9 100644
--- a/components/video/VideoPlayerView.bs
+++ b/components/video/VideoPlayerView.bs
@@ -1,7 +1,9 @@
import "pkg:/source/utils/misc.bs"
import "pkg:/source/utils/config.bs"
+import "pkg:/source/roku_modules/log/LogMixin.brs"
sub init()
+ m.log = log.Logger("VideoPlayerView")
' Hide the overhang on init to prevent showing 2 clocks
m.top.getScene().findNode("overhang").visible = false
m.currentItem = m.global.queueManager.callFunc("getCurrentItem")
@@ -602,6 +604,7 @@ end sub
'
' When Video Player state changes
sub onState(msg)
+ m.log.debug("start onState()", m.top.state)
if isValid(m.captionTask)
m.captionTask.playerState = m.top.state + m.top.globalCaptionMode
end if
@@ -610,12 +613,23 @@ sub onState(msg)
m.osd.playbackState = m.top.state
' When buffering, start timer to monitor buffering process
- if m.top.state = "buffering" and m.bufferCheckTimer <> invalid
+ if m.top.state = "buffering"
+ ' start buffer timer
+ if isValid(m.bufferCheckTimer)
+ m.bufferCheckTimer.control = "start"
+ m.bufferCheckTimer.ObserveField("fire", "bufferCheck")
+ end if
- ' start timer
- m.bufferCheckTimer.control = "start"
- m.bufferCheckTimer.ObserveField("fire", "bufferCheck")
+ ' update server if needed
+ if not m.playReported
+ m.playReported = true
+ ReportPlayback("start")
+ end if
else if m.top.state = "error"
+ m.log.error(m.top.errorCode, m.top.errorMsg, m.top.errorStr, m.top.errorCode)
+
+ print m.top.errorInfo
+
if not m.playReported and m.top.transcodeAvailable
m.top.retryWithTranscoding = true ' If playback was not reported, retry with transcoding
else
@@ -625,7 +639,6 @@ sub onState(msg)
' Stop playback and exit player
m.top.control = "stop"
- m.top.backPressed = true
else if m.top.state = "playing"
' Check if next episode is available
@@ -651,16 +664,22 @@ sub onState(msg)
m.playbackTimer.control = "stop"
ReportPlayback("stop")
m.playReported = false
+ else if m.top.state = "finished"
+ m.playbackTimer.control = "stop"
+ ReportPlayback("finished")
+ else
+ m.log.warning("Unhandled state", m.top.state, m.playReported, m.playFinished)
end if
-
+ m.log.debug("end onState()", m.top.state)
end sub
'
' Report playback to server
sub ReportPlayback(state = "update" as string)
-
if m.top.position = invalid then return
+ m.log.debug("start ReportPlayback()", state, int(m.top.position))
+
params = {
"ItemId": m.top.id,
"PlaySessionId": m.top.PlaySessionId,
@@ -679,6 +698,8 @@ sub ReportPlayback(state = "update" as string)
playstateTask = m.global.playstateTask
playstateTask.setFields({ status: state, params: params })
playstateTask.control = "RUN"
+
+ m.log.debug("end ReportPlayback()", state, int(m.top.position))
end sub
'
@@ -704,7 +725,6 @@ sub bufferCheck(msg)
' Stop playback and exit player
m.top.control = "stop"
- m.top.backPressed = true
end if
end if
diff --git a/images/1px-262626.png b/images/1px-262626.png
new file mode 100644
index 000000000..5122799ac
Binary files /dev/null and b/images/1px-262626.png differ
diff --git a/manifest b/manifest
index 6dac4b714..47de6e805 100644
--- a/manifest
+++ b/manifest
@@ -3,7 +3,7 @@
title=Jellyfin
major_version=2
minor_version=1
-build_version=4
+build_version=5
### Main Menu Icons / Channel Poster Artwork
diff --git a/package-lock.json b/package-lock.json
index 2490ceb8b..99083eb06 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "jellyfin-roku",
- "version": "2.1.4",
+ "version": "2.1.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "jellyfin-roku",
- "version": "2.1.4",
+ "version": "2.1.5",
"hasInstallScript": true,
"license": "GPL-2.0",
"dependencies": {
diff --git a/package.json b/package.json
index bf2b648d4..f4c7abe4a 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "jellyfin-roku",
"type": "module",
- "version": "2.1.4",
+ "version": "2.1.5",
"description": "Roku app for Jellyfin media server",
"dependencies": {
"@rokucommunity/bslib": "0.1.1",
diff --git a/source/api/Items.bs b/source/api/Items.bs
index 6b5db4ead..f4262dc29 100644
--- a/source/api/Items.bs
+++ b/source/api/Items.bs
@@ -26,7 +26,17 @@ function ItemPostPlaybackInfo(id as string, mediaSourceId = "" as string, audioT
"SubtitleStreamIndex": subtitleTrackIndex
}
- if mediaSourceId <> "" then params.MediaSourceId = mediaSourceId
+ ' 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
+ ' transcoding URL (which is just a remux and not a transcode; unless it is)
+ ' The web handles this by disabling EnableDirectPlay on a Retry, but we don't currently Retry a Live
+ ' TV stream, thus we just turn it off on the first try here.
+ if mediaSourceId <> ""
+ params.MediaSourceId = mediaSourceId
+ else
+ ' No mediaSourceId? Must be LiveTV...
+ params.EnableDirectPlay = false
+ end if
if audioTrackIndex > -1 then params.AudioStreamIndex = audioTrackIndex
diff --git a/source/utils/deviceCapabilities.bs b/source/utils/deviceCapabilities.bs
index 3dd234dce..05bec80b5 100644
--- a/source/utils/deviceCapabilities.bs
+++ b/source/utils/deviceCapabilities.bs
@@ -440,7 +440,26 @@ function getCodecProfiles() as object
if di.CanDecodeAudio({ Codec: audioCodec, ChCnt: audioChannel }).Result
channelSupportFound = true
for each codecType in ["VideoAudio", "Audio"]
- if audioCodec = "opus" and codecType = "Audio"
+ if audioCodec = "aac"
+ codecProfiles.push({
+ "Type": codecType,
+ "Codec": audioCodec,
+ "Conditions": [
+ {
+ "Condition": "NotEquals",
+ "Property": "AudioProfile",
+ "Value": "Main",
+ "IsRequired": true
+ },
+ {
+ "Condition": "LessThanEqual",
+ "Property": "AudioChannels",
+ "Value": audioChannel,
+ "IsRequired": true
+ }
+ ]
+ })
+ else if audioCodec = "opus" and codecType = "Audio"
' opus audio files not supported by roku
else
codecProfiles.push({