jf-roku/docs/api/components_music_AudioPlayerView.bs.html
2024-02-23 01:34:08 +00:00

827 lines
64 KiB
HTML

<!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/music/AudioPlayerView.bs</title><!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![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="M0 0h24v24H0z"/><path d="M16.5 13.202L13 15.535v3.596L19.197 15 16.5 13.202zM14.697 12L12 10.202 9.303 12 12 13.798 14.697 12zM20 10.869L18.303 12 20 13.131V10.87zM19.197 9L13 4.869v3.596l3.5 2.333L19.197 9zM7.5 10.798L11 8.465V4.869L4.803 9 7.5 10.798zM4.803 15L11 19.131v-3.596l-3.5-2.333L4.803 15zM4 13.131L5.697 12 4 10.869v2.262zM2 9a1 1 0 0 1 .445-.832l9-6a1 1 0 0 1 1.11 0l9 6A1 1 0 0 1 22 9v6a1 1 0 0 1-.445.832l-9 6a1 1 0 0 1-1.11 0l-9-6A1 1 0 0 1 2 15V9z"/></symbol><symbol id="close-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></symbol><symbol id="menu-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"/></symbol></defs></svg></head><body data-theme="fallback-dark"><div class="sidebar-container"><div class="sidebar" id="sidebar"><a href="/" class="sidebar-title sidebar-title-anchor">jellyfin-roku Code Documentation</a><div class="sidebar-items-container"><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-FolderData.html">FolderData</a></div><div class="sidebar-section-children"><a href="module-GetFiltersTask.html">GetFiltersTask</a></div><div class="sidebar-section-children"><a href="module-GetNextEpisodeTask.html">GetNextEpisodeTask</a></div><div class="sidebar-section-children"><a href="module-GetPlaybackInfoTask.html">GetPlaybackInfoTask</a></div><div class="sidebar-section-children"><a href="module-GetShuffleEpisodesTask.html">GetShuffleEpisodesTask</a></div><div class="sidebar-section-children"><a href="module-GridItem.html">GridItem</a></div><div class="sidebar-section-children"><a href="module-GridItemSmall.html">GridItemSmall</a></div><div class="sidebar-section-children"><a href="module-Home.html">Home</a></div><div class="sidebar-section-children"><a href="module-HomeData.html">HomeData</a></div><div class="sidebar-section-children"><a href="module-HomeItem.html">HomeItem</a></div><div class="sidebar-section-children"><a href="module-HomeRowItemSizes.html">HomeRowItemSizes</a></div><div class="sidebar-section-children"><a href="module-HomeRows.html">HomeRows</a></div><div class="sidebar-section-children"><a href="module-IconButton.html">IconButton</a></div><div class="sidebar-section-children"><a href="module-Image.html">Image</a></div><div class="sidebar-section-children"><a href="module-ImageData.html">ImageData</a></div><div class="sidebar-section-children"><a href="module-IntegerKeyboard.html">IntegerKeyboard</a></div><div class="sidebar-section-children"><a href="module-ItemGrid.html">ItemGrid</a></div><div class="sidebar-section-children"><a href="module-ItemGridOptions.html">ItemGridOptions</a></div><div class="sidebar-section-children"><a href="module-Items.html">Items</a></div><div class="sidebar-section-children"><a href="module-JFButton.html">JFButton</a></div><div class="sidebar-section-children"><a href="module-JFButtons.html">JFButtons</a></div><div class="sidebar-section-children"><a href="module-JFContentItem.html">JFContentItem</a></div><div class="sidebar-section-children"><a href="module-JFGroup.html">JFGroup</a></div><div class="sidebar-section-children"><a href="module-JFMessageDialog.html">JFMessageDialog</a></div><div class="sidebar-section-children"><a href="module-JFOverhang.html">JFOverhang</a></div><div class="sidebar-section-children"><a href="module-JFScene.html">JFScene</a></div><div class="sidebar-section-children"><a href="module-JFScreen.html">JFScreen</a></div><div class="sidebar-section-children"><a href="module-JFServer.html">JFServer</a></div><div class="sidebar-section-children"><a href="module-JFVideo.html">JFVideo</a></div><div class="sidebar-section-children"><a href="module-ListPoster.html">ListPoster</a></div><div class="sidebar-section-children"><a href="module-LoadChannelsTask.html">LoadChannelsTask</a></div><div class="sidebar-section-children"><a href="module-LoadItemsTask.html">LoadItemsTask</a></div><div class="sidebar-section-children"><a href="module-LoadItemsTask2.html">LoadItemsTask2</a></div><div class="sidebar-section-children"><a href="module-LoadPhotoTask.html">LoadPhotoTask</a></div><div class="sidebar-section-children"><a href="module-LoadProgramDetailsTask.html">LoadProgramDetailsTask</a></div><div class="sidebar-section-children"><a href="module-LoadScreenSaverTimeoutTask.html">LoadScreenSaverTimeoutTask</a></div><div class="sidebar-section-children"><a href="module-LoadSheduleTask.html">LoadSheduleTask</a></div><div class="sidebar-section-children"><a href="module-LoadVideoContentTask.html">LoadVideoContentTask</a></div><div class="sidebar-section-children"><a href="module-LoginScene.html">LoginScene</a></div><div class="sidebar-section-children"><a href="module-Main.html">Main</a></div><div class="sidebar-section-children"><a href="module-MovieData.html">MovieData</a></div><div class="sidebar-section-children"><a href="module-MovieDetails.html">MovieDetails</a></div><div class="sidebar-section-children"><a href="module-MovieLibraryView.html">MovieLibraryView</a></div><div class="sidebar-section-children"><a href="module-MovieOptions.html">MovieOptions</a></div><div class="sidebar-section-children"><a href="module-MusicAlbumData.html">MusicAlbumData</a></div><div class="sidebar-section-children"><a href="module-MusicAlbumSongListData.html">MusicAlbumSongListData</a></div><div class="sidebar-section-children"><a href="module-MusicArtistData.html">MusicArtistData</a></div><div class="sidebar-section-children"><a href="module-MusicArtistGridItem.html">MusicArtistGridItem</a></div><div class="sidebar-section-children"><a href="module-MusicLibraryView.html">MusicLibraryView</a></div><div class="sidebar-section-children"><a href="module-MusicSongData.html">MusicSongData</a></div><div class="sidebar-section-children"><a href="module-OSD.html">OSD</a></div><div class="sidebar-section-children"><a href="module-OptionNode.html">OptionNode</a></div><div class="sidebar-section-children"><a href="module-OptionsButton.html">OptionsButton</a></div><div class="sidebar-section-children"><a href="module-OptionsData.html">OptionsData</a></div><div class="sidebar-section-children"><a href="module-OptionsSlider.html">OptionsSlider</a></div><div class="sidebar-section-children"><a href="module-OverviewDialog.html">OverviewDialog</a></div><div class="sidebar-section-children"><a href="module-PersonData.html">PersonData</a></div><div class="sidebar-section-children"><a href="module-PersonDetails.html">PersonDetails</a></div><div class="sidebar-section-children"><a href="module-PhotoData.html">PhotoData</a></div><div class="sidebar-section-children"><a href="module-PhotoDetails.html">PhotoDetails</a></div><div class="sidebar-section-children"><a href="module-PlaybackDialog.html">PlaybackDialog</a></div><div class="sidebar-section-children"><a href="module-PlayedCheckmark.html">PlayedCheckmark</a></div><div class="sidebar-section-children"><a href="module-PlaylistData.html">PlaylistData</a></div><div class="sidebar-section-children"><a href="module-PlaylistView.html">PlaylistView</a></div><div class="sidebar-section-children"><a href="module-PlaystateTask.html">PlaystateTask</a></div><div class="sidebar-section-children"><a href="module-PostTask.html">PostTask</a></div><div class="sidebar-section-children"><a href="module-ProgramDetails.html">ProgramDetails</a></div><div class="sidebar-section-children"><a href="module-PublicUserData.html">PublicUserData</a></div><div class="sidebar-section-children"><a href="module-QueueManager.html">QueueManager</a></div><div class="sidebar-section-children"><a href="module-QuickConnect.html">QuickConnect</a></div><div class="sidebar-section-children"><a href="module-QuickConnectDialog.html">QuickConnectDialog</a></div><div class="sidebar-section-children"><a href="module-RadioDialog.html">RadioDialog</a></div><div class="sidebar-section-children"><a href="module-RecordProgramTask.html">RecordProgramTask</a></div><div class="sidebar-section-children"><a href="module-RecordingData.html">RecordingData</a></div><div class="sidebar-section-children"><a href="module-SceneManager.html">SceneManager</a></div><div class="sidebar-section-children"><a href="module-ScheduleProgramData.html">ScheduleProgramData</a></div><div class="sidebar-section-children"><a href="module-SearchBox.html">SearchBox</a></div><div class="sidebar-section-children"><a href="module-SearchData.html">SearchData</a></div><div class="sidebar-section-children"><a href="module-SearchResults.html">SearchResults</a></div><div class="sidebar-section-children"><a href="module-SearchRow.html">SearchRow</a></div><div class="sidebar-section-children"><a href="module-SearchTask.html">SearchTask</a></div><div class="sidebar-section-children"><a href="module-SeriesData.html">SeriesData</a></div><div class="sidebar-section-children"><a href="module-ServerDiscoveryTask.html">ServerDiscoveryTask</a></div><div class="sidebar-section-children"><a href="module-SetServerScreen.html">SetServerScreen</a></div><div class="sidebar-section-children"><a href="module-ShowScenes.html">ShowScenes</a></div><div class="sidebar-section-children"><a href="module-SlideOutButton.html">SlideOutButton</a></div><div class="sidebar-section-children"><a href="module-SongItem.html">SongItem</a></div><div class="sidebar-section-children"><a href="module-Spinner.html">Spinner</a></div><div class="sidebar-section-children"><a href="module-StandardDialog.html">StandardDialog</a></div><div class="sidebar-section-children"><a href="module-Subtitles.html">Subtitles</a></div><div class="sidebar-section-children"><a href="module-TVEpisode.html">TVEpisode</a></div><div class="sidebar-section-children"><a href="module-TVEpisodeData.html">TVEpisodeData</a></div><div class="sidebar-section-children"><a href="module-TVEpisodeRow.html">TVEpisodeRow</a></div><div class="sidebar-section-children"><a href="module-TVEpisodeRowWithOptions.html">TVEpisodeRowWithOptions</a></div><div class="sidebar-section-children"><a href="module-TVEpisodes.html">TVEpisodes</a></div><div class="sidebar-section-children"><a href="module-TVListDetails.html">TVListDetails</a></div><div class="sidebar-section-children"><a href="module-TVListOptions.html">TVListOptions</a></div><div class="sidebar-section-children"><a href="module-TVSeasonData.html">TVSeasonData</a></div><div class="sidebar-section-children"><a href="module-TVSeasonRow.html">TVSeasonRow</a></div><div class="sidebar-section-children"><a href="module-TVShowDescription.html">TVShowDescription</a></div><div class="sidebar-section-children"><a href="module-TVShowDetails.html">TVShowDetails</a></div><div class="sidebar-section-children"><a href="module-TextSizeTask.html">TextSizeTask</a></div><div class="sidebar-section-children"><a href="module-UserData.html">UserData</a></div><div class="sidebar-section-children"><a href="module-UserItem.html">UserItem</a></div><div class="sidebar-section-children"><a href="module-UserLibrary.html">UserLibrary</a></div><div class="sidebar-section-children"><a href="module-UserRow.html">UserRow</a></div><div class="sidebar-section-children"><a href="module-UserSelect.html">UserSelect</a></div><div class="sidebar-section-children"><a href="module-VideoData.html">VideoData</a></div><div class="sidebar-section-children"><a href="module-VideoPlayer.html">VideoPlayer</a></div><div class="sidebar-section-children"><a href="module-VideoPlayerView.html">VideoPlayerView</a></div><div class="sidebar-section-children"><a href="module-VideoTrackListItem.html">VideoTrackListItem</a></div><div class="sidebar-section-children"><a href="module-ViewCreator.html">ViewCreator</a></div><div class="sidebar-section-children"><a href="module-WhatsNewDialog.html">WhatsNewDialog</a></div><div class="sidebar-section-children"><a href="module-baserequest.html">baserequest</a></div><div class="sidebar-section-children"><a href="module-captionTask.html">captionTask</a></div><div class="sidebar-section-children"><a href="module-conditional.html">conditional</a></div><div class="sidebar-section-children"><a href="module-config.html">config</a></div><div class="sidebar-section-children"><a href="module-deviceCapabilities.html">deviceCapabilities</a></div><div class="sidebar-section-children"><a href="module-globals.html">globals</a></div><div class="sidebar-section-children"><a href="module-homeRowItemSizes_.html">homeRowItemSizes</a></div><div class="sidebar-section-children"><a href="module-migrations.html">migrations</a></div><div class="sidebar-section-children"><a href="module-misc.html">misc</a></div><div class="sidebar-section-children"><a href="module-quickplay.html">quickplay</a></div><div class="sidebar-section-children"><a href="module-schedule.html">schedule</a></div><div class="sidebar-section-children"><a href="module-section.html">section</a></div><div class="sidebar-section-children"><a href="module-sectionScroller.html">sectionScroller</a></div><div class="sidebar-section-children"><a href="module-settings.html">settings</a></div><div class="sidebar-section-children"><a href="module-userauth.html">userauth</a></div></div></div></div></div><div class="navbar-container" id="VuAckcnZhf"><nav class="navbar"><div class="navbar-left-items"><div class="external-link navbar-item"><a id="jellyfin-link" href="https://jellyfin.org/" target="_blank">Jellyfin</a></div><div class="external-link navbar-item"><a id="github-link" href="https://github.com/jellyfin/jellyfin-roku" target="_blank">GitHub</a></div><div class="external-link navbar-item"><a id="forum-link" href="https://forum.jellyfin.org/f-roku-development" target="_blank">Forum</a></div><div class="external-link navbar-item"><a id="matrix-link" href="https://matrix.to/#/#jellyfin-dev-roku:matrix.org" target="_blank">Matrix</a></div></div><div class="navbar-right-items"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div><nav></nav></nav></div><div class="toc-container"><div class="toc-content"><span class="bold">On this page</span><div id="eed4d2a0bfd64539bb9df78095dec881"></div></div></div><div class="body-wrapper"><div class="main-content"><div class="main-wrapper"><section id="source-page" class="source-page"><header><h1 id="title" class="has-anchor">components_music_AudioPlayerView.bs</h1></header><article><pre class="prettyprint source lang-js"><code>import "pkg:/source/utils/misc.bs"
import "pkg:/source/api/Image.bs"
import "pkg:/source/api/baserequest.bs"
import "pkg:/source/utils/config.bs"
sub init()
m.top.optionsAvailable = false
m.inScrubMode = false
m.lastRecordedPositionTimestamp = 0
m.scrubTimestamp = -1
m.playlistTypeCount = m.global.queueManager.callFunc("getQueueUniqueTypes").count()
setupAudioNode()
setupAnimationTasks()
setupButtons()
setupInfoNodes()
setupDataTasks()
setupScreenSaver()
m.buttonCount = m.buttons.getChildCount()
m.seekPosition.translation = [720 - (m.seekPosition.width / 2), m.seekPosition.translation[1]]
m.screenSaverTimeout = 300
m.LoadScreenSaverTimeoutTask.observeField("content", "onScreensaverTimeoutLoaded")
m.LoadScreenSaverTimeoutTask.control = "RUN"
m.di = CreateObject("roDeviceInfo")
' Write screen tracker for screensaver
WriteAsciiFile("tmp:/scene.temp", "nowplaying")
MoveFile("tmp:/scene.temp", "tmp:/scene")
loadButtons()
pageContentChanged()
setShuffleIconState()
setLoopButtonImage()
m.buttons.setFocus(true)
end sub
sub onScreensaverTimeoutLoaded()
data = m.LoadScreenSaverTimeoutTask.content
m.LoadScreenSaverTimeoutTask.unobserveField("content")
if isValid(data)
m.screenSaverTimeout = data
end if
end sub
sub setupScreenSaver()
m.screenSaverBackground = m.top.FindNode("screenSaverBackground")
' Album Art Screensaver
m.screenSaverAlbumCover = m.top.FindNode("screenSaverAlbumCover")
m.screenSaverAlbumAnimation = m.top.findNode("screenSaverAlbumAnimation")
m.screenSaverAlbumCoverFadeIn = m.top.findNode("screenSaverAlbumCoverFadeIn")
' Jellyfin Screensaver
m.PosterOne = m.top.findNode("PosterOne")
m.PosterOne.uri = "pkg:/images/logo.png"
m.BounceAnimation = m.top.findNode("BounceAnimation")
m.PosterOneFadeIn = m.top.findNode("PosterOneFadeIn")
end sub
sub setupAnimationTasks()
m.displayButtonsAnimation = m.top.FindNode("displayButtonsAnimation")
m.playPositionAnimation = m.top.FindNode("playPositionAnimation")
m.playPositionAnimationWidth = m.top.FindNode("playPositionAnimationWidth")
m.bufferPositionAnimation = m.top.FindNode("bufferPositionAnimation")
m.bufferPositionAnimationWidth = m.top.FindNode("bufferPositionAnimationWidth")
m.screenSaverStartAnimation = m.top.FindNode("screenSaverStartAnimation")
end sub
' Creates tasks to gather data needed to render Scene and play song
sub setupDataTasks()
' Load meta data
m.LoadMetaDataTask = CreateObject("roSGNode", "LoadItemsTask")
m.LoadMetaDataTask.itemsToLoad = "metaData"
' Load background image
m.LoadBackdropImageTask = CreateObject("roSGNode", "LoadItemsTask")
m.LoadBackdropImageTask.itemsToLoad = "backdropImage"
' Load audio stream
m.LoadAudioStreamTask = CreateObject("roSGNode", "LoadItemsTask")
m.LoadAudioStreamTask.itemsToLoad = "audioStream"
m.LoadScreenSaverTimeoutTask = CreateObject("roSGNode", "LoadScreenSaverTimeoutTask")
end sub
' Creates audio node used to play song(s)
sub setupAudioNode()
m.global.audioPlayer.observeField("state", "audioStateChanged")
m.global.audioPlayer.observeField("position", "audioPositionChanged")
m.global.audioPlayer.observeField("bufferingStatus", "bufferPositionChanged")
end sub
' Setup playback buttons, default to Play button selected
sub setupButtons()
m.buttons = m.top.findNode("buttons")
m.top.observeField("selectedButtonIndex", "onButtonSelectedChange")
' If we're playing a mixed playlist, remove the shuffle and loop buttons
if m.playlistTypeCount > 1
shuffleButton = m.top.findNode("shuffle")
m.buttons.removeChild(shuffleButton)
loopButton = m.top.findNode("loop")
m.buttons.removeChild(loopButton)
m.previouslySelectedButtonIndex = 0
m.top.selectedButtonIndex = 1
return
end if
m.previouslySelectedButtonIndex = 1
m.top.selectedButtonIndex = 2
end sub
' Event handler when user selected a different playback button
sub onButtonSelectedChange()
' Change previously selected button back to default image
selectedButton = m.buttons.getChild(m.previouslySelectedButtonIndex)
selectedButton.uri = selectedButton.uri.Replace("-selected", "-default")
' Change selected button image to selected image
selectedButton = m.buttons.getChild(m.top.selectedButtonIndex)
selectedButton.uri = selectedButton.uri.Replace("-default", "-selected")
end sub
sub setupInfoNodes()
m.albumCover = m.top.findNode("albumCover")
m.backDrop = m.top.findNode("backdrop")
m.playPosition = m.top.findNode("playPosition")
m.bufferPosition = m.top.findNode("bufferPosition")
m.seekBar = m.top.findNode("seekBar")
m.thumb = m.top.findNode("thumb")
m.shuffleIndicator = m.top.findNode("shuffleIndicator")
m.loopIndicator = m.top.findNode("loopIndicator")
m.positionTimestamp = m.top.findNode("positionTimestamp")
m.seekPosition = m.top.findNode("seekPosition")
m.seekTimestamp = m.top.findNode("seekTimestamp")
m.totalLengthTimestamp = m.top.findNode("totalLengthTimestamp")
end sub
sub bufferPositionChanged()
if m.inScrubMode then return
if not isValid(m.global.audioPlayer.bufferingStatus)
bufferPositionBarWidth = m.seekBar.width
else
bufferPositionBarWidth = m.seekBar.width * m.global.audioPlayer.bufferingStatus.percentage
end if
' Ensure position bar is never wider than the seek bar
if bufferPositionBarWidth > m.seekBar.width
bufferPositionBarWidth = m.seekBar.width
end if
' Use animation to make the display smooth
m.bufferPositionAnimationWidth.keyValue = [m.bufferPosition.width, bufferPositionBarWidth]
m.bufferPositionAnimation.control = "start"
end sub
sub audioPositionChanged()
stopLoadingSpinner()
if m.global.audioPlayer.position = 0
m.playPosition.width = 0
end if
if not isValid(m.global.audioPlayer.position)
playPositionBarWidth = 0
else if not isValid(m.songDuration)
playPositionBarWidth = 0
else
songPercentComplete = m.global.audioPlayer.position / m.songDuration
playPositionBarWidth = m.seekBar.width * songPercentComplete
end if
' Ensure position bar is never wider than the seek bar
if playPositionBarWidth > m.seekBar.width
playPositionBarWidth = m.seekBar.width
end if
if not m.inScrubMode
moveSeekbarThumb(playPositionBarWidth)
' Change the seek position timestamp
m.seekTimestamp.text = secondsToHuman(m.global.audioPlayer.position, false)
end if
' Use animation to make the display smooth
m.playPositionAnimationWidth.keyValue = [m.playPosition.width, playPositionBarWidth]
m.playPositionAnimation.control = "start"
' Update displayed position timestamp
if isValid(m.global.audioPlayer.position)
m.lastRecordedPositionTimestamp = m.global.audioPlayer.position
m.positionTimestamp.text = secondsToHuman(m.global.audioPlayer.position, false)
else
m.lastRecordedPositionTimestamp = 0
m.positionTimestamp.text = "0:00"
end if
' Only fall into screensaver logic if the user has screensaver enabled in Roku settings
if m.screenSaverTimeout > 0
if m.di.TimeSinceLastKeypress() >= m.screenSaverTimeout - 2
if not screenSaverActive()
startScreenSaver()
end if
end if
end if
end sub
function screenSaverActive() as boolean
return m.screenSaverBackground.visible or m.screenSaverAlbumCover.opacity > 0 or m.PosterOne.opacity > 0
end function
sub startScreenSaver()
m.screenSaverBackground.visible = true
m.top.overhangVisible = false
if m.albumCover.uri = ""
' Jellyfin Logo Screensaver
m.PosterOne.visible = true
m.PosterOneFadeIn.control = "start"
m.BounceAnimation.control = "start"
else
' Album Art Screensaver
m.screenSaverAlbumCoverFadeIn.control = "start"
m.screenSaverAlbumAnimation.control = "start"
end if
end sub
sub endScreenSaver()
m.PosterOneFadeIn.control = "pause"
m.screenSaverAlbumCoverFadeIn.control = "pause"
m.screenSaverAlbumAnimation.control = "pause"
m.BounceAnimation.control = "pause"
m.screenSaverBackground.visible = false
m.screenSaverAlbumCover.opacity = 0
m.PosterOne.opacity = 0
m.top.overhangVisible = true
end sub
sub audioStateChanged()
' Song Finished, attempt to move to next song
if m.global.audioPlayer.state = "finished"
' User has enabled single song loop, play current song again
if m.global.audioPlayer.loopMode = "one"
m.scrubTimestamp = -1
playAction()
exitScrubMode()
return
end if
if m.global.queueManager.callFunc("getPosition") &lt; m.global.queueManager.callFunc("getCount") - 1
m.top.state = "finished"
else
' We are at the end of the song queue
' User has enabled loop for entire song queue, move back to first song
if m.global.audioPlayer.loopMode = "all"
m.global.queueManager.callFunc("setPosition", -1)
LoadNextSong()
return
end if
' Return to previous screen
m.top.state = "finished"
end if
end if
end sub
function playAction() as boolean
if m.global.audioPlayer.state = "playing"
m.global.audioPlayer.control = "pause"
' Allow screen to go to real screensaver
WriteAsciiFile("tmp:/scene.temp", "nowplaying-paused")
MoveFile("tmp:/scene.temp", "tmp:/scene")
else if m.global.audioPlayer.state = "paused"
m.global.audioPlayer.control = "resume"
' Write screen tracker for screensaver
WriteAsciiFile("tmp:/scene.temp", "nowplaying")
MoveFile("tmp:/scene.temp", "tmp:/scene")
else if m.global.audioPlayer.state = "finished"
m.global.audioPlayer.control = "play"
' Write screen tracker for screensaver
WriteAsciiFile("tmp:/scene.temp", "nowplaying")
MoveFile("tmp:/scene.temp", "tmp:/scene")
end if
return true
end function
function previousClicked() as boolean
currentQueuePosition = m.global.queueManager.callFunc("getPosition")
if currentQueuePosition = 0 then return false
if m.playlistTypeCount > 1
previousItem = m.global.queueManager.callFunc("getItemByIndex", currentQueuePosition - 1)
previousItemType = m.global.queueManager.callFunc("getItemType", previousItem)
if previousItemType &lt;> "audio"
m.global.audioPlayer.control = "stop"
m.global.sceneManager.callFunc("clearPreviousScene")
m.global.queueManager.callFunc("moveBack")
m.global.queueManager.callFunc("playQueue")
return true
end if
end if
exitScrubMode()
m.lastRecordedPositionTimestamp = 0
m.positionTimestamp.text = "0:00"
if m.global.audioPlayer.state = "playing"
m.global.audioPlayer.control = "stop"
end if
' Reset loop mode due to manual user interaction
if m.global.audioPlayer.loopMode = "one"
resetLoopModeToDefault()
end if
m.global.queueManager.callFunc("moveBack")
pageContentChanged()
return true
end function
sub resetLoopModeToDefault()
m.global.audioPlayer.loopMode = ""
setLoopButtonImage()
end sub
function loopClicked() as boolean
if m.global.audioPlayer.loopMode = ""
m.global.audioPlayer.loopMode = "all"
else if m.global.audioPlayer.loopMode = "all"
m.global.audioPlayer.loopMode = "one"
else
m.global.audioPlayer.loopMode = ""
end if
setLoopButtonImage()
return true
end function
sub setLoopButtonImage()
if m.global.audioPlayer.loopMode = "all"
m.loopIndicator.opacity = "1"
m.loopIndicator.uri = m.loopIndicator.uri.Replace("-off", "-on")
else if m.global.audioPlayer.loopMode = "one"
m.loopIndicator.uri = m.loopIndicator.uri.Replace("-on", "1-on")
else
m.loopIndicator.uri = m.loopIndicator.uri.Replace("1-on", "-off")
end if
end sub
function nextClicked() as boolean
if m.playlistTypeCount > 1
currentQueuePosition = m.global.queueManager.callFunc("getPosition")
if currentQueuePosition &lt; m.global.queueManager.callFunc("getCount") - 1
nextItem = m.global.queueManager.callFunc("getItemByIndex", currentQueuePosition + 1)
nextItemType = m.global.queueManager.callFunc("getItemType", nextItem)
if nextItemType &lt;> "audio"
m.global.audioPlayer.control = "stop"
m.global.sceneManager.callFunc("clearPreviousScene")
m.global.queueManager.callFunc("moveForward")
m.global.queueManager.callFunc("playQueue")
return true
end if
end if
end if
exitScrubMode()
m.lastRecordedPositionTimestamp = 0
m.positionTimestamp.text = "0:00"
' Reset loop mode due to manual user interaction
if m.global.audioPlayer.loopMode = "one"
resetLoopModeToDefault()
end if
if m.global.queueManager.callFunc("getPosition") &lt; m.global.queueManager.callFunc("getCount") - 1
LoadNextSong()
end if
return true
end function
sub toggleShuffleEnabled()
m.global.queueManager.callFunc("toggleShuffle")
end sub
function findCurrentSongIndex(songList) as integer
if not isValidAndNotEmpty(songList) then return 0
for i = 0 to songList.count() - 1
if songList[i].id = m.global.queueManager.callFunc("getCurrentItem").id
return i
end if
end for
return 0
end function
function shuffleClicked() as boolean
currentSongIndex = findCurrentSongIndex(m.global.queueManager.callFunc("getUnshuffledQueue"))
toggleShuffleEnabled()
if not m.global.queueManager.callFunc("getIsShuffled")
m.shuffleIndicator.opacity = ".4"
m.shuffleIndicator.uri = m.shuffleIndicator.uri.Replace("-on", "-off")
m.global.queueManager.callFunc("setPosition", currentSongIndex)
setTrackNumberDisplay()
return true
end if
m.shuffleIndicator.opacity = "1"
m.shuffleIndicator.uri = m.shuffleIndicator.uri.Replace("-off", "-on")
setTrackNumberDisplay()
return true
end function
sub setShuffleIconState()
if m.global.queueManager.callFunc("getIsShuffled")
m.shuffleIndicator.opacity = "1"
m.shuffleIndicator.uri = m.shuffleIndicator.uri.Replace("-off", "-on")
end if
end sub
sub setTrackNumberDisplay()
setFieldTextValue("numberofsongs", "Track " + stri(m.global.queueManager.callFunc("getPosition") + 1) + "/" + stri(m.global.queueManager.callFunc("getCount")))
end sub
sub LoadNextSong()
if m.global.audioPlayer.state = "playing"
m.global.audioPlayer.control = "stop"
end if
exitScrubMode()
' Reset playPosition bar without animation
m.playPosition.width = 0
m.global.queueManager.callFunc("moveForward")
pageContentChanged()
end sub
' Update values on screen when page content changes
sub pageContentChanged()
m.LoadAudioStreamTask.control = "STOP"
currentItem = m.global.queueManager.callFunc("getCurrentItem")
m.LoadAudioStreamTask.itemId = currentItem.id
m.LoadAudioStreamTask.observeField("content", "onAudioStreamLoaded")
m.LoadAudioStreamTask.control = "RUN"
end sub
' If we have more and 1 song to play, fade in the next and previous controls
sub loadButtons()
if m.global.queueManager.callFunc("getCount") > 1
m.shuffleIndicator.opacity = ".4"
m.loopIndicator.opacity = ".4"
m.displayButtonsAnimation.control = "start"
setLoopButtonImage()
end if
end sub
sub onAudioStreamLoaded()
stopLoadingSpinner()
data = m.LoadAudioStreamTask.content[0]
m.LoadAudioStreamTask.unobserveField("content")
if data &lt;> invalid and data.count() > 0
' Reset buffer bar without animation
m.bufferPosition.width = 0
useMetaTask = false
currentItem = m.global.queueManager.callFunc("getCurrentItem")
if not isValid(currentItem.RunTimeTicks)
useMetaTask = true
end if
if not isValid(currentItem.AlbumArtist)
useMetaTask = true
end if
if not isValid(currentItem.name)
useMetaTask = true
end if
if not isValid(currentItem.Artists)
useMetaTask = true
end if
if useMetaTask
m.LoadMetaDataTask.itemId = currentItem.id
m.LoadMetaDataTask.observeField("content", "onMetaDataLoaded")
m.LoadMetaDataTask.control = "RUN"
else
if isValid(currentItem.ParentBackdropItemId)
setBackdropImage(ImageURL(currentItem.ParentBackdropItemId, "Backdrop", { "maxHeight": "720", "maxWidth": "1280" }))
end if
setPosterImage(ImageURL(currentItem.id, "Primary", { "maxHeight": 500, "maxWidth": 500 }))
setScreenTitle(currentItem)
setOnScreenTextValues(currentItem)
m.songDuration = currentItem.RunTimeTicks / 10000000.0
' Update displayed total audio length
m.totalLengthTimestamp.text = ticksToHuman(currentItem.RunTimeTicks)
end if
m.global.audioPlayer.content = data
m.global.audioPlayer.control = "none"
m.global.audioPlayer.control = "play"
end if
end sub
sub onBackdropImageLoaded()
data = m.LoadBackdropImageTask.content[0]
m.LoadBackdropImageTask.unobserveField("content")
if isValid(data) and data &lt;> ""
setBackdropImage(data)
end if
end sub
sub onMetaDataLoaded()
data = m.LoadMetaDataTask.content[0]
m.LoadMetaDataTask.unobserveField("content")
if isValid(data) and data.count() > 0 and isValid(data.json)
' Use metadata to load backdrop image
if isValid(data.json.ArtistItems) and isValid(data.json.ArtistItems[0]) and isValid(data.json.ArtistItems[0].id)
m.LoadBackdropImageTask.itemId = data.json.ArtistItems[0].id
m.LoadBackdropImageTask.observeField("content", "onBackdropImageLoaded")
m.LoadBackdropImageTask.control = "RUN"
end if
setPosterImage(data.posterURL)
setScreenTitle(data.json)
setOnScreenTextValues(data.json)
if isValid(data.json.RunTimeTicks)
m.songDuration = data.json.RunTimeTicks / 10000000.0
' Update displayed total audio length
m.totalLengthTimestamp.text = ticksToHuman(data.json.RunTimeTicks)
end if
end if
end sub
' Set poster image on screen
sub setPosterImage(posterURL)
if isValid(posterURL)
if m.albumCover.uri &lt;> posterURL
m.albumCover.uri = posterURL
m.screenSaverAlbumCover.uri = posterURL
end if
end if
end sub
' Set screen's title text
sub setScreenTitle(json)
newTitle = ""
if isValid(json)
if isValid(json.AlbumArtist)
newTitle = json.AlbumArtist
end if
if isValid(json.AlbumArtist) and isValid(json.name)
newTitle = newTitle + " / "
end if
if isValid(json.name)
newTitle = newTitle + json.name
end if
end if
if m.top.overhangTitle &lt;> newTitle
m.top.overhangTitle = newTitle
end if
end sub
' Populate on screen text variables
sub setOnScreenTextValues(json)
if isValid(json)
if m.playlistTypeCount = 1
setTrackNumberDisplay()
end if
setFieldTextValue("artist", json.Artists[0])
setFieldTextValue("song", json.name)
end if
end sub
' Add backdrop image to screen
sub setBackdropImage(data)
if isValid(data)
if m.backDrop.uri &lt;> data
m.backDrop.uri = data
end if
end if
end sub
' setSelectedButtonState: Changes the icon state url for the currently selected button
'
' @param {string} oldState - current state to replace in icon url
' @param {string} newState - state to replace {oldState} with in icon url
sub setSelectedButtonState(oldState as string, newState as string)
selectedButton = m.buttons.getChild(m.top.selectedButtonIndex)
selectedButton.uri = selectedButton.uri.Replace(oldState, newState)
end sub
' processScrubAction: Handles +/- seeking for the audio trickplay bar
'
' @param {integer} seekStep - seconds to move the trickplay position (negative values allowed)
sub processScrubAction(seekStep as integer)
' Prepare starting playStart property value
if m.scrubTimestamp = -1
m.scrubTimestamp = m.lastRecordedPositionTimestamp
end if
' Don't let seek to go past the end of the song
if m.scrubTimestamp + seekStep > m.songDuration - 5
return
end if
if seekStep > 0
' Move seek forward
m.scrubTimestamp += seekStep
else if m.scrubTimestamp >= Abs(seekStep)
' If back seek won't go below 0, move seek back
m.scrubTimestamp += seekStep
else
' Back seek would go below 0, set to 0 directly
m.scrubTimestamp = 0
end if
' Move the seedbar thumb forward
songPercentComplete = m.scrubTimestamp / m.songDuration
playPositionBarWidth = m.seekBar.width * songPercentComplete
moveSeekbarThumb(playPositionBarWidth)
' Change the displayed position timestamp
m.seekTimestamp.text = secondsToHuman(m.scrubTimestamp, false)
end sub
' resetSeekbarThumb: Resets the thumb to the playing position
'
sub resetSeekbarThumb()
m.scrubTimestamp = -1
moveSeekbarThumb(m.playPosition.width)
end sub
' moveSeekbarThumb: Positions the thumb on the seekbar
'
' @param {float} playPositionBarWidth - width of the play position bar
sub moveSeekbarThumb(playPositionBarWidth as float)
' Center the thumb on the play position bar
thumbPostionLeft = playPositionBarWidth - 10
' Don't let thumb go below 0
if thumbPostionLeft &lt; 0 then thumbPostionLeft = 0
' Don't let thumb go past end of seekbar
if thumbPostionLeft > m.seekBar.width - 25
thumbPostionLeft = m.seekBar.width - 25
end if
' Move the thumb
m.thumb.translation = [thumbPostionLeft, m.thumb.translation[1]]
' Move the seek position element so it follows the thumb
m.seekPosition.translation = [720 + thumbPostionLeft - (m.seekPosition.width / 2), m.seekPosition.translation[1]]
end sub
' exitScrubMode: Moves player out of scrub mode state, resets back to standard play mode
'
sub exitScrubMode()
m.buttons.setFocus(true)
m.thumb.setFocus(false)
if m.seekPosition.visible
m.seekPosition.visible = false
end if
resetSeekbarThumb()
m.inScrubMode = false
m.thumb.visible = false
setSelectedButtonState("-default", "-selected")
end sub
' Process key press events
function onKeyEvent(key as string, press as boolean) as boolean
' Key bindings for remote control buttons
if press
' If user presses key to turn off screensaver, don't do anything else with it
if screenSaverActive()
endScreenSaver()
return true
end if
' Key Event handler when m.thumb is in focus
if m.thumb.hasFocus()
if key = "right"
m.inScrubMode = true
processScrubAction(10)
return true
end if
if key = "left"
m.inScrubMode = true
processScrubAction(-10)
return true
end if
if key = "OK" or key = "play"
if m.inScrubMode
startLoadingSpinner()
m.inScrubMode = false
m.global.audioPlayer.seek = m.scrubTimestamp
return true
end if
return playAction()
end if
end if
if key = "play"
return playAction()
end if
if key = "up"
if not m.thumb.visible
m.thumb.visible = true
setSelectedButtonState("-selected", "-default")
end if
if not m.seekPosition.visible
m.seekPosition.visible = true
end if
m.thumb.setFocus(true)
m.buttons.setFocus(false)
return true
end if
if key = "down"
if m.thumb.visible
exitScrubMode()
end if
return true
end if
if key = "back"
m.global.audioPlayer.control = "stop"
m.global.audioPlayer.loopMode = ""
else if key = "rewind"
return previousClicked()
else if key = "fastforward"
return nextClicked()
else if key = "left"
if m.buttons.hasFocus()
if m.global.queueManager.callFunc("getCount") = 1 then return false
if m.top.selectedButtonIndex > 0
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
m.top.selectedButtonIndex = m.top.selectedButtonIndex - 1
end if
return true
end if
else if key = "right"
if m.buttons.hasFocus()
if m.global.queueManager.callFunc("getCount") = 1 then return false
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
if m.top.selectedButtonIndex &lt; m.buttonCount - 1 then m.top.selectedButtonIndex = m.top.selectedButtonIndex + 1
return true
end if
else if key = "OK"
if m.buttons.hasFocus()
if m.buttons.getChild(m.top.selectedButtonIndex).id = "play"
return playAction()
else if m.buttons.getChild(m.top.selectedButtonIndex).id = "previous"
return previousClicked()
else if m.buttons.getChild(m.top.selectedButtonIndex).id = "next"
return nextClicked()
else if m.buttons.getChild(m.top.selectedButtonIndex).id = "shuffle"
return shuffleClicked()
else if m.buttons.getChild(m.top.selectedButtonIndex).id = "loop"
return loopClicked()
end if
end if
end if
end if
return false
end function
sub OnScreenHidden()
' Write screen tracker for screensaver
WriteAsciiFile("tmp:/scene.temp", "")
MoveFile("tmp:/scene.temp", "tmp:/scene")
end sub
</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-FolderData.html">FolderData</a></div><div class="sidebar-section-children"><a href="module-GetFiltersTask.html">GetFiltersTask</a></div><div class="sidebar-section-children"><a href="module-GetNextEpisodeTask.html">GetNextEpisodeTask</a></div><div class="sidebar-section-children"><a href="module-GetPlaybackInfoTask.html">GetPlaybackInfoTask</a></div><div class="sidebar-section-children"><a href="module-GetShuffleEpisodesTask.html">GetShuffleEpisodesTask</a></div><div class="sidebar-section-children"><a href="module-GridItem.html">GridItem</a></div><div class="sidebar-section-children"><a href="module-GridItemSmall.html">GridItemSmall</a></div><div class="sidebar-section-children"><a href="module-Home.html">Home</a></div><div class="sidebar-section-children"><a href="module-HomeData.html">HomeData</a></div><div class="sidebar-section-children"><a href="module-HomeItem.html">HomeItem</a></div><div class="sidebar-section-children"><a href="module-HomeRowItemSizes.html">HomeRowItemSizes</a></div><div class="sidebar-section-children"><a href="module-HomeRows.html">HomeRows</a></div><div class="sidebar-section-children"><a href="module-IconButton.html">IconButton</a></div><div class="sidebar-section-children"><a href="module-Image.html">Image</a></div><div class="sidebar-section-children"><a href="module-ImageData.html">ImageData</a></div><div class="sidebar-section-children"><a href="module-IntegerKeyboard.html">IntegerKeyboard</a></div><div class="sidebar-section-children"><a href="module-ItemGrid.html">ItemGrid</a></div><div class="sidebar-section-children"><a href="module-ItemGridOptions.html">ItemGridOptions</a></div><div class="sidebar-section-children"><a href="module-Items.html">Items</a></div><div class="sidebar-section-children"><a href="module-JFButton.html">JFButton</a></div><div class="sidebar-section-children"><a href="module-JFButtons.html">JFButtons</a></div><div class="sidebar-section-children"><a href="module-JFContentItem.html">JFContentItem</a></div><div class="sidebar-section-children"><a href="module-JFGroup.html">JFGroup</a></div><div class="sidebar-section-children"><a href="module-JFMessageDialog.html">JFMessageDialog</a></div><div class="sidebar-section-children"><a href="module-JFOverhang.html">JFOverhang</a></div><div class="sidebar-section-children"><a href="module-JFScene.html">JFScene</a></div><div class="sidebar-section-children"><a href="module-JFScreen.html">JFScreen</a></div><div class="sidebar-section-children"><a href="module-JFServer.html">JFServer</a></div><div class="sidebar-section-children"><a href="module-JFVideo.html">JFVideo</a></div><div class="sidebar-section-children"><a href="module-ListPoster.html">ListPoster</a></div><div class="sidebar-section-children"><a href="module-LoadChannelsTask.html">LoadChannelsTask</a></div><div class="sidebar-section-children"><a href="module-LoadItemsTask.html">LoadItemsTask</a></div><div class="sidebar-section-children"><a href="module-LoadItemsTask2.html">LoadItemsTask2</a></div><div class="sidebar-section-children"><a href="module-LoadPhotoTask.html">LoadPhotoTask</a></div><div class="sidebar-section-children"><a href="module-LoadProgramDetailsTask.html">LoadProgramDetailsTask</a></div><div class="sidebar-section-children"><a href="module-LoadScreenSaverTimeoutTask.html">LoadScreenSaverTimeoutTask</a></div><div class="sidebar-section-children"><a href="module-LoadSheduleTask.html">LoadSheduleTask</a></div><div class="sidebar-section-children"><a href="module-LoadVideoContentTask.html">LoadVideoContentTask</a></div><div class="sidebar-section-children"><a href="module-LoginScene.html">LoginScene</a></div><div class="sidebar-section-children"><a href="module-Main.html">Main</a></div><div class="sidebar-section-children"><a href="module-MovieData.html">MovieData</a></div><div class="sidebar-section-children"><a href="module-MovieDetails.html">MovieDetails</a></div><div class="sidebar-section-children"><a href="module-MovieLibraryView.html">MovieLibraryView</a></div><div class="sidebar-section-children"><a href="module-MovieOptions.html">MovieOptions</a></div><div class="sidebar-section-children"><a href="module-MusicAlbumData.html">MusicAlbumData</a></div><div class="sidebar-section-children"><a href="module-MusicAlbumSongListData.html">MusicAlbumSongListData</a></div><div class="sidebar-section-children"><a href="module-MusicArtistData.html">MusicArtistData</a></div><div class="sidebar-section-children"><a href="module-MusicArtistGridItem.html">MusicArtistGridItem</a></div><div class="sidebar-section-children"><a href="module-MusicLibraryView.html">MusicLibraryView</a></div><div class="sidebar-section-children"><a href="module-MusicSongData.html">MusicSongData</a></div><div class="sidebar-section-children"><a href="module-OSD.html">OSD</a></div><div class="sidebar-section-children"><a href="module-OptionNode.html">OptionNode</a></div><div class="sidebar-section-children"><a href="module-OptionsButton.html">OptionsButton</a></div><div class="sidebar-section-children"><a href="module-OptionsData.html">OptionsData</a></div><div class="sidebar-section-children"><a href="module-OptionsSlider.html">OptionsSlider</a></div><div class="sidebar-section-children"><a href="module-OverviewDialog.html">OverviewDialog</a></div><div class="sidebar-section-children"><a href="module-PersonData.html">PersonData</a></div><div class="sidebar-section-children"><a href="module-PersonDetails.html">PersonDetails</a></div><div class="sidebar-section-children"><a href="module-PhotoData.html">PhotoData</a></div><div class="sidebar-section-children"><a href="module-PhotoDetails.html">PhotoDetails</a></div><div class="sidebar-section-children"><a href="module-PlaybackDialog.html">PlaybackDialog</a></div><div class="sidebar-section-children"><a href="module-PlayedCheckmark.html">PlayedCheckmark</a></div><div class="sidebar-section-children"><a href="module-PlaylistData.html">PlaylistData</a></div><div class="sidebar-section-children"><a href="module-PlaylistView.html">PlaylistView</a></div><div class="sidebar-section-children"><a href="module-PlaystateTask.html">PlaystateTask</a></div><div class="sidebar-section-children"><a href="module-PostTask.html">PostTask</a></div><div class="sidebar-section-children"><a href="module-ProgramDetails.html">ProgramDetails</a></div><div class="sidebar-section-children"><a href="module-PublicUserData.html">PublicUserData</a></div><div class="sidebar-section-children"><a href="module-QueueManager.html">QueueManager</a></div><div class="sidebar-section-children"><a href="module-QuickConnect.html">QuickConnect</a></div><div class="sidebar-section-children"><a href="module-QuickConnectDialog.html">QuickConnectDialog</a></div><div class="sidebar-section-children"><a href="module-RadioDialog.html">RadioDialog</a></div><div class="sidebar-section-children"><a href="module-RecordProgramTask.html">RecordProgramTask</a></div><div class="sidebar-section-children"><a href="module-RecordingData.html">RecordingData</a></div><div class="sidebar-section-children"><a href="module-SceneManager.html">SceneManager</a></div><div class="sidebar-section-children"><a href="module-ScheduleProgramData.html">ScheduleProgramData</a></div><div class="sidebar-section-children"><a href="module-SearchBox.html">SearchBox</a></div><div class="sidebar-section-children"><a href="module-SearchData.html">SearchData</a></div><div class="sidebar-section-children"><a href="module-SearchResults.html">SearchResults</a></div><div class="sidebar-section-children"><a href="module-SearchRow.html">SearchRow</a></div><div class="sidebar-section-children"><a href="module-SearchTask.html">SearchTask</a></div><div class="sidebar-section-children"><a href="module-SeriesData.html">SeriesData</a></div><div class="sidebar-section-children"><a href="module-ServerDiscoveryTask.html">ServerDiscoveryTask</a></div><div class="sidebar-section-children"><a href="module-SetServerScreen.html">SetServerScreen</a></div><div class="sidebar-section-children"><a href="module-ShowScenes.html">ShowScenes</a></div><div class="sidebar-section-children"><a href="module-SlideOutButton.html">SlideOutButton</a></div><div class="sidebar-section-children"><a href="module-SongItem.html">SongItem</a></div><div class="sidebar-section-children"><a href="module-Spinner.html">Spinner</a></div><div class="sidebar-section-children"><a href="module-StandardDialog.html">StandardDialog</a></div><div class="sidebar-section-children"><a href="module-Subtitles.html">Subtitles</a></div><div class="sidebar-section-children"><a href="module-TVEpisode.html">TVEpisode</a></div><div class="sidebar-section-children"><a href="module-TVEpisodeData.html">TVEpisodeData</a></div><div class="sidebar-section-children"><a href="module-TVEpisodeRow.html">TVEpisodeRow</a></div><div class="sidebar-section-children"><a href="module-TVEpisodeRowWithOptions.html">TVEpisodeRowWithOptions</a></div><div class="sidebar-section-children"><a href="module-TVEpisodes.html">TVEpisodes</a></div><div class="sidebar-section-children"><a href="module-TVListDetails.html">TVListDetails</a></div><div class="sidebar-section-children"><a href="module-TVListOptions.html">TVListOptions</a></div><div class="sidebar-section-children"><a href="module-TVSeasonData.html">TVSeasonData</a></div><div class="sidebar-section-children"><a href="module-TVSeasonRow.html">TVSeasonRow</a></div><div class="sidebar-section-children"><a href="module-TVShowDescription.html">TVShowDescription</a></div><div class="sidebar-section-children"><a href="module-TVShowDetails.html">TVShowDetails</a></div><div class="sidebar-section-children"><a href="module-TextSizeTask.html">TextSizeTask</a></div><div class="sidebar-section-children"><a href="module-UserData.html">UserData</a></div><div class="sidebar-section-children"><a href="module-UserItem.html">UserItem</a></div><div class="sidebar-section-children"><a href="module-UserLibrary.html">UserLibrary</a></div><div class="sidebar-section-children"><a href="module-UserRow.html">UserRow</a></div><div class="sidebar-section-children"><a href="module-UserSelect.html">UserSelect</a></div><div class="sidebar-section-children"><a href="module-VideoData.html">VideoData</a></div><div class="sidebar-section-children"><a href="module-VideoPlayer.html">VideoPlayer</a></div><div class="sidebar-section-children"><a href="module-VideoPlayerView.html">VideoPlayerView</a></div><div class="sidebar-section-children"><a href="module-VideoTrackListItem.html">VideoTrackListItem</a></div><div class="sidebar-section-children"><a href="module-ViewCreator.html">ViewCreator</a></div><div class="sidebar-section-children"><a href="module-WhatsNewDialog.html">WhatsNewDialog</a></div><div class="sidebar-section-children"><a href="module-baserequest.html">baserequest</a></div><div class="sidebar-section-children"><a href="module-captionTask.html">captionTask</a></div><div class="sidebar-section-children"><a href="module-conditional.html">conditional</a></div><div class="sidebar-section-children"><a href="module-config.html">config</a></div><div class="sidebar-section-children"><a href="module-deviceCapabilities.html">deviceCapabilities</a></div><div class="sidebar-section-children"><a href="module-globals.html">globals</a></div><div class="sidebar-section-children"><a href="module-homeRowItemSizes_.html">homeRowItemSizes</a></div><div class="sidebar-section-children"><a href="module-migrations.html">migrations</a></div><div class="sidebar-section-children"><a href="module-misc.html">misc</a></div><div class="sidebar-section-children"><a href="module-quickplay.html">quickplay</a></div><div class="sidebar-section-children"><a href="module-schedule.html">schedule</a></div><div class="sidebar-section-children"><a href="module-section.html">section</a></div><div class="sidebar-section-children"><a href="module-sectionScroller.html">sectionScroller</a></div><div class="sidebar-section-children"><a href="module-settings.html">settings</a></div><div class="sidebar-section-children"><a href="module-userauth.html">userauth</a></div></div></div><div class="mobile-navbar-actions"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div></div></div><script type="text/javascript" src="scripts/core.min.js"></script><script src="scripts/search.min.js" defer="defer"></script><script src="scripts/third-party/fuse.js" defer="defer"></script><script type="text/javascript">var tocbotInstance=tocbot.init({tocSelector:"#eed4d2a0bfd64539bb9df78095dec881",contentSelector:".main-content",headingSelector:"h1, h2, h3",hasInnerContainers:!0,scrollContainer:".main-content",headingsOffset:130,onClick:bringLinkToView})</script></body></html>