490 lines
23 KiB
Text
490 lines
23 KiB
Text
' these are needed for ServerInfo() inside session.server.Populate()
|
|
import "pkg:/source/api/baserequest.bs"
|
|
import "pkg:/source/api/userauth.bs"
|
|
import "pkg:/source/enums/LiveTVChannelSortOrder.bs"
|
|
import "pkg:/source/enums/LiveTVScreen.bs"
|
|
import "pkg:/source/enums/String.bs"
|
|
import "pkg:/source/utils/misc.bs"
|
|
|
|
namespace session
|
|
' Initialize the global session array
|
|
sub Init()
|
|
m.global.addFields({
|
|
session: {
|
|
"memoryLevel": "normal",
|
|
server: {},
|
|
user: {
|
|
Configuration: {},
|
|
Policy: {},
|
|
settings: {},
|
|
lastRunVersion: invalid
|
|
}
|
|
}
|
|
})
|
|
session.user.settings.SaveDefaults()
|
|
end sub
|
|
|
|
' Empty the global session array
|
|
sub Delete()
|
|
session.server.Delete()
|
|
session.user.Logout()
|
|
end sub
|
|
|
|
' Update one value from the global session array (m.global.session)
|
|
sub Update(key as string, value = {} as object)
|
|
' validate parameters
|
|
if key = "" or (key <> "user" and key <> "server") or value = invalid
|
|
print "Error in session.Update(): Invalid parameters provided"
|
|
return
|
|
end if
|
|
' make copy of global session array
|
|
tmpSession = m.global.session
|
|
' update the temp session array
|
|
tmpSession.AddReplace(key, value)
|
|
' use the temp session array to update the global node
|
|
m.global.setFields({ session: tmpSession })
|
|
end sub
|
|
|
|
namespace server
|
|
' Empty the global server session array
|
|
sub Delete()
|
|
session.Update("server")
|
|
end sub
|
|
|
|
' Add or update one value from the global server session array (m.global.session.server)
|
|
sub Update(key as string, value as dynamic)
|
|
' validate parameters
|
|
if key = "" or value = invalid then return
|
|
' make copy of global server session array
|
|
tmpSessionServer = m.global.session.server
|
|
' update the temp server array
|
|
tmpSessionServer[key] = value
|
|
|
|
session.Update("server", tmpSessionServer)
|
|
end sub
|
|
|
|
' Add or update the jellyfin server URL from the global server session array (m.global.session.server)
|
|
' Returns a boolean based on if a connection to the Jellyfin server was made
|
|
function UpdateURL(value as string) as boolean
|
|
' validate parameters
|
|
if value = "" then return false
|
|
session.server.Update("url", value)
|
|
|
|
success = session.server.Populate()
|
|
if not success
|
|
session.server.Delete()
|
|
end if
|
|
|
|
return success
|
|
end function
|
|
|
|
' Use the saved server url to populate the global server session array (m.global.session.server)
|
|
' Returns a boolean based on if a connection to the Jellyfin server was made
|
|
function Populate() as boolean
|
|
' validate server url
|
|
if m.global.session.server.url = invalid or m.global.session.server.url = "" then return false
|
|
' get server info using API
|
|
myServerInfo = ServerInfo()
|
|
' validate data returned from API
|
|
if myServerInfo.id = invalid then return false
|
|
' make copy of global server session
|
|
tmpSessionServer = m.global.session.server
|
|
' update the temp array
|
|
tmpSessionServer.AddReplace("id", myServerInfo.Id)
|
|
tmpSessionServer.AddReplace("name", myServerInfo.ServerName)
|
|
tmpSessionServer.AddReplace("localURL", myServerInfo.LocalAddress)
|
|
tmpSessionServer.AddReplace("os", myServerInfo.OperatingSystem)
|
|
tmpSessionServer.AddReplace("startupWizardCompleted", myServerInfo.StartupWizardCompleted)
|
|
tmpSessionServer.AddReplace("version", myServerInfo.Version)
|
|
tmpSessionServer.AddReplace("hasError", myServerInfo.error)
|
|
' check urls for https
|
|
isServerHTTPS = false
|
|
if tmpSessionServer.url.left(8) = "https://" then isServerHTTPS = true
|
|
tmpSessionServer.AddReplace("isHTTPS", isServerHTTPS)
|
|
isLocalServerHTTPS = false
|
|
if myServerInfo.LocalAddress <> invalid and myServerInfo.LocalAddress.left(8) = "https://" then isLocalServerHTTPS = true
|
|
tmpSessionServer.AddReplace("isLocalHTTPS", isLocalServerHTTPS)
|
|
' update global server session using the temp array
|
|
session.Update("server", tmpSessionServer)
|
|
|
|
return true
|
|
end function
|
|
end namespace
|
|
|
|
namespace user
|
|
|
|
' Add or update one value from the global user session array (m.global.session.user)
|
|
sub Update(key as string, value as dynamic)
|
|
' validate parameters
|
|
if key = "" or value = invalid then return
|
|
|
|
' make copy of global user session
|
|
tmpSessionUser = m.global.session.user
|
|
' update the temp user array
|
|
tmpSessionUser[key] = value
|
|
|
|
' keep friendlyName in sync
|
|
if LCase(key) = "name"
|
|
regex = CreateObject("roRegex", "[^a-zA-Z0-9\-\_]", string.EMPTY)
|
|
tmpSessionUser["friendlyName"] = regex.ReplaceAll(value, string.EMPTY)
|
|
end if
|
|
|
|
' update global user session using the temp array
|
|
session.Update("user", tmpSessionUser)
|
|
|
|
' keep auth header in sync
|
|
if LCase(key) = "name"
|
|
session.user.SetServerDeviceName()
|
|
end if
|
|
end sub
|
|
|
|
' Update the global session after user is authenticated.
|
|
' Accepts a UserData.xml object from get_token() or an assocArray from AboutMe()
|
|
sub Login(userData as object, saveCredentials = false as boolean)
|
|
' validate parameters
|
|
if userData = invalid or userData.id = invalid then return
|
|
' make copy of global user session array
|
|
tmpSession = m.global.session
|
|
oldUserSettings = tmpSession.user.settings
|
|
if userData.json = invalid
|
|
' we were passed data from AboutMe()
|
|
myAuthToken = tmpSession.user.authToken
|
|
tmpSession.AddReplace("user", userData)
|
|
tmpSession.user.AddReplace("authToken", myAuthToken)
|
|
else
|
|
' we were passed data from a UserData object
|
|
tmpSession.AddReplace("user", userData.json.User)
|
|
tmpSession.user.AddReplace("authToken", userData.json.AccessToken)
|
|
end if
|
|
' remove special characters from name
|
|
regex = CreateObject("roRegex", "[^a-zA-Z0-9\-\_]", string.EMPTY)
|
|
friendlyName = regex.ReplaceAll(tmpSession.user.name, string.EMPTY)
|
|
tmpSession.user.AddReplace("friendlyName", friendlyName)
|
|
|
|
tmpSession.user.AddReplace("settings", oldUserSettings)
|
|
' update global user session
|
|
session.Update("user", tmpSession.user)
|
|
|
|
' grab lastRunVersion for this user
|
|
lastRunVersion = get_user_setting("LastRunVersion")
|
|
if isValid(lastRunVersion)
|
|
session.user.Update("LastRunVersion", lastRunVersion)
|
|
end if
|
|
|
|
' update user session settings with values from registry
|
|
userSettings = RegistryReadAll(tmpSession.user.id)
|
|
for each setting in userSettings
|
|
' don't add auth token to user session
|
|
if setting <> "token"
|
|
session.user.settings.Save(setting, userSettings[setting])
|
|
end if
|
|
end for
|
|
|
|
set_user_setting("serverId", m.global.session.server.id)
|
|
|
|
if saveCredentials
|
|
set_user_setting("token", tmpSession.user.authToken)
|
|
set_user_setting("username", tmpSession.user.name)
|
|
end if
|
|
|
|
if m.global.session.user.settings["global.rememberme"]
|
|
set_setting("active_user", tmpSession.user.id)
|
|
end if
|
|
|
|
user = CreateObject("roSGNode", "UserData")
|
|
user.json = tmpSession
|
|
user.callFunc("saveToRegistry")
|
|
|
|
' Save device id so we don't calculate it every time we need it
|
|
session.user.SetServerDeviceName()
|
|
' Load user preferences from server
|
|
session.user.LoadUserPreferences()
|
|
end sub
|
|
|
|
' Sets the global server device name value used by the API
|
|
sub SetServerDeviceName()
|
|
localGlobal = m.global
|
|
|
|
' default device name is the unique id for the device
|
|
deviceName = localGlobal.device.id
|
|
|
|
if isChainValid(localGlobal, "session.user.friendlyName")
|
|
deviceName = deviceName + localGlobal.session.user.friendlyName
|
|
end if
|
|
|
|
' update global if needed
|
|
if localGlobal.device.serverDeviceName <> deviceName
|
|
tmpDevice = localGlobal.device
|
|
tmpDevice.AddReplace("serverDeviceName", deviceName)
|
|
m.global.setFields({ device: tmpDevice })
|
|
end if
|
|
end sub
|
|
|
|
' Load and parse Display Settings from server
|
|
sub LoadUserPreferences()
|
|
id = m.global.session.user.id
|
|
' Currently using client "emby", which is what website uses so we get same Display prefs as web.
|
|
' May want to change to specific Roku display settings
|
|
url = Substitute("DisplayPreferences/usersettings?userId={0}&client=emby", id)
|
|
resp = APIRequest(url)
|
|
jsonResponse = getJson(resp)
|
|
|
|
if isValid(jsonResponse) and isValid(jsonResponse.CustomPrefs)
|
|
' save useEpisodeImagesInNextUpAndResume to global session
|
|
tmpSetting = jsonResponse.CustomPrefs.useEpisodeImagesInNextUpAndResume
|
|
if isValid(tmpSetting)
|
|
tmpConfig = m.global.session.user.Configuration
|
|
tmpConfig.AddReplace("useEpisodeImagesInNextUpAndResume", toBoolean(tmpSetting))
|
|
session.user.Update("Configuration", tmpConfig)
|
|
end if
|
|
|
|
session.user.SaveUserGuideSettings(jsonResponse.CustomPrefs)
|
|
session.user.SaveUserHomeSections(jsonResponse.CustomPrefs)
|
|
else
|
|
homeSection0 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection0", "smalllibrarytiles")
|
|
homeSection1 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection1", "resume")
|
|
homeSection2 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection2", "nextup")
|
|
homeSection3 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection3", "latestmedia")
|
|
homeSection4 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection4", "livetv")
|
|
homeSection5 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection5", "mylist")
|
|
homeSection6 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection6", "favorites")
|
|
homeSection7 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection7", "none")
|
|
|
|
' User has no custom prefs. Save default home section values.
|
|
session.user.SaveUserHomeSections({
|
|
homesection0: homeSection0,
|
|
homesection1: homeSection1,
|
|
homesection2: homeSection2,
|
|
homesection3: homeSection3,
|
|
homesection4: homeSection4,
|
|
homesection5: homeSection5,
|
|
homesection6: homeSection6,
|
|
homesection7: homeSection7
|
|
})
|
|
end if
|
|
end sub
|
|
|
|
' Saves user's Live TV guide settings as Roku guide user settings.
|
|
sub SaveUserGuideSettings(customPrefs as object)
|
|
displayLive = "true"
|
|
displayRepeat = "false"
|
|
displayScreen = LiveTVScreen.Guide
|
|
liveTvChannelOrder = "SortName"
|
|
liveTvFavoritesAtTop = "false"
|
|
|
|
if isValid(customPrefs)
|
|
userPreferenceDisplayLive = customPrefs["guide-indicator-live"]
|
|
userPreferenceDisplayRepeat = customPrefs["guide-indicator-repeat"]
|
|
userPreferenceDisplayScreen = customPrefs["landing-livetv"]
|
|
userPreferenceLiveTvChannelOrder = customPrefs["livetv-channelorder"]
|
|
userPreferenceLiveTvFavoritesAtTop = customPrefs["livetv-favoritechannelsattop"]
|
|
|
|
if isValidAndNotEmpty(userPreferenceDisplayLive)
|
|
displayLive = userPreferenceDisplayLive
|
|
end if
|
|
if isValidAndNotEmpty(userPreferenceDisplayRepeat)
|
|
displayRepeat = userPreferenceDisplayRepeat
|
|
end if
|
|
if isStringEqual(userPreferenceDisplayScreen, LiveTVScreen.CHANNELS)
|
|
' Live TV has five options but Roku only supports guide and channels in this context
|
|
displayScreen = userPreferenceDisplayScreen
|
|
end if
|
|
if isValidAndNotEmpty(userPreferenceLiveTvChannelOrder)
|
|
if isStringEqual(userPreferenceLiveTvChannelOrder, LiveTVChannelSortOrder.DATEPLAYED)
|
|
liveTvChannelOrder = "DatePlayed"
|
|
else
|
|
liveTvChannelOrder = "SortName"
|
|
end if
|
|
end if
|
|
if isValidAndNotEmpty(userPreferenceLiveTvFavoritesAtTop)
|
|
liveTvFavoritesAtTop = userPreferenceLiveTvFavoritesAtTop
|
|
end if
|
|
|
|
end if
|
|
|
|
set_user_setting("ui.guide.indicator.live", displayLive)
|
|
set_user_setting("ui.guide.indicator.repeat", displayRepeat)
|
|
set_user_setting("display.livetv.landing", displayScreen)
|
|
set_user_setting("display.livetv.sortField", liveTvChannelOrder)
|
|
set_user_setting("ui.guide.favorites.at.top", liveTvFavoritesAtTop)
|
|
|
|
' Always start off ascending when sorted by channel number, descending when sorted by date played
|
|
print "Setting ascending to " + isStringEqual(liveTvChannelOrder, "SortName").tostr()
|
|
set_user_setting("display.livetv.sortAscending", (isStringEqual(liveTvChannelOrder, "SortName")).tostr())
|
|
end sub
|
|
|
|
' Saves user's web client home sections as Roku user settings.
|
|
' Handles unsupported sections and ignores duplicates.
|
|
sub SaveUserHomeSections(customPrefs as object)
|
|
userPreferences = customPrefs
|
|
rowTypes = []
|
|
|
|
useWebSectionArrangement = m.global.session.user.settings["ui.home.useWebSectionArrangement"]
|
|
|
|
if isValid(useWebSectionArrangement)
|
|
if not useWebSectionArrangement
|
|
userPreferences.delete("homesection0")
|
|
end if
|
|
end if
|
|
|
|
homeSection0 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection0", "smalllibrarytiles")
|
|
homeSection1 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection1", "resume")
|
|
homeSection2 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection2", "nextup")
|
|
homeSection3 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection3", "latestmedia")
|
|
homeSection4 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection4", "livetv")
|
|
homeSection5 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection5", "mylist")
|
|
homeSection6 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection6", "favorites")
|
|
homeSection7 = chainLookupReturn(m.global, "session.user.settings.homeScreenSection7", "none")
|
|
|
|
' If user has no section preferences, use default settings
|
|
if not userPreferences.doesExist("homesection0")
|
|
userPreferences = {
|
|
homesection0: homeSection0,
|
|
homesection1: homeSection1,
|
|
homesection2: homeSection2,
|
|
homesection3: homeSection3,
|
|
homesection4: homeSection4,
|
|
homesection5: homeSection5,
|
|
homesection6: homeSection6,
|
|
homesection7: homeSection7
|
|
}
|
|
end if
|
|
|
|
for i = 0 to 7
|
|
homeSectionKey = "homesection" + i.toStr()
|
|
|
|
' If home section doesn't exist, create it as a none row
|
|
if not userPreferences.DoesExist(homeSectionKey)
|
|
userPreferences.AddReplace(homeSectionKey, "none")
|
|
end if
|
|
|
|
rowType = LCase(userPreferences[homeSectionKey])
|
|
|
|
' Just in case we get invalid data
|
|
if not isValid(rowType) then rowType = "none"
|
|
|
|
' Small size library item buttons - Currently unsupported, use small library titles
|
|
if rowType = "librarybuttons"
|
|
rowType = "smalllibrarytiles"
|
|
end if
|
|
|
|
' None is the only section type allowed to have duplicates
|
|
' For all other types, only accept the 1st entry
|
|
if inArray(rowTypes, rowType)
|
|
set_user_setting(homeSectionKey, "none")
|
|
else
|
|
set_user_setting(homeSectionKey, rowType)
|
|
|
|
if rowType <> "none"
|
|
rowTypes.push(rowType)
|
|
end if
|
|
end if
|
|
end for
|
|
end sub
|
|
|
|
' Empty the global user session array and reload defaults
|
|
sub Logout()
|
|
session.Update("user", {
|
|
Configuration: {},
|
|
Policy: {},
|
|
settings: {}
|
|
})
|
|
' reload default user settings
|
|
session.user.settings.SaveDefaults()
|
|
end sub
|
|
|
|
namespace settings
|
|
' Delete the user setting from the global session (m.global.session.user.settings)
|
|
sub Delete(name as string)
|
|
' validate parameters
|
|
if name = "" then return
|
|
tmpSettingArray = m.global.session.user.settings
|
|
' update the temp user array
|
|
tmpSettingArray.Delete(name)
|
|
' update global user session using the temp array
|
|
session.user.Update("settings", tmpSettingArray)
|
|
end sub
|
|
|
|
' Read the user setting from the global session (m.global.session.user.settings)
|
|
function Read(name as string) as dynamic
|
|
' validate parameters
|
|
if name = "" then return invalid
|
|
|
|
if m.global.session.user.settings[name] <> invalid
|
|
return m.global.session.user.settings[name]
|
|
else
|
|
return invalid
|
|
end if
|
|
end function
|
|
|
|
' retrieve all default user settings from Config Tree
|
|
sub SaveDefaults()
|
|
configTree = GetConfigTree()
|
|
if configTree = invalid then return
|
|
|
|
for each item in configTree
|
|
if item.default <> invalid and item.settingName <> invalid
|
|
session.user.settings.Save(item.settingName, item.default)
|
|
else if item.children <> invalid and item.children.Count() > 0
|
|
for each child in item.children
|
|
if child.default <> invalid and child.settingName <> invalid
|
|
session.user.settings.Save(child.settingName, child.default)
|
|
else if child.children <> invalid and child.children.Count() > 0
|
|
for each child in child.children
|
|
if child.default <> invalid and child.settingName <> invalid
|
|
session.user.settings.Save(child.settingName, child.default)
|
|
else if child.children <> invalid and child.children.Count() > 0
|
|
for each child in child.children
|
|
if child.default <> invalid and child.settingName <> invalid
|
|
session.user.settings.Save(child.settingName, child.default)
|
|
else if child.children <> invalid and child.children.Count() > 0
|
|
for each child in child.children
|
|
if child.default <> invalid and child.settingName <> invalid
|
|
session.user.settings.Save(child.settingName, child.default)
|
|
else if child.children <> invalid and child.children.Count() > 0
|
|
for each child in child.children
|
|
if child.default <> invalid and child.settingName <> invalid
|
|
session.user.settings.Save(child.settingName, child.default)
|
|
end if
|
|
end for
|
|
end if
|
|
end for
|
|
end if
|
|
end for
|
|
end if
|
|
end for
|
|
end if
|
|
end for
|
|
end if
|
|
end for
|
|
|
|
' load globals
|
|
session.user.settings.LoadGlobals()
|
|
' Reset server device name state
|
|
session.user.SetServerDeviceName()
|
|
end sub
|
|
|
|
' Grab global vars from registry and overwrite defaults
|
|
sub LoadGlobals()
|
|
' search main registry block for all keys that start with "global."
|
|
jfRegistry = RegistryReadAll("Jellyfin")
|
|
for each item in jfRegistry
|
|
if Left(item, 7) = "global."
|
|
session.user.settings.Save(item, get_setting(item))
|
|
end if
|
|
end for
|
|
end sub
|
|
|
|
' Saves the user setting to the global session.
|
|
' This also converts strings to boolean as necessary before saving to global session
|
|
sub Save(name as string, value as string)
|
|
if name = invalid or value = invalid then return
|
|
tmpSettingArray = m.global.session.user.settings
|
|
|
|
tmpSettingArray[name] = toBoolean(value)
|
|
|
|
session.user.Update("settings", tmpSettingArray)
|
|
end sub
|
|
end namespace
|
|
end namespace
|
|
end namespace
|