Merge pull request #1374 from cewert/make-back-work-on-user-select

Fix login bugs, enable support for saving user's credentials, and add "Change User" and "Remember Me?" options
This commit is contained in:
Charles Ewert 2023-09-21 13:01:14 -04:00 committed by GitHub
commit 51c629ced1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 220 additions and 145 deletions

View File

@ -77,16 +77,17 @@ end sub
sub popScene()
group = m.groups.pop()
if group <> invalid
if group.isSubType("JFGroup")
groupType = group.subtype()
if groupType = "JFGroup"
unregisterOverhangData(group)
else if group.isSubType("JFVideo")
else if groupType = "JFVideo"
' Stop video to make sure app communicates stop playstate to server
group.control = "stop"
end if
group.visible = false
if group.isSubType("JFScreen")
if groupType = "JFScreen"
group.callFunc("OnScreenHidden")
end if
else

View File

@ -20,9 +20,6 @@ sub loadFromRegistry(id as string)
end sub
sub saveToRegistry()
set_user_setting("username", m.top.username)
set_user_setting("token", m.top.token)
users = parseJson(get_setting("available_users", "[]"))
this_user = invalid
for each user in users
@ -57,7 +54,9 @@ function setPreference(key as string, value as string)
end function
sub setActive()
set_setting("active_user", m.top.id)
if m.global.session.user.settings["global.rememberme"]
set_setting("active_user", m.top.id)
end if
end sub
sub setServer(hostname as string)

View File

@ -1,6 +1,7 @@
import "pkg:/source/utils/config.brs"
import "pkg:/source/utils/misc.brs"
import "pkg:/source/roku_modules/log/LogMixin.brs"
import "pkg:/source/api/sdk.bs"
sub init()
m.log = log.Logger("Settings")
@ -158,14 +159,40 @@ end sub
sub boolSettingChanged()
if m.boolSetting.focusedChild = invalid then return
selectedSetting = m.userLocation.peek().children[m.settingsMenu.itemFocused]
if m.boolSetting.checkedItem
set_user_setting(selectedSetting.settingName, "true")
session.user.settings.Save(selectedSetting.settingName, "true")
if Left(selectedSetting.settingName, 7) = "global."
' global user setting
' save to main registry block
set_setting(selectedSetting.settingName, "true")
' setting specific triggers
if selectedSetting.settingName = "global.rememberme"
print "m.global.session.user.id=", m.global.session.user.id
set_setting("active_user", m.global.session.user.id)
end if
else
' regular user setting
' save to user specific registry block
set_user_setting(selectedSetting.settingName, "true")
end if
else
set_user_setting(selectedSetting.settingName, "false")
session.user.settings.Save(selectedSetting.settingName, "false")
if Left(selectedSetting.settingName, 7) = "global."
' global user setting
' save to main registry block
set_setting(selectedSetting.settingName, "false")
' setting specific triggers
if selectedSetting.settingName = "global.rememberme"
unset_setting("active_user")
end if
else
' regular user setting
' save to user specific registry block
set_user_setting(selectedSetting.settingName, "false")
end if
end if
end sub

View File

@ -1208,5 +1208,25 @@
<translation>Disable the HEVC codec on this device. This may improve playback for some devices (ultra).</translation>
<extracomment>User Setting - Setting description</extracomment>
</message>
<message>
<source>Global</source>
<translation>Global</translation>
<extracomment>User Setting - Setting title</extracomment>
</message>
<message>
<source>Global settings that affect everyone that uses this Roku device.</source>
<translation>Global settings that affect everyone that uses this Roku device.</translation>
<extracomment>User Setting - Setting description</extracomment>
</message>
<message>
<source>Remember Me?</source>
<translation>Remember Me?</translation>
<extracomment>User Setting - Setting title</extracomment>
</message>
<message>
<source>Remember the currently logged in user and try to log them in again next time you start the Jellyfin app.</source>
<translation>Remember the currently logged in user and try to log them in again next time you start the Jellyfin app.</translation>
<extracomment>User Setting - Setting description</extracomment>
</message>
</context>
</TS>

View File

