jf-roku/docs/api/components_ItemGrid_MovieLibraryView.bs.html

939 lines
66 KiB
HTML
Raw Normal View History

2023-11-11 13:41:20 +00:00
<!DOCTYPE html><html lang="en" style="font-size:16px"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Source: components/ItemGrid/MovieLibraryView.bs</title><!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
2023-12-05 16:56:00 +00:00
<![endif]--><script src="scripts/third-party/hljs.js" defer="defer"></script><script src="scripts/third-party/hljs-line-num.js" defer="defer"></script><script src="scripts/third-party/popper.js" defer="defer"></script><script src="scripts/third-party/tippy.js" defer="defer"></script><script src="scripts/third-party/tocbot.min.js"></script><script>var baseURL="/",locationPathname="";baseURL=(baseURL=(baseURL="https://jellyfin.github.io/jellyfin-roku/").replace(/https?:\/\//i,"")).substr(baseURL.indexOf("/"))</script><link rel="stylesheet" href="styles/clean-jsdoc-theme.min.css"><svg aria-hidden="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none"><defs><symbol id="copy-icon" viewbox="0 0 488.3 488.3"><g><path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z"/><path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z"/></g></symbol><symbol id="search-icon" viewBox="0 0 512 512"><g><g><path d="M225.474,0C101.151,0,0,101.151,0,225.474c0,124.33,101.151,225.474,225.474,225.474 c124.33,0,225.474-101.144,225.474-225.474C450.948,101.151,349.804,0,225.474,0z M225.474,409.323 c-101.373,0-183.848-82.475-183.848-183.848S124.101,41.626,225.474,41.626s183.848,82.475,183.848,183.848 S326.847,409.323,225.474,409.323z"/></g></g><g><g><path d="M505.902,476.472L386.574,357.144c-8.131-8.131-21.299-8.131-29.43,0c-8.131,8.124-8.131,21.306,0,29.43l119.328,119.328 c4.065,4.065,9.387,6.098,14.715,6.098c5.321,0,10.649-2.033,14.715-6.098C514.033,497.778,514.033,484.596,505.902,476.472z"/></g></g></symbol><symbol id="font-size-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.246 15H4.754l-2 5H.6L7 4h2l6.4 16h-2.154l-2-5zm-.8-2L8 6.885 5.554 13h4.892zM21 12.535V12h2v8h-2v-.535a4 4 0 1 1 0-6.93zM19 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol id="add-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></symbol><symbol id="minus-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></symbol><symbol id="dark-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></symbol><symbol id="light-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></symbol><symbol id="reset-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/></symbol><symbol id="down-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></symbol><symbol id="codepen-icon" viewBox="0 0 24 24"><path fill="none" d=
2023-11-11 02:08:52 +00:00
import "pkg:/source/utils/config.bs"
import "pkg:/source/api/baserequest.bs"
import "pkg:/source/api/Image.bs"
import "pkg:/source/utils/deviceCapabilities.bs"
2023-10-06 03:18:36 +00:00
sub setupNodes()
m.options = m.top.findNode("options")
m.itemGrid = m.top.findNode("itemGrid")
m.voiceBox = m.top.findNode("voiceBox")
m.backdrop = m.top.findNode("backdrop")
m.newBackdrop = m.top.findNode("backdropTransition")
m.emptyText = m.top.findNode("emptyText")
m.selectedMovieName = m.top.findNode("selectedMovieName")
m.selectedMovieOverview = m.top.findNode("selectedMovieOverview")
m.selectedMovieProductionYear = m.top.findNode("selectedMovieProductionYear")
m.selectedMovieOfficialRating = m.top.findNode("selectedMovieOfficialRating")
m.movieLogo = m.top.findNode("movieLogo")
m.swapAnimation = m.top.findNode("backroundSwapAnimation")
m.Alpha = m.top.findNode("AlphaMenu")
m.AlphaSelected = m.top.findNode("AlphaSelected")
m.micButton = m.top.findNode("micButton")
m.micButtonText = m.top.findNode("micButtonText")
m.communityRatingGroup = m.top.findNode("communityRatingGroup")
m.criticRatingIcon = m.top.findNode("criticRatingIcon")
m.criticRatingGroup = m.top.findNode("criticRatingGroup")
m.overhang = m.top.getScene().findNode("overhang")
m.genreList = m.top.findNode("genrelist")
m.infoGroup = m.top.findNode("infoGroup")
m.star = m.top.findNode("star")
end sub
sub init()
setupNodes()
m.overhang.isVisible = false
m.showItemCount = m.global.session.user.settings["itemgrid.showItemCount"]
m.swapAnimation.observeField("state", "swapDone")
m.loadedRows = 0
m.loadedItems = 0
m.data = CreateObject("roSGNode", "ContentNode")
m.itemGrid.content = m.data
m.genreData = CreateObject("roSGNode", "ContentNode")
m.genreList.observeField("itemSelected", "onGenreItemSelected")
m.genreList.content = m.genreData
m.itemGrid.observeField("itemFocused", "onItemFocused")
m.itemGrid.observeField("itemSelected", "onItemSelected")
m.itemGrid.observeField("alphaSelected", "onItemalphaSelected")
'Voice filter setup
m.voiceBox.voiceEnabled = true
m.voiceBox.active = true
m.voiceBox.observeField("text", "onvoiceFilter")
'set voice help text
m.voiceBox.hintText = tr("Use voice remote to search")
'backdrop
m.newBackdrop.observeField("loadStatus", "newBGLoaded")
'Background Image Queued for loading
m.queuedBGUri = ""
'Item sort - maybe load defaults from user prefs?
m.sortField = "SortName"
m.sortAscending = true
m.filter = "All"
m.filterOptions = {}
m.favorite = "Favorite"
m.loadItemsTask = createObject("roSGNode", "LoadItemsTask2")
m.loadLogoTask = createObject("roSGNode", "LoadItemsTask2")
m.getFiltersTask = createObject("roSGNode", "GetFiltersTask")
'set inital counts for overhang before content is loaded.
m.loadItemsTask.totalRecordCount = 0
'Get reset folder setting
m.resetGrid = m.global.session.user.settings["itemgrid.reset"]
'Hide voice search if device does not have voice remote
if m.global.device.hasVoiceRemote = false
m.micButton.visible = false
m.micButtonText.visible = false
end if
end sub
sub OnScreenHidden()
if not m.overhang.isVisible
m.overhang.disableMoveAnimation = true
m.overhang.isVisible = true
m.overhang.disableMoveAnimation = false
end if
end sub
sub OnScreenShown()
m.overhang.isVisible = false
if m.top.lastFocus &lt;> invalid
m.top.lastFocus.setFocus(true)
else
m.top.setFocus(true)
end if
end sub
'
'Load initial set of Data
sub loadInitialItems()
m.loadItemsTask.control = "stop"
2023-11-30 01:22:26 +00:00
startLoadingSpinner(false)
2023-10-06 03:18:36 +00:00
if m.top.parentItem.json.Type = "CollectionFolder"
m.top.HomeLibraryItem = m.top.parentItem.Id
end if
if m.top.parentItem.backdropUrl &lt;> invalid
SetBackground(m.top.parentItem.backdropUrl)
else
SetBackground("")
end if
m.sortField = m.global.session.user.settings["display." + m.top.parentItem.Id + ".sortField"]
m.filter = m.global.session.user.settings["display." + m.top.parentItem.Id + ".filter"]
m.filterOptions = m.global.session.user.settings["display." + m.top.parentItem.Id + ".filterOptions"]
m.view = m.global.session.user.settings["display." + m.top.parentItem.Id + ".landing"]
m.sortAscending = m.global.session.user.settings["display." + m.top.parentItem.Id + ".sortAscending"]
' If user has not set a preferred view for this folder, check if they've set a default view
if not isValid(m.view)
m.view = m.global.session.user.settings["itemgrid.movieDefaultView"]
end if
if not isValid(m.sortField) then m.sortField = "SortName"
if not isValid(m.filter) then m.filter = "All"
if not isValid(m.filterOptions) then m.filterOptions = "{}"
if not isValid(m.view) then m.view = "Movies"
2023-10-28 15:23:12 +00:00
if not isValid(m.sortAscending) then m.sortAscending = true
2023-10-06 03:18:36 +00:00
m.filterOptions = ParseJson(m.filterOptions)
if m.top.parentItem.json.type = "Studio"
m.loadItemsTask.studioIds = m.top.parentItem.id
m.loadItemsTask.itemId = m.top.parentItem.parentFolder
m.loadItemsTask.genreIds = ""
else if m.top.parentItem.json.type = "Genre"
m.loadItemsTask.genreIds = m.top.parentItem.id
m.loadItemsTask.itemId = m.top.parentItem.parentFolder
m.loadItemsTask.studioIds = ""
else if m.view = "Movies" or m.options.view = "Movies"
m.loadItemsTask.studioIds = ""
m.loadItemsTask.genreIds = ""
else
m.loadItemsTask.itemId = m.top.parentItem.Id
end if
m.loadItemsTask.nameStartsWith = m.top.alphaSelected
m.loadItemsTask.searchTerm = m.voiceBox.text
m.emptyText.visible = false
m.loadItemsTask.sortField = m.sortField
m.loadItemsTask.sortAscending = m.sortAscending
m.loadItemsTask.filter = m.filter
m.loadItemsTask.filterOptions = m.filterOptions
m.loadItemsTask.startIndex = 0
' Load Item Types
if getCollectionType() = "movies"
m.loadItemsTask.itemType = "Movie"
m.loadItemsTask.itemId = m.top.parentItem.Id
end if
' By default we load movies
m.loadItemsTask.studioIds = ""
m.loadItemsTask.view = "Movies"
m.itemGrid.translation = "[96, 650]"
m.itemGrid.itemSize = "[230, 310]"
m.itemGrid.rowHeights = "[310]"
m.itemGrid.numRows = "2"
m.selectedMovieOverview.visible = true
m.infoGroup.visible = true
m.top.showItemTitles = "hidealways"
if m.options.view = "Studios" or m.view = "Studios"
m.itemGrid.translation = "[96, 60]"
m.itemGrid.numRows = "3"
m.loadItemsTask.view = "Networks"
m.top.imageDisplayMode = "scaleToFit"
m.selectedMovieOverview.visible = false
m.infoGroup.visible = false
else if LCase(m.options.view) = "moviesgrid" or LCase(m.view) = "moviesgrid"
m.itemGrid.translation = "[96, 60]"
m.itemGrid.numRows = "3"
m.selectedMovieOverview.visible = false
m.infoGroup.visible = false
m.top.showItemTitles = m.global.session.user.settings["itemgrid.gridTitles"]
if LCase(m.top.showItemTitles) = "hidealways"
m.itemGrid.itemSize = "[230, 315]"
m.itemGrid.rowHeights = "[315]"
else
m.itemGrid.itemSize = "[230, 350]"
m.itemGrid.rowHeights = "[350]"
end if
else if m.options.view = "Genres" or m.view = "Genres"
m.loadItemsTask.StudioIds = m.top.parentItem.Id
m.loadItemsTask.view = "Genres"
m.movieLogo.visible = false
m.selectedMovieName.visible = false
m.selectedMovieOverview.visible = false
m.infoGroup.visible = false
end if
m.loadItemsTask.observeField("content", "ItemDataLoaded")
m.loadItemsTask.control = "RUN"
m.getFiltersTask.observeField("filters", "FilterDataLoaded")
m.getFiltersTask.params = {
userid: m.global.session.user.id,
parentid: m.top.parentItem.Id,
includeitemtypes: "Movie"
}
m.getFiltersTask.control = "RUN"
end sub
' Set Movies view, sort, and filter options
sub setMoviesOptions(options)
options.views = [
{ "Title": tr("Movies (Presentation)"), "Name": "Movies" },
{ "Title": tr("Movies (Grid)"), "Name": "MoviesGrid" },
{ "Title": tr("Studios"), "Name": "Studios" },
{ "Title": tr("Genres"), "Name": "Genres" }
]
if m.top.parentItem.json.type = "Genre"
options.views = [
{ "Title": tr("Movies (Presentation)"), "Name": "Movies" },
{ "Title": tr("Movies (Grid)"), "Name": "MoviesGrid" },
]
end if
options.sort = [
{ "Title": tr("TITLE"), "Name": "SortName" },
{ "Title": tr("IMDB_RATING"), "Name": "CommunityRating" },
{ "Title": tr("CRITIC_RATING"), "Name": "CriticRating" },
{ "Title": tr("DATE_ADDED"), "Name": "DateCreated" },
{ "Title": tr("DATE_PLAYED"), "Name": "DatePlayed" },
{ "Title": tr("OFFICIAL_RATING"), "Name": "OfficialRating" },
{ "Title": tr("PLAY_COUNT"), "Name": "PlayCount" },
{ "Title": tr("RELEASE_DATE"), "Name": "PremiereDate" },
{ "Title": tr("RUNTIME"), "Name": "Runtime" }
]
options.filter = [
{ "Title": tr("All"), "Name": "All" },
{ "Title": tr("Favorites"), "Name": "Favorites" },
{ "Title": tr("Played"), "Name": "Played" },
{ "Title": tr("Unplayed"), "Name": "Unplayed" },
{ "Title": tr("Resumable"), "Name": "Resumable" }
]
if m.options.view = "Genres" or m.view = "Genres"
options.sort = [{ "Title": tr("TITLE"), "Name": "SortName" }]
options.filter = []
end if
if m.options.view = "Studios" or m.view = "Studios"
options.sort = [
{ "Title": tr("TITLE"), "Name": "SortName" },
{ "Title": tr("DATE_ADDED"), "Name": "DateCreated" },
]
options.filter = [
{ "Title": tr("All"), "Name": "All" },
{ "Title": tr("Favorites"), "Name": "Favorites" }
]
end if
end sub
' Return parent collection type
function getCollectionType() as string
if m.top.parentItem.collectionType = invalid
return m.top.parentItem.Type
else
return m.top.parentItem.CollectionType
end if
end function
' Search string array for search value. Return if it's found
function inStringArray(array, searchValue) as boolean
for each item in array
if lcase(item) = lcase(searchValue) then return true
end for
return false
end function
' Data to display when options button selected
sub setSelectedOptions(options)
' Set selected view option
for each o in options.views
if o.Name = m.view
o.Selected = true
o.Ascending = m.sortAscending
m.options.view = o.Name
end if
end for
' Set selected sort option
for each o in options.sort
if o.Name = m.sortField
o.Selected = true
o.Ascending = m.sortAscending
m.options.sortField = o.Name
end if
end for
' Set selected filter
for each o in options.filter
if o.Name = m.filter
o.Selected = true
m.options.filter = o.Name
end if
' Select selected filter options
if isValid(o.options) and isValid(m.filterOptions)
if o.options.Count() > 0 and m.filterOptions.Count() > 0
if LCase(o.Name) = LCase(m.filterOptions.keys()[0])
selectedFilterOptions = m.filterOptions[m.filterOptions.keys()[0]].split(o.delimiter)
checkedState = []
for each availableFilterOption in o.options
matchFound = false
for each selectedFilterOption in selectedFilterOptions
if LCase(toString(availableFilterOption).trim()) = LCase(selectedFilterOption.trim())
matchFound = true
end if
end for
checkedState.push(matchFound)
end for
o.checkedState = checkedState
end if
end if
end if
end for
m.options.options = options
end sub
'
' Logo Image Loaded Event Handler
sub FilterDataLoaded(msg)
options = {}
options.filter = []
options.favorite = []
setMoviesOptions(options)
data = msg.GetData()
m.getFiltersTask.unobserveField("filters")
if not isValid(data) then return
' Add Movie filters from the API data
if LCase(m.loadItemsTask.view) = "movies"
if isValid(data.genres)
options.filter.push({ "Title": tr("Genres"), "Name": "Genres", "Options": data.genres, "Delimiter": "|", "CheckedState": [] })
end if
if isValid(data.OfficialRatings)
options.filter.push({ "Title": tr("Parental Ratings"), "Name": "OfficialRatings", "Options": data.OfficialRatings, "Delimiter": "|", "CheckedState": [] })
end if
if isValid(data.Years)
options.filter.push({ "Title": tr("Years"), "Name": "Years", "Options": data.Years, "Delimiter": ",", "CheckedState": [] })
end if
end if
setSelectedOptions(options)
m.options.options = options
end sub
'
' Logo Image Loaded Event Handler
sub LogoImageLoaded(msg)
data = msg.GetData()
m.loadLogoTask.unobserveField("content")
m.loadLogoTask.content = []
if data.Count() > 0
m.movieLogo.uri = data[0]
m.movieLogo.visible = true
else
m.selectedMovieName.visible = true
end if
end sub
'
'Handle loaded data, and add to Grid
sub ItemDataLoaded(msg)
m.top.alphaActive = false
itemData = msg.GetData()
m.loadItemsTask.unobserveField("content")
m.loadItemsTask.content = []
if itemData = invalid
m.Loading = false
return
end if
if m.loadItemsTask.view = "Genres"
' Reset genre list data
m.genreData.removeChildren(m.genreData.getChildren(-1, 0))
for each item in itemData
m.genreData.appendChild(item)
end for
m.itemGrid.opacity = "0"
m.genreList.opacity = "1"
m.itemGrid.setFocus(false)
m.genreList.setFocus(true)
m.loading = false
2023-11-30 01:22:26 +00:00
stopLoadingSpinner()
2023-10-06 03:18:36 +00:00
' Return focus to options menu if it was opened while library was loading
if m.options.visible
m.options.setFocus(true)
end if
return
end if
m.itemGrid.opacity = "1"
m.genreList.opacity = "0"
m.itemGrid.setFocus(true)
m.genreList.setFocus(false)
if m.data.getChildCount() = 0
m.itemGrid.jumpToItem = 0
end if
for each item in itemData
m.data.appendChild(item)
end for
'Update the stored counts
m.loadedItems = m.itemGrid.content.getChildCount()
m.loadedRows = m.loadedItems / m.itemGrid.numColumns
m.Loading = false
'If there are no items to display, show message
if m.loadedItems = 0
m.selectedMovieOverview.visible = false
m.infoGroup.visible = false
m.movieLogo.visible = false
m.movieLogo.uri = ""
m.selectedMovieName.visible = false
SetName("")
SetOverview("")
SetOfficialRating("")
SetProductionYear("")
setFieldText("runtime", "")
setFieldText("communityRating", "")
setFieldText("criticRatingLabel", "")
m.criticRatingIcon.uri = ""
m.star.uri = ""
m.emptyText.text = tr("NO_ITEMS").Replace("%1", m.top.parentItem.Type)
m.emptyText.visible = true
end if
2023-11-30 01:22:26 +00:00
stopLoadingSpinner()
2023-10-06 03:18:36 +00:00
' Return focus to options menu if it was opened while library was loading
if m.options.visible
m.options.setFocus(true)
end if
end sub
'
'Set Selected Movie Name
sub SetName(movieName as string)
m.selectedMovieName.text = movieName
end sub
'
'Set Selected Movie Overview
sub SetOverview(movieOverview as string)
m.selectedMovieOverview.text = movieOverview
end sub
'
'Set Selected Movie OfficialRating
sub SetOfficialRating(movieOfficialRating as string)
m.selectedMovieOfficialRating.text = movieOfficialRating
end sub
'
'Set Selected Movie ProductionYear
sub SetProductionYear(movieProductionYear)
m.selectedMovieProductionYear.text = movieProductionYear
end sub
'
'Set Background Image
sub SetBackground(backgroundUri as string)
if backgroundUri = ""
m.backdrop.opacity = 0
end if
'If a new image is being loaded, or transitioned to, store URL to load next
if m.swapAnimation.state &lt;> "stopped" or m.newBackdrop.loadStatus = "loading"
m.queuedBGUri = backgroundUri
return
end if
m.newBackdrop.uri = backgroundUri
end sub
'
'Handle new item being focused
sub onItemFocused()
focusedRow = m.itemGrid.currFocusRow
itemInt = m.itemGrid.itemFocused
' If no selected item, set background to parent backdrop
if itemInt = -1
return
end if
m.movieLogo.visible = false
m.selectedMovieName.visible = false
' Load more data if focus is within last 5 rows, and there are more items to load
if focusedRow >= m.loadedRows - 5 and m.loadeditems &lt; m.loadItemsTask.totalRecordCount
loadMoreData()
end if
m.selectedFavoriteItem = getItemFocused()
m.communityRatingGroup.visible = false
m.criticRatingGroup.visible = false
if not isValid(m.selectedFavoriteItem)
return
end if
if LCase(m.options.view) = "studios" or LCase(m.view) = "studios"
return
else if LCase(m.options.view) = "moviesgrid" or LCase(m.view) = "moviesgrid"
return
end if
itemData = m.selectedFavoriteItem.json
m.star.uri = "pkg:/images/sharp_star_white_18dp.png"
if isValid(itemData.communityRating)
setFieldText("communityRating", int(itemData.communityRating * 10) / 10)
m.communityRatingGroup.visible = true
end if
if isValid(itemData.CriticRating)
setFieldText("criticRatingLabel", itemData.criticRating)
tomato = "pkg:/images/rotten.png"
if itemData.CriticRating > 60
tomato = "pkg:/images/fresh.png"
end if
m.criticRatingIcon.uri = tomato
m.criticRatingGroup.visible = true
end if
if isValid(itemData.Name)
SetName(itemData.Name)
else
SetName("")
end if
if isValid(itemData.Overview)
SetOverview(itemData.Overview)
else
SetOverview("")
end if
if isValid(itemData.ProductionYear)
SetProductionYear(str(itemData.ProductionYear))
else
SetProductionYear("")
end if
if type(itemData.RunTimeTicks) = "LongInteger"
setFieldText("runtime", stri(getRuntime(itemData.RunTimeTicks)) + " mins")
else
setFieldText("runtime", "")
end if
if isValid(itemData.OfficialRating)
SetOfficialRating(itemData.OfficialRating)
else
SetOfficialRating("")
end if
m.loadLogoTask.itemId = itemData.id
m.loadLogoTask.itemType = "LogoImage"
m.loadLogoTask.observeField("content", "LogoImageLoaded")
m.loadLogoTask.control = "RUN"
' Set Background to item backdrop
SetBackground(m.selectedFavoriteItem.backdropUrl)
end sub
function getRuntime(runTimeTicks) as integer
return round(runTimeTicks / 600000000.0)
end function
function round(f as float) as integer
' BrightScript only has a "floor" round
' This compares floor to floor + 1 to find which is closer
m = int(f)
n = m + 1
x = abs(f - m)
y = abs(f - n)
if y > x
return m
else
return n
end if
end function
sub setFieldText(field, value)
node = m.top.findNode(field)
if node = invalid or value = invalid then return
' Handle non strings... Which _shouldn't_ happen, but hey
if type(value) = "roInt" or type(value) = "Integer"
value = str(value)
else if type(value) = "roFloat" or type(value) = "Float"
value = str(value)
else if type(value) &lt;> "roString" and type(value) &lt;> "String"
value = ""
end if
node.text = value
end sub
'
'When Image Loading Status changes
sub newBGLoaded()
'If image load was sucessful, start the fade swap
if m.newBackdrop.loadStatus = "ready"
m.swapAnimation.control = "start"
end if
end sub
'
'Swap Complete
sub swapDone()
if m.swapAnimation.state = "stopped"
'Set main BG node image and hide transitioning node
m.backdrop.uri = m.newBackdrop.uri
m.backdrop.opacity = 1
m.newBackdrop.opacity = 0
'If there is another one to load
if m.newBackdrop.uri &lt;> m.queuedBGUri and m.queuedBGUri &lt;> ""
SetBackground(m.queuedBGUri)
m.queuedBGUri = ""
end if
end if
end sub
'
'Load next set of items
sub loadMoreData()
if m.Loading = true then return
2023-12-04 21:50:15 +00:00
startLoadingSpinner(false)
2023-10-06 03:18:36 +00:00
m.Loading = true
m.loadItemsTask.startIndex = m.loadedItems
m.loadItemsTask.observeField("content", "ItemDataLoaded")
m.loadItemsTask.control = "RUN"
end sub
'
'Item Selected
sub onItemSelected()
m.top.selectedItem = m.itemGrid.content.getChild(m.itemGrid.itemSelected)
end sub
'
'Returns Focused Item
function getItemFocused()
2023-10-28 21:26:12 +00:00
if m.itemGrid.isinFocusChain() and isValid(m.itemGrid.itemFocused)
return m.itemGrid.content.getChild(m.itemGrid.itemFocused)
else if m.genreList.isinFocusChain() and isValid(m.genreList.rowItemFocused)
return m.genreList.content.getChild(m.genreList.rowItemFocused[0]).getChild(m.genreList.rowItemFocused[1])
end if
return invalid
2023-10-06 03:18:36 +00:00
end function
'
'Genre Item Selected
sub onGenreItemSelected()
m.top.selectedItem = m.genreList.content.getChild(m.genreList.rowItemSelected[0]).getChild(m.genreList.rowItemSelected[1])
end sub
sub onItemalphaSelected()
if m.top.alphaSelected &lt;> ""
m.loadedRows = 0
m.loadedItems = 0
m.data = CreateObject("roSGNode", "ContentNode")
m.itemGrid.content = m.data
m.genreData = CreateObject("roSGNode", "ContentNode")
m.genreList.content = m.genreData
m.loadItemsTask.searchTerm = ""
m.VoiceBox.text = ""
m.loadItemsTask.nameStartsWith = m.alpha.itemAlphaSelected
loadInitialItems()
end if
end sub
sub onvoiceFilter()
if m.VoiceBox.text &lt;> ""
m.loadedRows = 0
m.loadedItems = 0
m.data = CreateObject("roSGNode", "ContentNode")
m.itemGrid.content = m.data
m.top.alphaSelected = ""
m.loadItemsTask.NameStartsWith = " "
m.loadItemsTask.searchTerm = m.voiceBox.text
m.loadItemsTask.recursive = true
loadInitialItems()
end if
end sub
'
'Check if options updated and any reloading required
sub optionsClosed()
reload = false
if m.options.sortField &lt;> m.sortField or m.options.sortAscending &lt;> m.sortAscending
m.sortField = m.options.sortField
m.sortAscending = m.options.sortAscending
reload = true
sortAscendingStr = "true"
'Store sort settings
if not m.sortAscending
sortAscendingStr = "false"
end if
set_user_setting("display." + m.top.parentItem.Id + ".sortField", m.sortField)
set_user_setting("display." + m.top.parentItem.Id + ".sortAscending", sortAscendingStr)
end if
if m.options.filter &lt;> m.filter
m.filter = m.options.filter
reload = true
set_user_setting("display." + m.top.parentItem.Id + ".filter", m.options.filter)
end if
if not isValid(m.options.filterOptions)
m.filterOptions = {}
end if
if not AssocArrayEqual(m.options.filterOptions, m.filterOptions)
m.filterOptions = m.options.filterOptions
reload = true
set_user_setting("display." + m.top.parentItem.Id + ".filterOptions", FormatJson(m.options.filterOptions))
end if
m.view = m.global.session.user.settings["display." + m.top.parentItem.Id + ".landing"]
if m.options.view &lt;> m.view
m.view = m.options.view
set_user_setting("display." + m.top.parentItem.Id + ".landing", m.view)
' Reset any filtering or search terms
m.top.alphaSelected = ""
m.loadItemsTask.NameStartsWith = " "
m.loadItemsTask.searchTerm = ""
m.filter = "All"
m.filterOptions = {}
m.sortField = "SortName"
m.sortAscending = true
' Reset view to defaults
set_user_setting("display." + m.top.parentItem.Id + ".sortField", m.sortField)
set_user_setting("display." + m.top.parentItem.Id + ".sortAscending", "true")
set_user_setting("display." + m.top.parentItem.Id + ".filter", m.filter)
set_user_setting("display." + m.top.parentItem.Id + ".filterOptions", FormatJson(m.filterOptions))
reload = true
end if
if reload
m.loadedRows = 0
m.loadedItems = 0
m.data = CreateObject("roSGNode", "ContentNode")
m.itemGrid.content = m.data
loadInitialItems()
end if
m.itemGrid.setFocus(m.itemGrid.opacity = 1)
m.genreList.setFocus(m.genreList.opacity = 1)
end sub
sub onChannelSelected(msg)
node = msg.getRoSGNode()
m.top.lastFocus = lastFocusedChild(node)
if node.watchChannel &lt;> invalid
' Clone the node when it's reused/update in the TimeGrid it doesn't automatically start playing
m.top.selectedItem = node.watchChannel.clone(false)
end if
end sub
function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
if key = "left" and m.voiceBox.isinFocusChain()
m.itemGrid.setFocus(m.itemGrid.opacity = 1)
m.genreList.setFocus(m.genreList.opacity = 1)
m.voiceBox.setFocus(false)
end if
if key = "options"
if m.options.visible = true
m.options.visible = false
m.top.removeChild(m.options)
optionsClosed()
else
itemSelected = m.selectedFavoriteItem
if itemSelected &lt;> invalid
m.options.selectedFavoriteItem = itemSelected
end if
m.options.visible = true
m.top.appendChild(m.options)
m.options.setFocus(true)
end if
return true
else if key = "back"
if m.options.visible = true
m.options.visible = false
optionsClosed()
return true
else
m.global.sceneManager.callfunc("popScene")
m.loadItemsTask.control = "stop"
return true
end if
2023-10-28 21:26:12 +00:00
else if key = "play"
2023-10-06 03:18:36 +00:00
itemToPlay = getItemFocused()
2023-10-28 21:26:12 +00:00
if itemToPlay &lt;> invalid
2023-10-06 03:18:36 +00:00
m.top.quickPlayNode = itemToPlay
return true
end if
else if key = "left"
if m.itemGrid.isinFocusChain()
m.top.alphaActive = true
m.itemGrid.setFocus(false)
alpha = m.alpha.getChild(0).findNode("Alphamenu")
alpha.setFocus(true)
return true
else if m.genreList.isinFocusChain()
m.top.alphaActive = true
m.genreList.setFocus(false)
alpha = m.alpha.getChild(0).findNode("Alphamenu")
alpha.setFocus(true)
return true
end if
else if key = "right" and m.Alpha.isinFocusChain()
m.top.alphaActive = false
m.Alpha.setFocus(false)
m.Alpha.visible = true
m.itemGrid.setFocus(m.itemGrid.opacity = 1)
m.genreList.setFocus(m.genreList.opacity = 1)
return true
else if key = "replay" and m.itemGrid.isinFocusChain()
if m.resetGrid = true
m.itemGrid.animateToItem = 0
else
m.itemGrid.jumpToItem = 0
end if
return true
else if key = "replay" and m.genreList.isinFocusChain()
if m.resetGrid = true
m.genreList.animateToItem = 0
else
m.genreList.jumpToItem = 0
end if
return true
end if
if key = "replay"
m.loadItemsTask.searchTerm = ""
m.loadItemsTask.nameStartsWith = ""
m.voiceBox.text = ""
m.top.alphaSelected = ""
m.loadItemsTask.filter = "All"
m.filter = "All"
m.filterOptions = {}
m.data = CreateObject("roSGNode", "ContentNode")
m.itemGrid.content = m.data
loadInitialItems()
return true
end if
return false
end function
2023-12-05 16:56:00 +00:00
</code></pre></article></section><footer class="footer" id="PeOAagUepe"><div class="wrapper"><span class="jsdoc-message">Automatically generated using <a href="https://github.com/jsdoc/jsdoc" target="_blank">JSDoc</a> and the <a href="https://github.com/ankitskvmdam/clean-jsdoc-theme" target="_blank">clean-jsdoc-theme</a>.</span></div></footer></div></div></div><div class="search-container" id="PkfLWpAbet" style="display:none"><div class="wrapper" id="iCxFxjkHbP"><button class="icon-button search-close-button" id="VjLlGakifb" aria-label="close search"><svg><use xlink:href="#close-icon"></use></svg></button><div class="search-box-c"><svg><use xlink:href="#search-icon"></use></svg> <input type="text" id="vpcKVYIppa" class="search-input" placeholder="Search..." autofocus></div><div class="search-result-c" id="fWwVHRuDuN"><span class="search-result-c-text">Type anything to view search result</span></div></div></div><div class="mobile-menu-icon-container"><button class="icon-button" id="mobile-menu" data-isopen="false" aria-label="menu"><svg><use xlink:href="#menu-icon"></use></svg></button></div><div id="mobile-sidebar" class="mobile-sidebar-container"><div class="mobile-sidebar-wrapper"><a href="/" class="sidebar-title sidebar-title-anchor">jellyfin-roku Code Documentation</a><div class="mobile-nav-links"><div class="external-link navbar-item"><a id="jellyfin-link-mobile" href="https://jellyfin.org/" target="_blank">Jellyfin</a></div><div class="external-link navbar-item"><a id="github-link-mobile" href="https://github.com/jellyfin/jellyfin-roku" target="_blank">GitHub</a></div><div class="external-link navbar-item"><a id="forum-link-mobile" href="https://forum.jellyfin.org/f-roku-development" target="_blank">Forum</a></div><div class="external-link navbar-item"><a id="matrix-link-mobile" href="https://matrix.to/#/#jellyfin-dev-roku:matrix.org" target="_blank">Matrix</a></div></div><div class="mobile-sidebar-items-c"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-AlbumData.html">AlbumData</a></div><div class="sidebar-section-children"><a href="module-AlbumGrid.html">AlbumGrid</a></div><div class="sidebar-section-children"><a href="module-AlbumTrackList.html">AlbumTrackList</a></div><div class="sidebar-section-children"><a href="module-AlbumView.html">AlbumView</a></div><div class="sidebar-section-children"><a href="module-Alpha.html">Alpha</a></div><div class="sidebar-section-children"><a href="module-ArtistView.html">ArtistView</a></div><div class="sidebar-section-children"><a href="module-AudioPlayer.html">AudioPlayer</a></div><div class="sidebar-section-children"><a href="module-AudioPlayerView.html">AudioPlayerView</a></div><div class="sidebar-section-children"><a href="module-AudioTrackListItem.html">AudioTrackListItem</a></div><div class="sidebar-section-children"><a href="module-ButtonGroupHoriz.html">ButtonGroupHoriz</a></div><div class="sidebar-section-children"><a href="module-ButtonGroupVert.html">ButtonGroupVert</a></div><div class="sidebar-section-children"><a href="module-ChannelData.html">ChannelData</a></div><div class="sidebar-section-children"><a href="module-Clock.html">Clock</a></div><div class="sidebar-section-children"><a href="module-CollectionData.html">CollectionData</a></div><div class="sidebar-section-children"><a href="module-ConfigData.html">ConfigData</a></div><div class="sidebar-section-children"><a href="module-ConfigItem.html">ConfigItem</a></div><div class="sidebar-section-children"><a href="module-ConfigList.html">ConfigList</a></div><div class="sidebar-section-children"><a href="module-ExtrasItem.html">ExtrasItem</a></div><div class="sidebar-section-children"><a href="module-ExtrasRowList.html">ExtrasRowList</a></div><div class="sidebar-section-children"><a href="module-FavoriteItemsTask.html">FavoriteItemsTask</a></div><div class="sidebar-section-children"><a href="module-Folder