mirror of
https://github.com/jellyfin/jellyfin-roku.git
synced 2024-12-02 19:26:47 +00:00
Merge branch 'master' into handle-missing-media-streams
This commit is contained in:
commit
4e7211750b
10
DEVGUIDE.md
10
DEVGUIDE.md
@ -121,6 +121,16 @@ telnet ${ROKU_DEV_TARGET} 8085
|
||||
|
||||
To exit telnet: `CTRL + ]` and then type `quit + ENTER`
|
||||
|
||||
### Committing
|
||||
|
||||
Before commiting your code, please run:
|
||||
|
||||
```bash
|
||||
make prep_commit
|
||||
```
|
||||
|
||||
This will format your code and run the CI checks locally to ensure you will pass the CI tests.
|
||||
|
||||
### (Optional) Update Images
|
||||
|
||||
This repo already contains all necessary images for the app. This script only needs to be run when the [official Jellyfin images](https://github.com/jellyfin/jellyfin-ux) are changed to allow us to update the repo images.
|
||||
|
6
app.mk
6
app.mk
@ -169,6 +169,12 @@ prep_tests:
|
||||
cp -r $(SOURCEREL)/tests/source/* $(STAGINGREL)/source/tests/;\
|
||||
./node_modules/.bin/rooibos-cli i tests/.rooibosrc.json
|
||||
|
||||
prep_commit:
|
||||
npm run format
|
||||
npm ci
|
||||
npm run validate
|
||||
npm run check-formatting
|
||||
|
||||
install: prep_staging package home
|
||||
@echo "Installing $(APPNAME)-$(BUILD) to host $(ROKU_DEV_TARGET)"
|
||||
@$(CURLCMD) --user $(USERPASS) --digest -F "mysubmit=Install" -F "archive=@$(ZIPREL)/$(APPNAME)-$(BUILD).zip" -F "passwd=" http://$(ROKU_DEV_TARGET)/plugin_install | grep "<font color" | sed "s/<font color=\"red\">//" | sed "s[</font>[["
|
||||
|
28
components/ItemGrid/Alpha.brs
Normal file
28
components/ItemGrid/Alpha.brs
Normal file
@ -0,0 +1,28 @@
|
||||
sub init()
|
||||
m.top.visible = true
|
||||
m.Alphamenu = m.top.findNode("Alphamenu")
|
||||
m.Alphamenu.focusable = true
|
||||
m.Alphatext = m.top.findNode("alphatext")
|
||||
m.focusedChild = m.top.findNode("focusedChild")
|
||||
m.Alphamenu.focusedFont.size = 25
|
||||
m.Alphamenu.font.size = 25
|
||||
end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
|
||||
if not press then return false
|
||||
|
||||
if key = "OK"
|
||||
child = m.Alphatext.getChild(m.Alphamenu.itemFocused)
|
||||
|
||||
if child.title = m.top.itemAlphaSelected
|
||||
m.top.itemAlphaSelected = ""
|
||||
m.Alphamenu.focusFootprintBitmapUri = ""
|
||||
else
|
||||
m.Alphamenu.focusFootprintBitmapUri = "pkg:/images/white.png"
|
||||
m.top.itemAlphaSelected = child.title
|
||||
end if
|
||||
return true
|
||||
end if
|
||||
return false
|
||||
end function
|
55
components/ItemGrid/Alpha.xml
Normal file
55
components/ItemGrid/Alpha.xml
Normal file
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="Alpha" extends = "Group">
|
||||
<children>
|
||||
<LabelList
|
||||
translation="[1872, 185]"
|
||||
vertFocusAnimationStyle="floatingFocus"
|
||||
drawFocusFeedback="true"
|
||||
id = "Alphamenu"
|
||||
font="font:SmallestSystemFont"
|
||||
itemSpacing="[0,2]"
|
||||
itemSize="[28,28]"
|
||||
numRows="27"
|
||||
textHorizAlign="center"
|
||||
focusBitmapUri = "pkg:/images/white.png"
|
||||
focusBitmapBlendColor = "#FFFFFF"
|
||||
focusFootprintBlendColor = "#666666"
|
||||
>
|
||||
<ContentNode id="alphatext" role = "content" >
|
||||
<ContentNode title = "#" />
|
||||
<ContentNode title = "A" />
|
||||
<ContentNode title = "B" />
|
||||
<ContentNode title = "C" />
|
||||
<ContentNode title = "D" />
|
||||
<ContentNode title = "E" />
|
||||
<ContentNode title = "F" />
|
||||
<ContentNode title = "G" />
|
||||
<ContentNode title = "H" />
|
||||
<ContentNode title = "I" />
|
||||
<ContentNode title = "J" />
|
||||
<ContentNode title = "K" />
|
||||
<ContentNode title = "L" />
|
||||
<ContentNode title = "M" />
|
||||
<ContentNode title = "N" />
|
||||
<ContentNode title = "O" />
|
||||
<ContentNode title = "P" />
|
||||
<ContentNode title = "Q" />
|
||||
<ContentNode title = "R" />
|
||||
<ContentNode title = "S" />
|
||||
<ContentNode title = "T" />
|
||||
<ContentNode title = "U" />
|
||||
<ContentNode title = "V" />
|
||||
<ContentNode title = "W" />
|
||||
<ContentNode title = "X" />
|
||||
<ContentNode title = "Y" />
|
||||
<ContentNode title = "Z" />
|
||||
</ContentNode>
|
||||
</LabelList>
|
||||
</children>
|
||||
<interface>
|
||||
<field id="selectedItem" type="node" alwaysNotify="true" />
|
||||
<field id="focusedChild" type="node" onChange="focusChanged" />
|
||||
<field id="itemAlphaSelected" type="string" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="Alpha.brs" />
|
||||
</component>
|
@ -21,6 +21,7 @@ sub init()
|
||||
|
||||
m.itemGrid.observeField("itemFocused", "onItemFocused")
|
||||
m.itemGrid.observeField("itemSelected", "onItemSelected")
|
||||
m.itemGrid.observeField("AlphaSelected", "onItemAlphaSelected")
|
||||
m.newBackdrop.observeField("loadStatus", "newBGLoaded")
|
||||
|
||||
'Background Image Queued for loading
|
||||
@ -34,6 +35,8 @@ sub init()
|
||||
|
||||
m.loadItemsTask = createObject("roSGNode", "LoadItemsTask2")
|
||||
|
||||
m.Alpha = m.top.findNode("AlphaMenu")
|
||||
m.AlphaSelected = m.top.findNode("AlphaSelected")
|
||||
end sub
|
||||
|
||||
'
|
||||
@ -72,6 +75,9 @@ sub loadInitialItems()
|
||||
m.sortAscending = false
|
||||
end if
|
||||
|
||||
m.loadItemsTask.nameStartsWith = m.top.AlphaSelected
|
||||
m.emptyText.visible = false
|
||||
|
||||
updateTitle()
|
||||
|
||||
m.loadItemsTask.itemId = m.top.parentItem.Id
|
||||
@ -339,6 +345,14 @@ sub onItemSelected()
|
||||
m.top.selectedItem = m.itemGrid.content.getChild(m.itemGrid.itemSelected)
|
||||
end sub
|
||||
|
||||
sub onItemAlphaSelected()
|
||||
m.loadedRows = 0
|
||||
m.loadedItems = 0
|
||||
m.data = CreateObject("roSGNode", "ContentNode")
|
||||
m.itemGrid.content = m.data
|
||||
loadInitialItems()
|
||||
end sub
|
||||
|
||||
|
||||
'
|
||||
'Check if options updated and any reloading required
|
||||
@ -440,9 +454,8 @@ sub onChannelSelected(msg)
|
||||
end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
|
||||
if not press then return false
|
||||
|
||||
topGrp = m.top.findNode("itemGrid")
|
||||
if key = "options"
|
||||
if m.options.visible = true
|
||||
m.options.visible = false
|
||||
@ -463,6 +476,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
else if key = "play" or key = "OK"
|
||||
markupGrid = m.top.getChild(2)
|
||||
itemToPlay = markupGrid.content.getChild(markupGrid.itemFocused)
|
||||
|
||||
if itemToPlay <> invalid and (itemToPlay.type = "Movie" or itemToPlay.type = "Episode")
|
||||
m.top.quickPlayNode = itemToPlay
|
||||
return true
|
||||
@ -473,6 +487,16 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
photoPlayer.control = "RUN"
|
||||
return true
|
||||
end if
|
||||
else if key = "right" and topGrp.isinFocusChain()
|
||||
topGrp.setFocus(false)
|
||||
alpha = m.Alpha.getChild(0).findNode("Alphamenu")
|
||||
alpha.setFocus(true)
|
||||
return true
|
||||
else if key = "left" and m.Alpha.isinFocusChain()
|
||||
m.Alpha.setFocus(false)
|
||||
m.Alpha.visible = true
|
||||
topGrp.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
return false
|
||||
end function
|
||||
@ -481,8 +505,11 @@ sub updateTitle()
|
||||
if m.filter = "All"
|
||||
m.top.overhangTitle = m.top.parentItem.title
|
||||
else if m.filter = "Favorites"
|
||||
m.top.overhangTitle = m.top.parentItem.title + " (Favorites)"
|
||||
m.top.overhangTitle = m.top.parentItem.title + tr(" (Favorites)")
|
||||
else
|
||||
m.top.overhangTitle = m.top.parentItem.title + " (Filtered)"
|
||||
m.top.overhangTitle = m.top.parentItem.title + tr(" (Filtered)")
|
||||
end if
|
||||
if m.top.AlphaSelected <> ""
|
||||
m.top.overhangTitle = m.top.parentItem.title + tr(" (Filtered)")
|
||||
end if
|
||||
end sub
|
||||
|
@ -29,12 +29,14 @@
|
||||
<FloatFieldInterpolator id = "fadeinLoading" key="[0.0, 1.0]" keyValue="[ 0.00, 0.25 ]" fieldToInterp="backdropTransition.opacity" />
|
||||
<FloatFieldInterpolator id = "fadeoutLoaded" key="[0.0, 1.0]" keyValue="[ 0.25, 0.00 ]" fieldToInterp="backdrop.opacity" />
|
||||
</Animation>
|
||||
<Alpha id="AlphaMenu" />
|
||||
</children>
|
||||
<interface>
|
||||
<field id="parentItem" type="node" onChange="loadInitialItems" />
|
||||
<field id="selectedItem" type="node" alwaysNotify="true" />
|
||||
<field id="quickPlayNode" type="node" alwaysNotify="true" />
|
||||
<field id="imageDisplayMode" type="string" value="scaleToZoom" />
|
||||
<field id="AlphaSelected" type="string" alias="AlphaMenu.itemAlphaSelected" alwaysNotify="true" onChange="onItemAlphaSelected" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
|
||||
|
@ -25,6 +25,15 @@ sub loadItems()
|
||||
Fields: "Overview"
|
||||
}
|
||||
|
||||
' Handle special case when getting names starting with numeral
|
||||
if m.top.NameStartsWith <> ""
|
||||
if m.top.NameStartsWith = "#"
|
||||
params.NameLessThan = "A"
|
||||
else
|
||||
params.NameStartsWith = m.top.nameStartsWith
|
||||
end if
|
||||
end if
|
||||
|
||||
filter = m.top.filter
|
||||
if filter = "All" or filter = "all"
|
||||
' do nothing
|
||||
|
@ -9,6 +9,7 @@
|
||||
<field id="metadata" type="assocarray" />
|
||||
<field id="sortField" type="string" value="SortName" />
|
||||
<field id="sortAscending" type="boolean" value="true" />
|
||||
<field id="nameStartsWith" type="string" value="" />
|
||||
<field id="recursive" type="boolean" value="true" />
|
||||
<field id="filter" type="string" value="All" />
|
||||
|
||||
|
@ -5,6 +5,8 @@ sub init()
|
||||
|
||||
m.backdrop = m.top.findNode("backdrop")
|
||||
|
||||
m.deviceInfo = CreateObject("roDeviceInfo")
|
||||
|
||||
' Randmomise the background colors
|
||||
posterBackgrounds = m.global.constants.poster_bg_pallet
|
||||
m.backdrop.color = posterBackgrounds[rnd(posterBackgrounds.count()) - 1]
|
||||
@ -68,6 +70,13 @@ sub focusChanged()
|
||||
m.staticTitle.visible = false
|
||||
m.title.visible = true
|
||||
|
||||
' text to speech for accessibility
|
||||
if m.deviceInfo.IsAudioGuideEnabled() = true
|
||||
txt2Speech = CreateObject("roTextToSpeech")
|
||||
txt2Speech.Flush()
|
||||
txt2Speech.Say(m.title.text)
|
||||
end if
|
||||
|
||||
else
|
||||
m.title.repeatCount = 0
|
||||
m.staticTitle.visible = true
|
||||
|
31
components/extras/ExtrasItem.brs
Normal file
31
components/extras/ExtrasItem.brs
Normal file
@ -0,0 +1,31 @@
|
||||
sub init()
|
||||
m.posterImg = m.top.findNode("posterImg")
|
||||
m.name = m.top.findNode("pLabel")
|
||||
m.role = m.top.findNode("subTitle")
|
||||
|
||||
m.deviceInfo = CreateObject("roDeviceInfo")
|
||||
end sub
|
||||
|
||||
sub showContent()
|
||||
if m.top.itemContent <> invalid
|
||||
cont = m.top.itemContent
|
||||
m.name.text = cont.labelText
|
||||
m.name.maxWidth = cont.imageWidth
|
||||
m.role.Width = cont.imageWidth
|
||||
m.posterImg.uri = cont.posterUrl
|
||||
m.posterImg.width = cont.imageWidth
|
||||
m.role.Text = cont.subTitle
|
||||
else
|
||||
m.role.text = tr("Unknown")
|
||||
m.posterImg.uri = "pkg:/images/baseline_person_white_48dp.png"
|
||||
end if
|
||||
end sub
|
||||
|
||||
sub focusChanged()
|
||||
if m.deviceInfo.IsAudioGuideEnabled() = true
|
||||
txt2Speech = CreateObject("roTextToSpeech")
|
||||
txt2Speech.Flush()
|
||||
txt2Speech.Say(m.name.text)
|
||||
txt2Speech.Say(m.role.text)
|
||||
end if
|
||||
end sub
|
@ -2,31 +2,9 @@
|
||||
<component name="ExtrasItem" extends="JFGroup">
|
||||
<interface>
|
||||
<field id="itemContent" type="node" onChange="showContent" />
|
||||
<field id="itemHasFocus" type="boolean" onChange="focusChanged" />
|
||||
</interface>
|
||||
<script type="text/brightscript">
|
||||
<![CDATA[
|
||||
function init() as void
|
||||
m.posterImg = m.top.findNode("posterImg")
|
||||
m.name = m.top.findNode("pLabel")
|
||||
m.role = m.top.findNode("subTitle")
|
||||
end function
|
||||
|
||||
sub showContent()
|
||||
if m.top.itemContent <> Invalid
|
||||
cont = m.top.itemContent
|
||||
m.name.text = cont.labelText
|
||||
m.name.maxWidth = cont.imageWidth
|
||||
m.role.Width = cont.imageWidth
|
||||
m.posterImg.uri = cont.posterUrl
|
||||
m.posterImg.width = cont.imageWidth
|
||||
m.role.Text = cont.subTitle
|
||||
else
|
||||
m.role.text = "Who??"
|
||||
m.posterImg.uri = "pkg:/images/baseline_person_white_48dp.png"
|
||||
end if
|
||||
end sub
|
||||
]]>
|
||||
</script>
|
||||
<script type="text/brightscript" uri="ExtrasItem.brs" />
|
||||
<children>
|
||||
<LayoutGroup layoutDirection="vert" >
|
||||
<Poster id="posterImg" width="234" height="300" translation="[8,243]" failedBitmapUri="pkg:/images/baseline_person_white_48dp.png" />
|
||||
|
@ -1,6 +1,9 @@
|
||||
sub init()
|
||||
m.title = m.top.findNode("title")
|
||||
m.title.text = tr("Loading...")
|
||||
m.overview = m.top.findNode("overview")
|
||||
|
||||
m.deviceInfo = CreateObject("roDeviceInfo")
|
||||
end sub
|
||||
|
||||
sub itemContentChanged()
|
||||
@ -11,9 +14,9 @@ sub itemContentChanged()
|
||||
else
|
||||
indexNumber = ""
|
||||
end if
|
||||
m.top.findNode("title").text = indexNumber + item.title
|
||||
m.title.text = indexNumber + item.title
|
||||
m.top.findNode("poster").uri = item.posterURL
|
||||
m.top.findNode("overview").text = item.overview
|
||||
m.overview.text = item.overview
|
||||
|
||||
if type(itemData.RunTimeTicks) = "LongInteger"
|
||||
m.top.findNode("runtime").text = stri(getRuntime()).trim() + " mins"
|
||||
@ -63,3 +66,15 @@ function getEndTime() as string
|
||||
|
||||
return formatTime(date)
|
||||
end function
|
||||
|
||||
sub focusChanged()
|
||||
if m.top.itemHasFocus = true
|
||||
' text to speech for accessibility
|
||||
if m.deviceInfo.IsAudioGuideEnabled() = true
|
||||
txt2Speech = CreateObject("roTextToSpeech")
|
||||
txt2Speech.Flush()
|
||||
txt2Speech.Say(m.title.text)
|
||||
txt2Speech.Say(m.overview.text)
|
||||
end if
|
||||
end if
|
||||
end sub
|
||||
|
@ -27,6 +27,7 @@
|
||||
</children>
|
||||
<interface>
|
||||
<field id="itemContent" type="node" onChange="itemContentChanged"/>
|
||||
<field id="itemHasFocus" type="boolean" onChange="focusChanged" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="TVListDetails.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
|
||||
|
@ -135,17 +135,22 @@ function round(f as float) as integer
|
||||
end function
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
overview = m.top.findNode("overview")
|
||||
topGrp = m.top.findNode("seasons")
|
||||
bottomGrp = m.top.findNode("extrasGrid")
|
||||
|
||||
|
||||
if key = "down" and topGrp.isinFocusChain()
|
||||
if key = "down" and overview.hasFocus()
|
||||
topGrp.setFocus(true)
|
||||
return true
|
||||
else if key = "down" and topGrp.hasFocus()
|
||||
bottomGrp.setFocus(true)
|
||||
m.top.findNode("VertSlider").reverse = false
|
||||
m.top.findNode("extrasFader").reverse = false
|
||||
m.top.findNode("pplAnime").control = "start"
|
||||
return true
|
||||
else if key = "up" and bottomGrp.isinFocusChain()
|
||||
else if key = "up" and bottomGrp.hasFocus()
|
||||
if bottomGrp.itemFocused = 0
|
||||
m.top.findNode("VertSlider").reverse = true
|
||||
m.top.findNode("extrasFader").reverse = true
|
||||
@ -153,6 +158,10 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
topGrp.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
else if key = "up" and topGrp.hasFocus()
|
||||
overview.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
|
||||
return false
|
||||
end function
|
||||
|
BIN
images/white.png
Normal file
BIN
images/white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 593 B |
@ -431,6 +431,11 @@
|
||||
<translation>Not found</translation>
|
||||
<extracomment>Title of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unknown</source>
|
||||
<translation>Unknown</translation>
|
||||
<extracomment>Title for a cast member for which we have no information for</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>The requested content does not exist on the server</source>
|
||||
<translation>The requested content does not exist on the server</translation>
|
||||
|
Loading…
Reference in New Issue
Block a user