@ -1,4 +1,18 @@
[
{
"title": "Global",
"description": "Global settings that affect everyone that uses this Roku device.",
"children": [
{
"title": "Remember Me?",
"description": "Remember the currently logged in user and try to log them in again next time you start the Jellyfin app.",
"settingName": "global.rememberme",
"type": "bool",
"default": "false"
}
]
},
{
"title": "Playback",
"description": "Settings relating to playback and supported codec and media types.",

View File

@ -62,7 +62,8 @@ sub Main (args as dynamic) as void
end if
' Only show the Whats New popup the first time a user runs a new client version.
if m.global.app.version <> get_setting("LastRunVersion")
appLastRunVersion = get_setting("LastRunVersion")
if m.global.app.version <> appLastRunVersion
' Ensure the user hasn't disabled Whats New popups
if m.global.session.user.settings["load.allowwhatsnew"] = true
set_setting("LastRunVersion", m.global.app.version)
@ -72,6 +73,34 @@ sub Main (args as dynamic) as void
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
end if
' Handle input messages
input = CreateObject("roInput")
input.SetMessagePort(m.port)
@ -511,7 +540,11 @@ sub Main (args as dynamic) as void
group.findNode("SearchBox").findNode("search_Key").active = true
else if button.id = "change_server"
unset_setting("server")
unset_setting("port")
session.server.Delete()
SignOut(false)
sceneManager.callFunc("clearScenes")
goto app_start
else if button.id = "change_user"
SignOut(false)
sceneManager.callFunc("clearScenes")
goto app_start

View File

@ -1,4 +1,4 @@
function LoginFlow(startOver = false as boolean)
function LoginFlow()
'Collect Jellyfin server and user information
start_login:
@ -41,9 +41,11 @@ function LoginFlow(startOver = false as boolean)
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()
if publicUsers.count()
numPubUsers = publicUsers.count()
if numPubUsers > 0
publicUsersNodes = []
for each item in publicUsers
user = CreateObject("roSGNode", "PublicUserData")
@ -55,18 +57,57 @@ function LoginFlow(startOver = false as boolean)
publicUsersNodes.push(user)
end for
userSelected = CreateUserSelectGroup(publicUsersNodes)
SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
if userSelected = "backPressed"
SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
return LoginFlow(true)
session.server.Delete()
unset_setting("server")
goto start_login
else
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)
LoadUserPreferences()
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)
LoadUserPreferences()
LoadUserAbilities()
SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
return true
else
print "Auth failed. Password required"
end if
end if
else
@ -75,65 +116,52 @@ function LoginFlow(startOver = false as boolean)
passwordEntry = CreateSigninGroup(userSelected)
SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
if passwordEntry = "backPressed"
m.global.sceneManager.callFunc("clearScenes")
return LoginFlow(true)
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)
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 - restart login flow"
unset_user_setting("token")
unset_setting("active_user")
session.user.Logout()
goto start_login
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(userSelected, "")
if isValid(userData)
print "login success!"
session.user.Login(userData)
LoadUserPreferences()
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)
end if
else
print "No auth token found in registry"
myUsername = get_setting("username")
myPassword = get_setting("password")
userData = invalid
if isValid(myUsername) and isValid(myPassword)
if myUsername <> ""
print "Username and password found in registry. Attempting to login"
userData = get_token(myUsername, myPassword)
else
print "Username in registry is an empty string"
unset_setting("username")
unset_setting("password")
end if
else if isValid(myUsername) and not isValid(myPassword)
print "Username found in registry but no password"
if myUsername <> ""
print "Attempting to login with no password"
userData = get_token(myUsername, "")
else
print "Username in registry is an empty string"
unset_setting("username")
end if
else if not isValid(myUsername) and not isValid(myPassword)
print "Neither username nor password found in registry - restart login flow"
unset_setting("active_user")
session.user.Logout()
goto start_login
end if
if isValid(userData)
print "login success!"
session.user.Login(userData)
end if
end if
end if
@ -254,11 +282,6 @@ function CreateServerGroup()
m.scene.dialog = dialog
serverUrl = standardize_jellyfin_url(screen.serverUrl)
'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)
isConnected = session.server.UpdateURL(serverUrl)
@ -362,25 +385,6 @@ function CreateSigninGroup(user = "")
group.findNode("prompt").text = tr("Sign In")
'Load in any saved server data and see if we can just log them in...
server = m.global.session.server.url
if isValid(server)
server = LCase(server)'Saved server data is always lowercase
end if
saved = get_setting("saved_servers")
if isValid(saved)
savedServers = ParseJson(saved)
for each item in savedServers.serverList
if item.baseUrl = server and isValid(item.username) and isValid(item.password)
userData = get_token(item.username, item.password)
if isValid(userData)
session.user.Login(userData)
return "true"
end if
end if
end for
end if
config = group.findNode("configOptions")
username_field = CreateObject("roSGNode", "ConfigData")
username_field.label = tr("Username")
@ -447,11 +451,10 @@ function CreateSigninGroup(user = "")
activeUser = get_token(username.value, password.value)
if isValid(activeUser)
session.user.Login(activeUser)
set_setting("username", username.value)
set_setting("password", password.value)
' save credentials
if checkbox.checkedState[0] = true
'Update our saved server list, so next time the user can just click and go
UpdateSavedServerList()
set_user_setting("token", activeUser.token)
set_user_setting("username", username.value)
end if
return "true"
end if
@ -515,6 +518,7 @@ function CreateHomeGroup()
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" }
]
@ -862,34 +866,6 @@ function CreatePersonView(personData as object) as dynamic
return person
end function
sub UpdateSavedServerList()
server = m.global.session.server.url
username = get_setting("username")
password = get_setting("password")
if server = invalid or username = invalid or password = invalid
return
end if
server = LCase(server)'Saved server data is always lowercase
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
if item.baseUrl = server
item.username = username
item.password = password
end if
newServers.serverList.Push(item)
end for
set_setting("saved_servers", FormatJson(newServers))
end if
end if
end sub
'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)

