diff --git a/source/Main.brs b/source/Main.brs index 8a86ac07..347f4b63 100644 --- a/source/Main.brs +++ b/source/Main.brs @@ -61,44 +61,24 @@ sub Main (args as dynamic) as void end if end if - ' Only show the Whats New popup the first time a user runs a new client version. - appLastRunVersion = get_setting("LastRunVersion") - if m.global.app.version <> appLastRunVersion - ' Ensure the user hasn't disabled Whats New popups + ' Save the global last run version of the app + if m.global.app.version <> m.global.app.lastRunVersion + ' update global LastRunVersion + set_setting("LastRunVersion", m.global.app.version) + + ' Show the Whats New popup if m.global.session.user.settings["load.allowwhatsnew"] = true - set_setting("LastRunVersion", m.global.app.version) dialog = createObject("roSGNode", "WhatsNewDialog") m.scene.dialog = dialog m.scene.dialog.observeField("buttonSelected", m.port) end if end if - ' Registry migrations - if isValid(appLastRunVersion) and not versionChecker(appLastRunVersion, "1.7.0") - ' last app version used less than 1.7.0 - ' no longer saving raw password to registry - ' auth token and username are now stored in user settings and not global settings - print "Running 1.7.0 registry migrations" - ' remove global settings - unset_setting("token") - unset_setting("username") - unset_setting("password") - ' remove user settings - unset_user_setting("password") - ' remove saved credentials from saved_servers - saved = get_setting("saved_servers") - if isValid(saved) - savedServers = ParseJson(saved) - if isValid(savedServers.serverList) and savedServers.serverList.Count() > 0 - newServers = { serverList: [] } - for each item in savedServers.serverList - item.Delete("username") - item.Delete("password") - newServers.serverList.Push(item) - end for - set_setting("saved_servers", FormatJson(newServers)) - end if - end if + ' Save the user last run version of the app + if m.global.session.user.lastRunVersion <> m.global.app.lastRunVersion + ' update user LastRunVersion + set_user_setting("LastRunVersion", m.global.app.version) + session.user.Update("lastRunVersion", m.global.app.version) end if ' Handle input messages diff --git a/source/ShowScenes.brs b/source/ShowScenes.brs index b3b89057..b236aad3 100644 --- a/source/ShowScenes.brs +++ b/source/ShowScenes.brs @@ -43,19 +43,48 @@ function LoginFlow() print "No active user found in registry" user_select: SendPerformanceBeacon("AppDialogInitiate") ' Roku Performance monitoring - Dialog Starting + publicUsers = GetPublicUsers() + savedUsers = getSavedUsers() + numPubUsers = publicUsers.count() - if numPubUsers > 0 + numSavedUsers = savedUsers.count() + + if numPubUsers > 0 or numSavedUsers > 0 publicUsersNodes = [] - 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) - end for + 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 @@ -89,7 +118,7 @@ function LoginFlow() unset_user_setting("username") else print "Success! Auth token is still valid" - session.user.Login(currentUser) + session.user.Login(currentUser, true) session.user.LoadUserPreferences() LoadUserAbilities() return true @@ -102,7 +131,7 @@ function LoginFlow() userData = get_token(userSelected, "") if isValid(userData) print "login success!" - session.user.Login(userData) + session.user.Login(userData, true) session.user.LoadUserPreferences() LoadUserAbilities() return true @@ -145,7 +174,7 @@ function LoginFlow() userData = get_token(myUsername, "") if isValid(userData) print "login success!" - session.user.Login(userData) + session.user.Login(userData, true) session.user.LoadUserPreferences() LoadUserAbilities() return true @@ -158,7 +187,7 @@ function LoginFlow() end if else print "Success! Auth token is still valid" - session.user.Login(currentUser) + session.user.Login(currentUser, true) end if else print "No auth token found in registry" @@ -282,11 +311,11 @@ function CreateServerGroup() m.scene.dialog = dialog serverUrl = standardize_jellyfin_url(screen.serverUrl) - set_setting("server", serverUrl) isConnected = session.server.UpdateURL(serverUrl) serverInfoResult = invalid if isConnected + set_setting("server", serverUrl) serverInfoResult = ServerInfo() end if dialog.close = true @@ -302,9 +331,10 @@ function CreateServerGroup() ' If server redirected received, update the URL if isValid(serverInfoResult.UpdatedUrl) serverUrl = serverInfoResult.UpdatedUrl - set_setting("server", serverUrl) + isConnected = session.server.UpdateURL(serverUrl) if isConnected + set_setting("server", serverUrl) screen.visible = false return "" end if @@ -450,11 +480,14 @@ function CreateSigninGroup(user = "") ' Validate credentials activeUser = get_token(username.value, password.value) if isValid(activeUser) - session.user.Login(activeUser) - ' save credentials + 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 diff --git a/source/api/userauth.brs b/source/api/userauth.brs index 33f32252..a49d156e 100644 --- a/source/api/userauth.brs +++ b/source/api/userauth.brs @@ -33,7 +33,7 @@ function AboutMe(id = "" as string) end function sub SignOut(deleteSavedEntry = true as boolean) - if m.global.session.user.id <> invalid and deleteSavedEntry = true + if deleteSavedEntry unset_user_setting("token") unset_user_setting("username") end if diff --git a/source/migrations.bs b/source/migrations.bs new file mode 100644 index 00000000..3f6359ea --- /dev/null +++ b/source/migrations.bs @@ -0,0 +1,70 @@ +import "pkg:/source/utils/misc.brs" + +' Functions that update the registry based on the last run version and the currently running version + +' Run all necessary registry mirations on the "global" Jellyfin registry section +sub runGlobalMigrations() + ' Global registry migrations + if isValid(m.global.app.lastRunVersion) and not versionChecker(m.global.app.lastRunVersion, "1.7.0") + ' last app version used was less than 1.7.0 + print "Running 1.7.0 global registry migrations" + ' no longer saving raw password to registry + ' auth token and username are now stored in user settings and not global settings + + savedUserId = get_setting("active_user") + if isValid(savedUserId) + registry_write("serverId", m.global.session.server.id, savedUserId) + ' copy saved credentials to user block + savedUsername = get_setting("username") + if isValid(savedUsername) + registry_write("username", savedUsername, savedUserId) + end if + + savedToken = get_setting("token") + if isValid(savedToken) + registry_write("token", savedToken, savedUserId) + end if + end if + unset_setting("port") + unset_setting("token") + unset_setting("username") + unset_setting("password") + ' remove saved credentials from saved_servers + saved = get_setting("saved_servers") + if isValid(saved) + savedServers = ParseJson(saved) + if isValid(savedServers.serverList) and savedServers.serverList.Count() > 0 + newServers = { serverList: [] } + for each item in savedServers.serverList + item.Delete("username") + item.Delete("password") + newServers.serverList.Push(item) + end for + set_setting("saved_servers", FormatJson(newServers)) + end if + end if + end if + if m.global.app.lastRunVersion <> invalid + runRegistryUserMigrations(m.global.app.lastRunVersion) + end if +end sub + +sub runRegistryUserMigrations(version as string) + regSections = getRegistrySections() + for each section in regSections + if LCase(section) <> "jellyfin" + if version = "1.7.0" + print "Running User Registry Migration for 1.7.0" + ' now saving LastRunVersion globally and per user so that we can run user specific registry migrations + ' duplicate LastRunVersion to all user settings in the registry so that we can run user specific migrations + ' + ' now saving LastRunVersion per user in addition to globally + registry_write("LastRunVersion", m.global.app.version, section) + ' no longer saving password to registry + registry_delete("password", section) + ' av1 playback no longer hidden behind user setting + registry_delete("playback.av1", section) + end if + end if + end for +end sub diff --git a/source/utils/config.brs b/source/utils/config.brs index 7b8f3887..d6c184b1 100644 --- a/source/utils/config.brs +++ b/source/utils/config.brs @@ -37,7 +37,7 @@ function RegistryReadAll(section as string) as dynamic registryData = {} for each item in regKeyList ' ignore session related tokens - if item <> "token" and item <> "username" and item <> "password" + if item <> "token" and item <> "username" and item <> "password" and LCase(item) <> "lastrunversion" if registry.Exists(item) registryData.AddReplace(item, registry.Read(item)) end if @@ -47,6 +47,12 @@ function RegistryReadAll(section as string) as dynamic return registryData end function +' Return an array of all the registry section keys +function getRegistrySections() as object + registry = CreateObject("roRegistry") + return registry.GetSectionList() +end function + ' "Jellyfin" registry accessors for the default global settings function get_setting(key, defaultValue = invalid) value = registry_read(key, "Jellyfin") @@ -94,3 +100,40 @@ function findConfigTreeKey(key as string, tree) return invalid end function + +' Returns an array of saved users from the registry +' that belong to the active server +function getSavedUsers() as object + registrySections = getRegistrySections() + + savedUsers = [] + for each section in registrySections + if LCase(section) <> "jellyfin" + savedUsers.push(section) + end if + end for + + savedServerUsers = [] + for each userId in savedUsers + userArray = { + id: userId + } + token = registry_read("token", userId) + + username = registry_read("username", userId) + if username <> invalid + userArray["username"] = username + end if + + serverId = registry_read("serverId", userId) + if serverId <> invalid + userArray["serverId"] = serverId + end if + + if username <> invalid and token <> invalid and serverId <> invalid and serverId = m.global.session.server.id + savedServerUsers.push(userArray) + end if + end for + + return savedServerUsers +end function diff --git a/source/utils/globals.brs b/source/utils/globals.brs index 943984c7..538ce30d 100644 --- a/source/utils/globals.brs +++ b/source/utils/globals.brs @@ -28,11 +28,13 @@ end sub ' Save information from roAppInfo to m.global.app sub SaveAppToGlobal() appInfo = CreateObject("roAppInfo") + lastRunVersion = get_setting("LastRunVersion") m.global.addFields({ app: { id: appInfo.GetID(), isDev: appInfo.IsDev(), - version: appInfo.GetVersion() + version: appInfo.GetVersion(), + lastRunVersion: lastRunVersion } }) end sub diff --git a/source/utils/session.bs b/source/utils/session.bs index c84f3ef6..9b5665a7 100644 --- a/source/utils/session.bs +++ b/source/utils/session.bs @@ -1,6 +1,7 @@ ' these are needed for ServerInfo() inside session.server.Populate() import "pkg:/source/api/userauth.brs" import "pkg:/source/api/baserequest.brs" +import "pkg:/source/migrations.bs" namespace session ' Initialize the global session array @@ -11,7 +12,8 @@ namespace session user: { Configuration: {}, Policy: {}, - settings: {} + settings: {}, + lastRunVersion: invalid } } }) @@ -70,6 +72,9 @@ namespace session session.server.Delete() end if + ' migrate registry if needed + runGlobalMigrations() + return success end function @@ -121,7 +126,7 @@ namespace session ' 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) + 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 @@ -146,6 +151,12 @@ namespace session ' update global user session session.Update("user", tmpSession.user) + ' grab lastRunVersion for this user + lastRunVersion = get_user_setting("LastRunVersion") + if lastRunVersion <> invalid + 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 @@ -156,7 +167,9 @@ namespace session print "m.global.session.user.settings = ", m.global.session.user.settings end if - if m.global.session.user.settings["global.rememberme"] + 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