jf-roku/source/ShowScenes.brs
cthelight 39dad7666c Subtitles: Auto-select default option
On other players (web/andriod app) the user perferences for
subtitle behavior are taken into account, and used to make an
assumption about subtitle behavior.

This patch ports most of that logic here. "Smart" selection is not
yet fully-featured, as it requires additional knowledge about audio
language preferences. Rather it uses the fallback mechanism, which
emulates the "Default" subtitle option.

The logic for the different options was based on the main jellyfin
repo (specifically sha 49d5fdb33fc9792147c1df03e1d1b051e6b7ec79 in
file Emby.Server.Implementations/Library/MediaStreamSelector.cs)

Additionally, this implementation specifically prefers text-based
subtitles (assuming they match the user's preference) as they are
the only ones natively supported by Roku.

Also, the subtitle changing mechanism is reworked slightly to make
use of the new implementation herein
2022-05-22 17:08:54 -05:00

427 lines
14 KiB
Plaintext

function CreateServerGroup()
screen = CreateObject("roSGNode", "SetServerScreen")
screen.optionsAvailable = true
m.global.sceneManager.callFunc("pushScene", screen)
port = CreateObject("roMessagePort")
m.colors = {}
if get_setting("server") <> invalid
screen.serverUrl = get_setting("server")
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"
serverUrl = standardize_jellyfin_url(screen.serverUrl)
'If this is a different server from what we know, reset username/password setting
if get_setting("server") <> serverUrl
set_setting("username", "")
set_setting("password", "")
end if
set_setting("server", serverUrl)
' Show Connecting to Server spinner
dialog = createObject("roSGNode", "ProgressDialog")
dialog.title = tr("Connecting to Server")
m.scene.dialog = dialog
serverInfoResult = ServerInfo()
dialog.close = true
if 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 serverInfoResult.Error <> invalid and serverInfoResult.Error
' If server redirected received, update the URL
if serverInfoResult.UpdatedUrl <> invalid
serverUrl = serverInfoResult.UpdatedUrl
set_setting("server", serverUrl)
end if
' Display Error Message to user
message = tr("Error: ")
if serverInfoResult.ErrorCode <> invalid
message = message + "[" + serverInfoResult.ErrorCode.toStr() + "] "
end if
screen.errorMessage = message + tr(serverInfoResult.ErrorMessage)
SignOut(false)
else
screen.visible = false
if serverInfoResult.serverName <> invalid
return serverInfoResult.ServerName + " (Saved)"
else
return "Saved"
end if
end if
else if node = "delete_saved"
serverPicker = screen.findNode("serverPicker")
itemToDelete = serverPicker.content.getChild(serverPicker.itemFocused)
urlToDelete = itemToDelete.baseUrl
if urlToDelete <> invalid
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", "ConfigScene")
m.global.sceneManager.callFunc("pushScene", group)
port = CreateObject("roMessagePort")
group.findNode("prompt").text = tr("Sign In")
'Load in any saved server data and see if we can just log them in...
server = get_setting("server")
if server <> invalid
server = LCase(server)'Saved server data is always lowercase
end if
saved = get_setting("saved_servers")
if saved <> invalid
savedServers = ParseJson(saved)
for each item in savedServers.serverList
if item.baseUrl = server and item.username <> invalid and item.password <> invalid
get_token(item.username, item.password)
if get_setting("active_user") <> invalid
return "true"
end if
end if
end for
end if
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"
if get_setting("password") <> invalid
password_field.value = get_setting("password")
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]
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"
' Validate credentials
get_token(username.value, password.value)
if get_setting("active_user") <> invalid
set_setting("username", username.value)
set_setting("password", password.value)
if checkbox.checkedState[0] = true
'Update our saved server list, so next time the user can just click and go
UpdateSavedServerList()
end if
return "true"
end if
print "Login attempt failed..."
group.findNode("alert").text = tr("Login attempt failed.")
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 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 = get_setting("active_user")
new_options.push(user_node)
sidepanel.options = new_options
return group
end function
function CreateMovieDetailsGroup(movie)
group = CreateObject("roSGNode", "MovieDetails")
group.overhangTitle = movie.title
group.optionsAvailable = false
m.global.sceneManager.callFunc("pushScene", group)
movie = ItemMetaData(movie.id)
group.itemContent = movie
buttons = group.findNode("buttons")
for each b in buttons.getChildren(-1, 0)
b.observeField("buttonSelected", m.port)
end for
extras = group.findNode("extrasGrid")
extras.observeField("selectedItem", m.port)
extras.callFunc("loadPeople", movie.json)
return group
end function
function CreateSeriesDetailsGroup(series)
group = CreateObject("roSGNode", "TVShowDetails")
group.optionsAvailable = false
m.global.sceneManager.callFunc("pushScene", group)
group.itemContent = ItemMetaData(series.id)
group.seasonData = TVSeasons(series.id)
group.observeField("seasonSelected", m.port)
extras = group.findNode("extrasGrid")
extras.observeField("selectedItem", m.port)
extras.callFunc("loadPeople", group.itemcontent.json)
return group
end function
function CreateSeasonDetailsGroup(series, season)
group = CreateObject("roSGNode", "TVEpisodes")
group.optionsAvailable = false
m.global.sceneManager.callFunc("pushScene", group)
group.seasonData = ItemMetaData(season.id).json
group.objects = TVEpisodes(series.id, season.id)
group.observeField("episodeSelected", m.port)
group.observeField("quickPlayNode", m.port)
return group
end function
function CreateItemGrid(libraryItem)
group = CreateObject("roSGNode", "ItemGrid")
group.parentItem = libraryItem
group.optionsAvailable = true
group.observeField("selectedItem", m.port)
return group
end function
function CreateSearchPage()
' Search + Results Page
group = CreateObject("roSGNode", "SearchResults")
search = group.findNode("SearchBox")
search.observeField("search_value", m.port)
options = group.findNode("SearchSelect")
options.observeField("itemSelected", m.port)
return group
end function
sub CreateSidePanel(buttons, options)
group = CreateObject("roSGNode", "OptionsSlider")
group.buttons = buttons
group.options = options
end sub
function CreateVideoPlayerGroup(video_id, mediaSourceId = invalid, audio_stream_idx = 1)
' Video is Playing
video = VideoPlayer(video_id, mediaSourceId, audio_stream_idx, defaultSubtitleTrackFromVid(video_id))
if video = invalid then return invalid
video.observeField("selectSubtitlePressed", m.port)
video.observeField("state", m.port)
return video
end function
function CreatePersonView(personData as object) as object
person = CreateObject("roSGNode", "PersonDetails")
m.global.SceneManager.callFunc("pushScene", person)
info = ItemMetaData(personData.id)
person.itemContent = info
person.setFocus(true)
person.observeField("selectedItem", m.port)
person.findNode("favorite-button").observeField("buttonSelected", m.port)
return person
end function
function CreatePhotoPage(photo)
group = CreateObject("roSGNode", "PhotoDetails")
group.optionsAvailable = true
m.global.sceneManager.callFunc("pushScene", group)
group.itemContent = photo
return group
end function
sub UpdateSavedServerList()
server = get_setting("server")
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 saved <> invalid
savedServers = ParseJson(saved)
if savedServers.serverList <> invalid 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