940 lines
72 KiB
HTML
940 lines
72 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: source/ShowScenes.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-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-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-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-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">source_ShowScenes.bs</h1></header><article><pre class="prettyprint source lang-js"><code>function LoginFlow()
|
|
'Collect Jellyfin server and user information
|
|
start_login:
|
|
|
|
serverUrl = get_setting("server")
|
|
if isValid(serverUrl)
|
|
print "Previous server connection saved to registry"
|
|
startOver = not session.server.UpdateURL(serverUrl)
|
|
if startOver
|
|
print "Could not connect to previously saved server."
|
|
end if
|
|
else
|
|
startOver = true
|
|
print "No previous server connection saved to registry"
|
|
end if
|
|
|
|
invalidServer = true
|
|
if not startOver
|
|
m.scene.isLoading = true
|
|
invalidServer = ServerInfo().Error
|
|
m.scene.isLoading = false
|
|
end if
|
|
|
|
m.serverSelection = "Saved"
|
|
if startOver or invalidServer
|
|
print "Get server details"
|
|
SendPerformanceBeacon("AppDialogInitiate") ' Roku Performance monitoring - Dialog Starting
|
|
m.serverSelection = CreateServerGroup()
|
|
SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
|
|
if m.serverSelection = "backPressed"
|
|
print "backPressed"
|
|
m.global.sceneManager.callFunc("clearScenes")
|
|
return false
|
|
end if
|
|
SaveServerList()
|
|
end if
|
|
|
|
activeUser = get_setting("active_user")
|
|
if activeUser = invalid
|
|
print "No active user found in registry"
|
|
user_select:
|
|
SendPerformanceBeacon("AppDialogInitiate") ' Roku Performance monitoring - Dialog Starting
|
|
|
|
publicUsers = GetPublicUsers()
|
|
savedUsers = getSavedUsers()
|
|
|
|
numPubUsers = publicUsers.count()
|
|
numSavedUsers = savedUsers.count()
|
|
|
|
if numPubUsers > 0 or numSavedUsers > 0
|
|
publicUsersNodes = []
|
|
publicUserIds = []
|
|
' load public users
|
|
if numPubUsers > 0
|
|
for each item in publicUsers
|
|
user = CreateObject("roSGNode", "PublicUserData")
|
|
user.id = item.Id
|
|
user.name = item.Name
|
|
if isValid(item.PrimaryImageTag)
|
|
user.ImageURL = UserImageURL(user.id, { "tag": item.PrimaryImageTag })
|
|
end if
|
|
publicUsersNodes.push(user)
|
|
publicUserIds.push(user.id)
|
|
end for
|
|
end if
|
|
' load saved users for this server id
|
|
if numSavedUsers > 0
|
|
for each savedUser in savedUsers
|
|
if isValid(savedUser.serverId) and savedUser.serverId = m.global.session.server.id
|
|
' only show unique userids on screen.
|
|
if not arrayHasValue(publicUserIds, savedUser.Id)
|
|
user = CreateObject("roSGNode", "PublicUserData")
|
|
user.id = savedUser.Id
|
|
|
|
if isValid(savedUser.username)
|
|
user.name = savedUser.username
|
|
end if
|
|
|
|
publicUsersNodes.push(user)
|
|
end if
|
|
end if
|
|
end for
|
|
end if
|
|
' push all users to the user select view
|
|
userSelected = CreateUserSelectGroup(publicUsersNodes)
|
|
SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
|
|
if userSelected = "backPressed"
|
|
session.server.Delete()
|
|
unset_setting("server")
|
|
goto start_login
|
|
else if userSelected <> ""
|
|
startLoadingSpinner()
|
|
print "A public user was selected with username=" + userSelected
|
|
session.user.Update("name", userSelected)
|
|
regex = CreateObject("roRegex", "[^a-zA-Z0-9\ \-\_]", "")
|
|
session.user.Update("friendlyName", regex.ReplaceAll(userSelected, ""))
|
|
' save userid to session
|
|
for each user in publicUsersNodes
|
|
if user.name = userSelected
|
|
session.user.Update("id", user.id)
|
|
exit for
|
|
end if
|
|
end for
|
|
' try to login with token from registry
|
|
myToken = get_user_setting("token")
|
|
if myToken <> invalid
|
|
' check if token is valid
|
|
print "Auth token found in registry for selected user"
|
|
session.user.Update("authToken", myToken)
|
|
print "Attempting to use API with auth token"
|
|
currentUser = AboutMe()
|
|
if currentUser = invalid
|
|
print "Auth token is no longer valid - deleting token"
|
|
unset_user_setting("token")
|
|
unset_user_setting("username")
|
|
else
|
|
print "Success! Auth token is still valid"
|
|
session.user.Login(currentUser, true)
|
|
LoadUserAbilities()
|
|
return true
|
|
end if
|
|
else
|
|
print "No auth token found in registry for selected user"
|
|
end if
|
|
'Try to login without password. If the token is valid, we're done
|
|
print "Attempting to login with no password"
|
|
userData = get_token(userSelected, "")
|
|
if isValid(userData)
|
|
print "login success!"
|
|
session.user.Login(userData, true)
|
|
LoadUserAbilities()
|
|
return true
|
|
else
|
|
print "Auth failed. Password required"
|
|
end if
|
|
end if
|
|
else
|
|
userSelected = ""
|
|
end if
|
|
stopLoadingSpinner()
|
|
passwordEntry = CreateSigninGroup(userSelected)
|
|
SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
|
|
if passwordEntry = "backPressed"
|
|
if numPubUsers > 0
|
|
goto user_select
|
|
else
|
|
session.server.Delete()
|
|
unset_setting("server")
|
|
goto start_login
|
|
end if
|
|
end if
|
|
else
|
|
print "Active user found in registry"
|
|
session.user.Update("id", activeUser)
|
|
|
|
myUsername = get_user_setting("username")
|
|
myAuthToken = get_user_setting("token")
|
|
if isValid(myAuthToken) and isValid(myUsername)
|
|
print "Auth token found in registry"
|
|
session.user.Update("authToken", myAuthToken)
|
|
session.user.Update("name", myUsername)
|
|
regex = CreateObject("roRegex", "[^a-zA-Z0-9\ \-\_]", "")
|
|
session.user.Update("friendlyName", regex.ReplaceAll(myUsername, ""))
|
|
print "Attempting to use API with auth token"
|
|
currentUser = AboutMe()
|
|
if currentUser = invalid
|
|
print "Auth token is no longer valid"
|
|
'Try to login without password. If the token is valid, we're done
|
|
print "Attempting to login with no password"
|
|
userData = get_token(myUsername, "")
|
|
if isValid(userData)
|
|
print "login success!"
|
|
session.user.Login(userData, true)
|
|
LoadUserAbilities()
|
|
return true
|
|
else
|
|
print "Auth failed. Password required"
|
|
print "delete token and restart login flow"
|
|
unset_user_setting("token")
|
|
unset_user_setting("username")
|
|
goto start_login
|
|
end if
|
|
else
|
|
print "Success! Auth token is still valid"
|
|
session.user.Login(currentUser, true)
|
|
end if
|
|
else
|
|
print "No auth token found in registry"
|
|
end if
|
|
end if
|
|
|
|
if m.global.session.user.id = invalid or m.global.session.user.authToken = invalid
|
|
print "Login failed, restart flow"
|
|
unset_setting("active_user")
|
|
session.user.Logout()
|
|
goto start_login
|
|
end if
|
|
|
|
LoadUserAbilities()
|
|
m.global.sceneManager.callFunc("clearScenes")
|
|
|
|
return true
|
|
end function
|
|
|
|
sub SaveServerList()
|
|
'Save off this server to our list of saved servers for easier navigation between servers
|
|
server = m.global.session.server.url
|
|
saved = get_setting("saved_servers")
|
|
if isValid(server)
|
|
server = LCase(server)'Saved server data is always lowercase
|
|
end if
|
|
entryCount = 0
|
|
addNewEntry = true
|
|
savedServers = { serverList: [] }
|
|
if isValid(saved)
|
|
savedServers = ParseJson(saved)
|
|
entryCount = savedServers.serverList.Count()
|
|
if isValid(savedServers.serverList) and entryCount > 0
|
|
for each item in savedServers.serverList
|
|
if item.baseUrl = server
|
|
addNewEntry = false
|
|
exit for
|
|
end if
|
|
end for
|
|
end if
|
|
end if
|
|
|
|
if addNewEntry
|
|
if entryCount = 0
|
|
set_setting("saved_servers", FormatJson({ serverList: [{ name: m.serverSelection, baseUrl: server, iconUrl: "pkg:/images/logo-icon120.jpg", iconWidth: 120, iconHeight: 120 }] }))
|
|
else
|
|
savedServers.serverList.Push({ name: m.serverSelection, baseUrl: server, iconUrl: "pkg:/images/logo-icon120.jpg", iconWidth: 120, iconHeight: 120 })
|
|
set_setting("saved_servers", FormatJson(savedServers))
|
|
end if
|
|
end if
|
|
end sub
|
|
|
|
sub DeleteFromServerList(urlToDelete)
|
|
saved = get_setting("saved_servers")
|
|
if isValid(urlToDelete)
|
|
urlToDelete = LCase(urlToDelete)
|
|
end if
|
|
if isValid(saved)
|
|
savedServers = ParseJson(saved)
|
|
newServers = { serverList: [] }
|
|
for each item in savedServers.serverList
|
|
if item.baseUrl <> urlToDelete
|
|
newServers.serverList.Push(item)
|
|
end if
|
|
end for
|
|
set_setting("saved_servers", FormatJson(newServers))
|
|
end if
|
|
end sub
|
|
|
|
' Roku Performance monitoring
|
|
sub SendPerformanceBeacon(signalName as string)
|
|
if m.global.app_loaded = false
|
|
m.scene.signalBeacon(signalName)
|
|
end if
|
|
end sub
|
|
|
|
function CreateServerGroup()
|
|
screen = CreateObject("roSGNode", "SetServerScreen")
|
|
screen.optionsAvailable = true
|
|
m.global.sceneManager.callFunc("pushScene", screen)
|
|
port = CreateObject("roMessagePort")
|
|
m.colors = {}
|
|
|
|
if isValid(m.global.session.server.url)
|
|
screen.serverUrl = m.global.session.server.url
|
|
end if
|
|
m.viewModel = {}
|
|
button = screen.findNode("submit")
|
|
button.observeField("buttonSelected", port)
|
|
'create delete saved server option
|
|
new_options = []
|
|
sidepanel = screen.findNode("options")
|
|
opt = CreateObject("roSGNode", "OptionsButton")
|
|
opt.title = tr("Delete Saved")
|
|
opt.id = "delete_saved"
|
|
opt.observeField("optionSelected", port)
|
|
new_options.push(opt)
|
|
sidepanel.options = new_options
|
|
sidepanel.observeField("closeSidePanel", port)
|
|
|
|
screen.observeField("backPressed", port)
|
|
|
|
while true
|
|
msg = wait(0, port)
|
|
print type(msg), msg
|
|
if type(msg) = "roSGScreenEvent" and msg.isScreenClosed()
|
|
return "false"
|
|
else if isNodeEvent(msg, "backPressed")
|
|
return "backPressed"
|
|
else if isNodeEvent(msg, "closeSidePanel")
|
|
screen.setFocus(true)
|
|
serverPicker = screen.findNode("serverPicker")
|
|
serverPicker.setFocus(true)
|
|
else if type(msg) = "roSGNodeEvent"
|
|
node = msg.getNode()
|
|
if node = "submit"
|
|
m.scene.isLoading = true
|
|
|
|
serverUrl = inferServerUrl(screen.serverUrl)
|
|
|
|
isConnected = session.server.UpdateURL(serverUrl)
|
|
serverInfoResult = invalid
|
|
if isConnected
|
|
set_setting("server", serverUrl)
|
|
serverInfoResult = ServerInfo()
|
|
'If this is a different server from what we know, reset username/password setting
|
|
if m.global.session.server.url <> serverUrl
|
|
set_setting("username", "")
|
|
set_setting("password", "")
|
|
end if
|
|
set_setting("server", serverUrl)
|
|
end if
|
|
m.scene.isLoading = false
|
|
|
|
if isConnected = false or serverInfoResult = invalid
|
|
' Maybe don't unset setting, but offer as a prompt
|
|
' Server not found, is it online? New values / Retry
|
|
print "Server not found, is it online? New values / Retry"
|
|
screen.errorMessage = tr("Server not found, is it online?")
|
|
SignOut(false)
|
|
else
|
|
|
|
if isValid(serverInfoResult.Error) and serverInfoResult.Error
|
|
' If server redirected received, update the URL
|
|
if isValid(serverInfoResult.UpdatedUrl)
|
|
serverUrl = serverInfoResult.UpdatedUrl
|
|
|
|
isConnected = session.server.UpdateURL(serverUrl)
|
|
if isConnected
|
|
set_setting("server", serverUrl)
|
|
screen.visible = false
|
|
return ""
|
|
end if
|
|
end if
|
|
' Display Error Message to user
|
|
message = tr("Error: ")
|
|
if isValid(serverInfoResult.ErrorCode)
|
|
message = message + "[" + serverInfoResult.ErrorCode.toStr() + "] "
|
|
end if
|
|
screen.errorMessage = message + tr(serverInfoResult.ErrorMessage)
|
|
SignOut(false)
|
|
else
|
|
screen.visible = false
|
|
if isValid(serverInfoResult.serverName)
|
|
return serverInfoResult.ServerName + " (Saved)"
|
|
else
|
|
return "Saved"
|
|
end if
|
|
end if
|
|
end if
|
|
else if node = "delete_saved"
|
|
serverPicker = screen.findNode("serverPicker")
|
|
itemToDelete = serverPicker.content.getChild(serverPicker.itemFocused)
|
|
urlToDelete = itemToDelete.baseUrl
|
|
if isValid(urlToDelete)
|
|
DeleteFromServerList(urlToDelete)
|
|
serverPicker.content.removeChild(itemToDelete)
|
|
sidepanel.visible = false
|
|
serverPicker.setFocus(true)
|
|
end if
|
|
end if
|
|
end if
|
|
end while
|
|
|
|
' Just hide it when done, in case we need to come back
|
|
screen.visible = false
|
|
return ""
|
|
end function
|
|
|
|
function CreateUserSelectGroup(users = [])
|
|
if users.count() = 0
|
|
return ""
|
|
end if
|
|
group = CreateObject("roSGNode", "UserSelect")
|
|
m.global.sceneManager.callFunc("pushScene", group)
|
|
port = CreateObject("roMessagePort")
|
|
|
|
group.itemContent = users
|
|
group.findNode("userRow").observeField("userSelected", port)
|
|
group.findNode("alternateOptions").observeField("itemSelected", port)
|
|
group.observeField("backPressed", port)
|
|
while true
|
|
msg = wait(0, port)
|
|
if type(msg) = "roSGScreenEvent" and msg.isScreenClosed()
|
|
group.visible = false
|
|
return -1
|
|
else if isNodeEvent(msg, "backPressed")
|
|
return "backPressed"
|
|
else if type(msg) = "roSGNodeEvent" and msg.getField() = "userSelected"
|
|
return msg.GetData()
|
|
else if type(msg) = "roSGNodeEvent" and msg.getField() = "itemSelected"
|
|
if msg.getData() = 0
|
|
return ""
|
|
end if
|
|
end if
|
|
end while
|
|
|
|
' Just hide it when done, in case we need to come back
|
|
group.visible = false
|
|
return ""
|
|
end function
|
|
|
|
function CreateSigninGroup(user = "")
|
|
' Get and Save Jellyfin user login credentials
|
|
group = CreateObject("roSGNode", "LoginScene")
|
|
m.global.sceneManager.callFunc("pushScene", group)
|
|
port = CreateObject("roMessagePort")
|
|
|
|
group.findNode("prompt").text = tr("Sign In")
|
|
|
|
config = group.findNode("configOptions")
|
|
username_field = CreateObject("roSGNode", "ConfigData")
|
|
username_field.label = tr("Username")
|
|
username_field.field = "username"
|
|
username_field.type = "string"
|
|
if user = "" and get_setting("username") <> invalid
|
|
username_field.value = get_setting("username")
|
|
else
|
|
username_field.value = user
|
|
end if
|
|
password_field = CreateObject("roSGNode", "ConfigData")
|
|
password_field.label = tr("Password")
|
|
password_field.field = "password"
|
|
password_field.type = "password"
|
|
registryPassword = get_setting("password")
|
|
if isValid(registryPassword)
|
|
password_field.value = registryPassword
|
|
end if
|
|
' Add checkbox for saving credentials
|
|
checkbox = group.findNode("onOff")
|
|
items = CreateObject("roSGNode", "ContentNode")
|
|
items.role = "content"
|
|
saveCheckBox = CreateObject("roSGNode", "ContentNode")
|
|
saveCheckBox.title = tr("Save Credentials?")
|
|
items.appendChild(saveCheckBox)
|
|
checkbox.content = items
|
|
checkbox.checkedState = [true]
|
|
quickConnect = group.findNode("quickConnect")
|
|
' Quick Connect only supported for server version 10.8+ right now...
|
|
if versionChecker(m.global.session.server.version, "10.8.0")
|
|
' Add option for Quick Connect
|
|
quickConnect.text = tr("Quick Connect")
|
|
quickConnect.observeField("buttonSelected", port)
|
|
else
|
|
quickConnect.visible = false
|
|
end if
|
|
|
|
items = [username_field, password_field]
|
|
config.configItems = items
|
|
|
|
button = group.findNode("submit")
|
|
button.observeField("buttonSelected", port)
|
|
|
|
config = group.findNode("configOptions")
|
|
|
|
username = config.content.getChild(0)
|
|
password = config.content.getChild(1)
|
|
|
|
group.observeField("backPressed", port)
|
|
|
|
while true
|
|
msg = wait(0, port)
|
|
if type(msg) = "roSGScreenEvent" and msg.isScreenClosed()
|
|
group.visible = false
|
|
return "false"
|
|
else if isNodeEvent(msg, "backPressed")
|
|
group.unobserveField("backPressed")
|
|
group.backPressed = false
|
|
return "backPressed"
|
|
else if type(msg) = "roSGNodeEvent"
|
|
node = msg.getNode()
|
|
if node = "submit"
|
|
startLoadingSpinner()
|
|
' Validate credentials
|
|
activeUser = get_token(username.value, password.value)
|
|
if isValid(activeUser)
|
|
print "activeUser=", activeUser
|
|
if checkbox.checkedState[0] = true
|
|
' save credentials
|
|
session.user.Login(activeUser, true)
|
|
set_user_setting("token", activeUser.token)
|
|
set_user_setting("username", username.value)
|
|
else
|
|
session.user.Login(activeUser)
|
|
end if
|
|
return "true"
|
|
end if
|
|
stopLoadingSpinner()
|
|
print "Login attempt failed..."
|
|
group.findNode("alert").text = tr("Login attempt failed.")
|
|
else if node = "quickConnect"
|
|
json = initQuickConnect()
|
|
if json = invalid
|
|
group.findNode("alert").text = tr("Quick Connect not available.")
|
|
else
|
|
' Server user is talking to is at least 10.8 and has quick connect enabled...
|
|
m.quickConnectDialog = createObject("roSGNode", "QuickConnectDialog")
|
|
m.quickConnectDialog.saveCredentials = checkbox.checkedState[0]
|
|
m.quickConnectDialog.quickConnectJson = json
|
|
m.quickConnectDialog.title = tr("Quick Connect")
|
|
m.quickConnectDialog.message = [tr("Here is your Quick Connect code: ") + json.Code, tr("(Dialog will close automatically)")]
|
|
m.quickConnectDialog.buttons = [tr("Cancel")]
|
|
m.quickConnectDialog.observeField("authenticated", port)
|
|
m.scene.dialog = m.quickConnectDialog
|
|
end if
|
|
else if msg.getField() = "authenticated"
|
|
authenticated = msg.getData()
|
|
if authenticated = true
|
|
' Quick connect authentication was successful...
|
|
return "true"
|
|
else
|
|
dialog = createObject("roSGNode", "Dialog")
|
|
dialog.id = "QuickConnectError"
|
|
dialog.title = tr("Quick Connect")
|
|
dialog.buttons = [tr("OK")]
|
|
dialog.message = tr("There was an error authenticating via Quick Connect.")
|
|
m.scene.dialog = dialog
|
|
m.scene.dialog.observeField("buttonSelected", port)
|
|
end if
|
|
else
|
|
' If there are no other button matches, check if this is a simple "OK" Dialog & Close if so
|
|
dialog = msg.getRoSGNode()
|
|
if dialog.id = "QuickConnectError"
|
|
dialog.unobserveField("buttonSelected")
|
|
dialog.close = true
|
|
end if
|
|
end if
|
|
end if
|
|
end while
|
|
|
|
' Just hide it when done, in case we need to come back
|
|
group.visible = false
|
|
return ""
|
|
end function
|
|
|
|
function CreateHomeGroup()
|
|
' Main screen after logging in. Shows the user's libraries
|
|
group = CreateObject("roSGNode", "Home")
|
|
group.overhangTitle = tr("Home")
|
|
group.optionsAvailable = true
|
|
|
|
group.observeField("selectedItem", m.port)
|
|
group.observeField("quickPlayNode", m.port)
|
|
|
|
sidepanel = group.findNode("options")
|
|
sidepanel.observeField("closeSidePanel", m.port)
|
|
new_options = []
|
|
options_buttons = [
|
|
{ "title": "Search", "id": "goto_search" },
|
|
{ "title": "Change user", "id": "change_user" },
|
|
{ "title": "Change server", "id": "change_server" },
|
|
{ "title": "Sign out", "id": "sign_out" }
|
|
]
|
|
for each opt in options_buttons
|
|
o = CreateObject("roSGNode", "OptionsButton")
|
|
o.title = tr(opt.title)
|
|
o.id = opt.id
|
|
o.observeField("optionSelected", m.port)
|
|
new_options.push(o)
|
|
end for
|
|
|
|
' Add settings option to menu
|
|
o = CreateObject("roSGNode", "OptionsButton")
|
|
o.title = "Settings"
|
|
o.id = "settings"
|
|
o.observeField("optionSelected", m.port)
|
|
new_options.push(o)
|
|
|
|
' And a profile button
|
|
user_node = CreateObject("roSGNode", "OptionsData")
|
|
user_node.id = "active_user"
|
|
user_node.title = tr("Profile")
|
|
user_node.base_title = tr("Profile")
|
|
user_options = []
|
|
for each user in AvailableUsers()
|
|
user_options.push({ display: user.username + "@" + user.server, value: user.id })
|
|
end for
|
|
user_node.choices = user_options
|
|
user_node.value = m.global.session.user.id
|
|
new_options.push(user_node)
|
|
|
|
sidepanel.options = new_options
|
|
|
|
return group
|
|
end function
|
|
|
|
function CreateMovieDetailsGroup(movie as object) as dynamic
|
|
' validate movie node
|
|
if not isValid(movie) or not isValid(movie.id) then return invalid
|
|
|
|
startLoadingSpinner()
|
|
' get movie meta data
|
|
movieMetaData = ItemMetaData(movie.id)
|
|
' validate movie meta data
|
|
if not isValid(movieMetaData)
|
|
stopLoadingSpinner()
|
|
return invalid
|
|
end if
|
|
' start building MovieDetails view
|
|
group = CreateObject("roSGNode", "MovieDetails")
|
|
group.observeField("quickPlayNode", m.port)
|
|
group.overhangTitle = movie.title
|
|
group.optionsAvailable = false
|
|
group.trailerAvailable = false
|
|
' push scene asap (to prevent extra button presses when retriving series/movie info)
|
|
m.global.sceneManager.callFunc("pushScene", group)
|
|
group.itemContent = movieMetaData
|
|
' local trailers
|
|
trailerData = api.users.GetLocalTrailers(m.global.session.user.id, movie.id)
|
|
if isValid(trailerData)
|
|
group.trailerAvailable = trailerData.Count() > 0
|
|
end if
|
|
' watch for button presses
|
|
buttons = group.findNode("buttons")
|
|
for each b in buttons.getChildren(-1, 0)
|
|
b.observeField("buttonSelected", m.port)
|
|
end for
|
|
' setup and load movie extras
|
|
extras = group.findNode("extrasGrid")
|
|
extras.observeField("selectedItem", m.port)
|
|
extras.callFunc("loadParts", movieMetaData.json)
|
|
' done building MovieDetails view
|
|
stopLoadingSpinner()
|
|
return group
|
|
end function
|
|
|
|
function CreateSeriesDetailsGroup(seriesID as string) as dynamic
|
|
' validate series node
|
|
if not isValid(seriesID) or seriesID = "" then return invalid
|
|
|
|
startLoadingSpinner()
|
|
' get series meta data
|
|
seriesMetaData = ItemMetaData(seriesID)
|
|
' validate series meta data
|
|
if not isValid(seriesMetaData)
|
|
stopLoadingSpinner()
|
|
return invalid
|
|
end if
|
|
' Get season data early in the function so we can check number of seasons.
|
|
seasonData = TVSeasons(seriesID)
|
|
' Divert to season details if user setting goStraightToEpisodeListing is enabled and only one season exists.
|
|
if m.global.session.user.settings["ui.tvshows.goStraightToEpisodeListing"] = true and seasonData.Items.Count() = 1
|
|
stopLoadingSpinner()
|
|
return CreateSeasonDetailsGroupByID(seriesID, seasonData.Items[0].id)
|
|
end if
|
|
' start building SeriesDetails view
|
|
group = CreateObject("roSGNode", "TVShowDetails")
|
|
group.optionsAvailable = false
|
|
' push scene asap (to prevent extra button presses when retriving series/movie info)
|
|
m.global.sceneManager.callFunc("pushScene", group)
|
|
group.itemContent = seriesMetaData
|
|
group.seasonData = seasonData
|
|
' watch for button presses
|
|
group.observeField("seasonSelected", m.port)
|
|
group.observeField("quickPlayNode", m.port)
|
|
' setup and load series extras
|
|
extras = group.findNode("extrasGrid")
|
|
extras.observeField("selectedItem", m.port)
|
|
extras.callFunc("loadParts", seriesMetaData.json)
|
|
' done building SeriesDetails view
|
|
stopLoadingSpinner()
|
|
return group
|
|
end function
|
|
|
|
' Shows details on selected artist. Bio, image, and list of available albums
|
|
function CreateArtistView(artist as object) as dynamic
|
|
' validate artist node
|
|
if not isValid(artist) or not isValid(artist.id) then return invalid
|
|
|
|
musicData = MusicAlbumList(artist.id)
|
|
appearsOnData = AppearsOnList(artist.id)
|
|
|
|
if (musicData = invalid or musicData.Items.Count() = 0) and (appearsOnData = invalid or appearsOnData.Items.Count() = 0)
|
|
' Just songs under artists...
|
|
group = CreateObject("roSGNode", "AlbumView")
|
|
group.pageContent = ItemMetaData(artist.id)
|
|
|
|
' Lookup songs based on artist id
|
|
songList = GetSongsByArtist(artist.id)
|
|
|
|
if not isValid(songList)
|
|
' Lookup songs based on folder parent / child relationship
|
|
songList = MusicSongList(artist.id)
|
|
end if
|
|
|
|
if not isValid(songList)
|
|
return invalid
|
|
end if
|
|
|
|
group.albumData = songList
|
|
group.observeField("playSong", m.port)
|
|
group.observeField("playAllSelected", m.port)
|
|
group.observeField("instantMixSelected", m.port)
|
|
else
|
|
' User has albums under artists
|
|
group = CreateObject("roSGNode", "ArtistView")
|
|
group.pageContent = ItemMetaData(artist.id)
|
|
group.musicArtistAlbumData = musicData
|
|
group.musicArtistAppearsOnData = appearsOnData
|
|
group.artistOverview = ArtistOverview(artist.name)
|
|
|
|
group.observeField("musicAlbumSelected", m.port)
|
|
group.observeField("playArtistSelected", m.port)
|
|
group.observeField("instantMixSelected", m.port)
|
|
group.observeField("appearsOnSelected", m.port)
|
|
end if
|
|
|
|
group.observeField("quickPlayNode", m.port)
|
|
m.global.sceneManager.callFunc("pushScene", group)
|
|
|
|
return group
|
|
end function
|
|
|
|
' Shows details on selected album. Description text, image, and list of available songs
|
|
function CreateAlbumView(album as object) as dynamic
|
|
' validate album node
|
|
if not isValid(album) or not isValid(album.id) then return invalid
|
|
|
|
group = CreateObject("roSGNode", "AlbumView")
|
|
m.global.sceneManager.callFunc("pushScene", group)
|
|
|
|
group.pageContent = ItemMetaData(album.id)
|
|
group.albumData = MusicSongList(album.id)
|
|
|
|
' Watch for user clicking on a song
|
|
group.observeField("playSong", m.port)
|
|
|
|
' Watch for user click on Play button on album
|
|
group.observeField("playAllSelected", m.port)
|
|
|
|
' Watch for user click on Instant Mix button on album
|
|
group.observeField("instantMixSelected", m.port)
|
|
|
|
return group
|
|
end function
|
|
|
|
' Shows details on selected playlist. Description text, image, and list of available items
|
|
function CreatePlaylistView(playlist as object) as dynamic
|
|
' validate playlist node
|
|
if not isValid(playlist) or not isValid(playlist.id) then return invalid
|
|
|
|
group = CreateObject("roSGNode", "PlaylistView")
|
|
m.global.sceneManager.callFunc("pushScene", group)
|
|
|
|
group.pageContent = ItemMetaData(playlist.id)
|
|
group.albumData = PlaylistItemList(playlist.id)
|
|
|
|
' Watch for user clicking on an item
|
|
group.observeField("playItem", m.port)
|
|
|
|
' Watch for user click on Play button
|
|
group.observeField("playAllSelected", m.port)
|
|
|
|
return group
|
|
end function
|
|
|
|
function CreateSeasonDetailsGroup(series as object, season as object) as dynamic
|
|
' validate series node
|
|
if not isValid(series) or not isValid(series.id) then return invalid
|
|
' validate season node
|
|
if not isValid(season) or not isValid(season.id) then return invalid
|
|
|
|
startLoadingSpinner()
|
|
' get season meta data
|
|
seasonMetaData = ItemMetaData(season.id)
|
|
' validate season meta data
|
|
if not isValid(seasonMetaData)
|
|
stopLoadingSpinner()
|
|
return invalid
|
|
end if
|
|
' start building SeasonDetails view
|
|
group = CreateObject("roSGNode", "TVEpisodes")
|
|
group.optionsAvailable = false
|
|
' push scene asap (to prevent extra button presses when retriving series/movie info)
|
|
m.global.sceneManager.callFunc("pushScene", group)
|
|
group.seasonData = seasonMetaData.json
|
|
group.objects = TVEpisodes(series.id, season.id)
|
|
group.episodeObjects = group.objects
|
|
group.extrasObjects = TVSeasonExtras(season.id)
|
|
|
|
' watch for button presses
|
|
group.observeField("selectedItem", m.port)
|
|
group.observeField("quickPlayNode", m.port)
|
|
' finished building SeasonDetails view
|
|
stopLoadingSpinner()
|
|
return group
|
|
end function
|
|
|
|
function CreateSeasonDetailsGroupByID(seriesID as string, seasonID as string) as dynamic
|
|
' validate parameters
|
|
if seriesID = "" or seasonID = "" then return invalid
|
|
|
|
startLoadingSpinner()
|
|
' get season meta data
|
|
seasonMetaData = ItemMetaData(seasonID)
|
|
' validate season meta data
|
|
if not isValid(seasonMetaData)
|
|
stopLoadingSpinner()
|
|
return invalid
|
|
end if
|
|
' start building SeasonDetails view
|
|
group = CreateObject("roSGNode", "TVEpisodes")
|
|
group.optionsAvailable = false
|
|
' push scene asap (to prevent extra button presses when retriving series/movie info)
|
|
group.seasonData = seasonMetaData.json
|
|
group.objects = TVEpisodes(seriesID, seasonID)
|
|
group.episodeObjects = group.objects
|
|
' watch for button presses
|
|
group.observeField("episodeSelected", m.port)
|
|
group.observeField("quickPlayNode", m.port)
|
|
' don't wait for the extras button
|
|
stopLoadingSpinner()
|
|
m.global.sceneManager.callFunc("pushScene", group)
|
|
' check for specials/extras for this season
|
|
group.extrasObjects = TVSeasonExtras(seasonID)
|
|
|
|
' finished building SeasonDetails view
|
|
return group
|
|
end function
|
|
|
|
function CreateItemGrid(libraryItem as object) as dynamic
|
|
' validate libraryItem
|
|
if not isValid(libraryItem) then return invalid
|
|
|
|
group = CreateObject("roSGNode", "ItemGrid")
|
|
group.parentItem = libraryItem
|
|
group.optionsAvailable = true
|
|
group.observeField("selectedItem", m.port)
|
|
group.observeField("quickPlayNode", m.port)
|
|
return group
|
|
end function
|
|
|
|
function CreateMovieLibraryView(libraryItem as object) as dynamic
|
|
' validate libraryItem
|
|
if not isValid(libraryItem) then return invalid
|
|
|
|
group = CreateObject("roSGNode", "MovieLibraryView")
|
|
group.parentItem = libraryItem
|
|
group.optionsAvailable = true
|
|
group.observeField("selectedItem", m.port)
|
|
group.observeField("quickPlayNode", m.port)
|
|
return group
|
|
end function
|
|
|
|
function CreateMusicLibraryView(libraryItem as object) as dynamic
|
|
' validate libraryItem
|
|
if not isValid(libraryItem) then return invalid
|
|
|
|
group = CreateObject("roSGNode", "MusicLibraryView")
|
|
group.parentItem = libraryItem
|
|
group.optionsAvailable = true
|
|
group.observeField("selectedItem", m.port)
|
|
group.observeField("quickPlayNode", m.port)
|
|
return group
|
|
end function
|
|
|
|
function CreateSearchPage()
|
|
' Search + Results Page
|
|
group = CreateObject("roSGNode", "searchResults")
|
|
group.observeField("quickPlayNode", m.port)
|
|
options = group.findNode("searchSelect")
|
|
options.observeField("itemSelected", m.port)
|
|
|
|
return group
|
|
end function
|
|
|
|
function CreateVideoPlayerGroup(video_id as string, mediaSourceId = invalid as dynamic, audio_stream_idx = 1 as integer, forceTranscoding = false as boolean, showIntro = true as boolean, allowResumeDialog = true as boolean)
|
|
' validate video_id
|
|
if not isValid(video_id) or video_id = "" then return invalid
|
|
|
|
startLoadingSpinner()
|
|
' Video is Playing
|
|
video = VideoPlayer(video_id, mediaSourceId, audio_stream_idx, defaultSubtitleTrackFromVid(video_id), forceTranscoding, showIntro, allowResumeDialog)
|
|
|
|
if video = invalid then return invalid
|
|
|
|
video.allowCaptions = true
|
|
|
|
if video.errorMsg = "introaborted" then return video
|
|
video.observeField("selectSubtitlePressed", m.port)
|
|
video.observeField("selectPlaybackInfoPressed", m.port)
|
|
video.observeField("state", m.port)
|
|
stopLoadingSpinner()
|
|
return video
|
|
end function
|
|
|
|
function CreatePersonView(personData as object) as dynamic
|
|
' validate personData node
|
|
if not isValid(personData) or not isValid(personData.id) then return invalid
|
|
|
|
startLoadingSpinner()
|
|
' get person meta data
|
|
personMetaData = ItemMetaData(personData.id)
|
|
' validate season meta data
|
|
if not isValid(personMetaData)
|
|
stopLoadingSpinner()
|
|
return invalid
|
|
end if
|
|
' start building Person View
|
|
person = CreateObject("roSGNode", "PersonDetails")
|
|
' push scene asap (to prevent extra button presses when retriving series/movie info)
|
|
m.global.SceneManager.callFunc("pushScene", person)
|
|
person.itemContent = personMetaData
|
|
person.setFocus(true)
|
|
' watch for button presses
|
|
person.observeField("selectedItem", m.port)
|
|
person.findNode("favorite-button").observeField("buttonSelected", m.port)
|
|
' finished building Person View
|
|
stopLoadingSpinner()
|
|
return person
|
|
end function
|
|
|
|
'Opens dialog asking user if they want to resume video or start playback over only on the home screen
|
|
sub playbackOptionDialog(time as longinteger, meta as object)
|
|
|
|
resumeData = [
|
|
tr("Resume playing at ") + ticksToHuman(time) + ".",
|
|
tr("Start over from the beginning.")
|
|
]
|
|
|
|
group = m.global.sceneManager.callFunc("getActiveScene")
|
|
|
|
if LCase(group.subtype()) = "home"
|
|
if LCase(meta.type) = "episode"
|
|
resumeData.push(tr("Go to series"))
|
|
resumeData.push(tr("Go to season"))
|
|
resumeData.push(tr("Go to episode"))
|
|
end if
|
|
end if
|
|
stopLoadingSpinner()
|
|
m.global.sceneManager.callFunc("optionDialog", tr("Playback Options"), [], resumeData)
|
|
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-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-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-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-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> |