View File

@ -203,12 +203,16 @@ function authRequest(request as object) as object
if m.global.session.user.id <> invalid
auth = auth + ", UserId=" + QUOTE + m.global.session.user.id + QUOTE
auth = auth + ", DeviceId=" + QUOTE + m.global.device.id + QUOTE
if m.global.session.user.authToken <> invalid
auth = auth + ", Token=" + QUOTE + m.global.session.user.authToken + QUOTE
end if
end if
if m.global.session.user <> invalid and m.global.session.user.friendlyName <> invalid
auth = auth + ", DeviceId=" + QUOTE + m.global.device.id + m.global.session.user.friendlyName + QUOTE
else
auth = auth + ", DeviceId=" + QUOTE + m.global.device.uuid + QUOTE
auth = auth + ", DeviceId=" + QUOTE + m.global.device.id + QUOTE
end if
if m.global.session.user.authToken <> invalid
auth = auth + ", Token=" + QUOTE + m.global.session.user.authToken + QUOTE
end if
request.AddHeader("Authorization", auth)

View File

@ -32,28 +32,9 @@ function AboutMe(id = "" as string)
end function
sub SignOut(deleteSavedEntry = true as boolean)
if m.global.session.user.id <> invalid
if m.global.session.user.id <> invalid and deleteSavedEntry = true
unset_user_setting("token")
unset_setting("username")
unset_setting("password")
if deleteSavedEntry = true
'Also delete any credentials in the "saved servers" list
saved = get_setting("saved_servers")
server = m.global.session.server.url
if server <> invalid
server = LCase(server)
savedServers = ParseJson(saved)
newServers = { serverList: [] }
for each item in savedServers.serverList
if item.baseUrl = server
item.username = ""
item.password = ""
end if
newServers.serverList.Push(item)
end for
set_setting("saved_servers", FormatJson(newServers))
end if
end if
unset_user_setting("username")
end if
unset_setting("active_user")
session.user.Logout()

View File

@ -12,7 +12,7 @@ function getDeviceCapabilities() as object
"Photo"
],
"SupportedCommands": [],
"SupportsPersistentIdentifier": false,
"SupportsPersistentIdentifier": true,
"SupportsMediaControl": false,
"SupportsContentUploading": false,
"SupportsSync": false,

View File

@ -137,6 +137,10 @@ namespace session
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\ \-\_]", "")
friendlyName = regex.ReplaceAll(tmpSession.user.name, "")
tmpSession.user.AddReplace("friendlyName", friendlyName)
tmpSession.user.AddReplace("settings", oldUserSettings)
' update global user session
@ -151,9 +155,11 @@ namespace session
if m.global.app.isDev
print "m.global.session.user.settings = ", m.global.session.user.settings
end if
' ensure registry is updated
set_user_setting("username", tmpSession.user.name)
set_user_setting("token", tmpSession.user.authToken)
if m.global.session.user.settings["global.rememberme"]
set_user_setting("token", tmpSession.user.authToken)
set_user_setting("username", tmpSession.user.name)
end if
end sub
' Empty the global user session array and reload defaults
@ -231,6 +237,20 @@ namespace session
end for
end if
end for
' load globals
session.user.settings.LoadGlobals()
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.