Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infer server url on server select screen #1357

Merged
merged 27 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a93b175
proof of concept - infer server url proto and port
sevenrats Sep 7, 2023
0d4ed65
dont fail if server not found
sevenrats Sep 7, 2023
0b7b2d5
dont fail if server not found
sevenrats Sep 7, 2023
93d683a
address comments, update debug statements
sevenrats Sep 7, 2023
01bfb31
check for and remove trailing /
sevenrats Sep 10, 2023
481284a
infer url deterministically
sevenrats Sep 10, 2023
dfb4dcb
make we found jellyfin and not emby
sevenrats Sep 10, 2023
0f41e65
document new function
sevenrats Sep 10, 2023
f29cddf
serverinfo is a string not an object
sevenrats Sep 10, 2023
bffaf06
try to find string strip bug
sevenrats Sep 10, 2023
6b714a8
trim the input not the path
sevenrats Sep 10, 2023
c0974db
restore new function doc
sevenrats Sep 10, 2023
f75c992
don't write ports if we dont need to
sevenrats Sep 11, 2023
ec7ad49
remove untrue comment
sevenrats Sep 11, 2023
30bd09e
address comment
sevenrats Sep 11, 2023
14899f6
address comments
sevenrats Sep 11, 2023
d7c756e
Merge branch 'unstable' into server_url
cewert Sep 11, 2023
fbfe130
clean up debug prints
sevenrats Sep 11, 2023
02b4c9f
fix non descriptive argument
sevenrats Sep 13, 2023
babc668
stop snake casing
sevenrats Sep 13, 2023
6b16ed9
function definition
sevenrats Sep 14, 2023
d4483a2
Update source/utils/misc.brs
sevenrats Sep 14, 2023
8f8650f
Merge branch 'unstable' into server_url
sevenrats Oct 15, 2023
1e03533
dont crash on bad input
sevenrats Oct 15, 2023
c268a53
Merge branch 'server_url' into unstable
sevenrats Oct 31, 2023
7e680a9
Merge pull request #12 from sevenrats/unstable
sevenrats Oct 31, 2023
a3af52c
apply suggestions
sevenrats Oct 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions source/ShowScenes.brs
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,7 @@ function CreateServerGroup()
dialog = createObject("roSGNode", "ProgressDialog")
dialog.title = tr("Connecting to Server")
m.scene.dialog = dialog

serverUrl = standardize_jellyfin_url(screen.serverUrl)
serverUrl = inferServerUrl(screen.serverUrl)
sevenrats marked this conversation as resolved.
Show resolved Hide resolved
'If this is a different server from what we know, reset username/password setting
if m.global.session.server.url <> serverUrl
set_setting("username", "")
Expand Down
119 changes: 97 additions & 22 deletions source/utils/misc.brs
Original file line number Diff line number Diff line change
Expand Up @@ -162,31 +162,95 @@ function option_dialog(options, message = "", defaultSelection = 0) as integer
return show_dialog(message, options, defaultSelection)
end function

'
' Take a jellyfin hostname and ensure it's a full url.
' prepend http or https and append default ports, and remove excess slashes
'
function standardize_jellyfin_url(url as string)
'Append default ports
maxSlashes = 0
if left(url, 8) = "https://" or left(url, 7) = "http://"
maxSlashes = 2
end if
'Check to make sure entry has no extra slashes before adding default ports.
if Instr(0, url, "/") = maxSlashes
if url.len() > 5 and mid(url, url.len() - 4, 1) <> ":" and mid(url, url.len() - 5, 1) <> ":"
if left(url, 5) = "https"
url = url + ":8920"
else
url = url + ":8096"
' take an incomplete url string and use it to make educated guesses about
' the complete url. then tests these guesses to see if it can find a jf server
' returns the url of the server it found, or an empty string
function inferServerUrl(url as string) as string
port = CreateObject("roMessagePort")
hosts = CreateObject("roAssociativeArray")
reqs = []
candidates = urlCandidates(url)
for each endpoint in candidates
req = CreateObject("roUrlTransfer")
reqs.push(req) ' keep in scope outside of loop, else -10001
req.seturl(endpoint + "/system/info/public")
req.setMessagePort(port)
hosts.addreplace(req.getidentity().ToStr(), endpoint)
if endpoint.Left(8) = "https://"
req.setCertificatesFile("common:/certs/ca-bundle.crt")
end if
req.AsyncGetToString()
end for
handled = 0
timeout = CreateObject("roTimespan")
while timeout.totalseconds() < 15
resp = wait(0, port)
if type(resp) = "roUrlEvent"
' TODO
' if response code is a 300 redirect then we should return the redirect url
' Make sure this happens or make it happen
if resp.GetResponseCode() = 200
selectedUrl = hosts.lookup(resp.GetSourceIdentity().ToStr())
print("CANDIDATE:")
print(selectedUrl)
isJellyfinServer(resp.GetString())
print "Successfully inferred server URL: " selectedUrl
sevenrats marked this conversation as resolved.
Show resolved Hide resolved
return selectedUrl
end if
end if
handled += 1
if handled = reqs.count()
print "inferServerUrl in utils/misc.brs failed to find a server from the string " url " but did not timeout."
return ""
end if
end while
print "inferServerUrl in utils/misc.brs failed to find a server from the string " url " because it timed out."
return ""
end function

function urlCandidates(input as string)
sevenrats marked this conversation as resolved.
Show resolved Hide resolved
if input.endswith("/") then input = input.Left(len(input) - 1)
url = parseUrl(input)
if url[1] = invalid
' a proto wasn't declared
url = parseUrl("none://" + input)
end if
'Append http:// to server
if left(url, 4) <> "http"
url = "http://" + url
proto = url[1]
host = url[2]
port = url[3]
path = url[4]
print "THE PARTS"
print proto " " host " " port " " path
print "THE PATH IS: " path
print "The type of path is: " Type(path)
print "THE PATH AFTER I MODIFY IT: " path
protoCandidates = []
supportedProtos = ["http:", "https:"] ' appending colons because the regex does
if proto = "none:" ' the user did not declare a protocol
' try every supported proto
for each supportedProto in supportedProtos
protoCandidates.push(supportedProto + "//" + host)
sevenrats marked this conversation as resolved.
Show resolved Hide resolved
end for
else
protoCandidates.push(proto + "//" + host) ' but still allow arbitrary protocols if they are declared
end if
return url
final_candidates = []
sevenrats marked this conversation as resolved.
Show resolved Hide resolved
if isValid(port) and port <> "" ' if the port is defined just use that
for each candidate in protoCandidates
final_candidates.push(candidate + port + path)
end for
else ' the port wasnt declared so use default jellyfin and proto ports
for each candidate in protoCandidates:
if candidate.startswith("https")
final_candidates.push(candidate + ":443" + path)
final_candidates.push(candidate + ":8920" + path)
else if candidate.startswith("http")
final_candidates.push(candidate + ":80" + path)
final_candidates.push(candidate + ":8096" + path)
end if
end for
end if
return final_candidates
end function

sub setFieldTextValue(field, value)
Expand Down Expand Up @@ -228,7 +292,7 @@ function isValidAndNotEmpty(input as dynamic) as boolean
end if
end function

' Returns an array from a url - [ url, proto, host, port, subdir/params ]
' Returns an array from a url = [ url, proto, host, port, subdir+params ]
' If port or subdir are not found, an empty string will be added to the array
' Proto must be declared or array will be empty
function parseUrl(url as string) as object
Expand Down Expand Up @@ -382,3 +446,14 @@ sub stopLoadingSpinner()
m.scene.dialog.close = true
end if
end sub

function isJellyfinServer(si as object) as boolean
sevenrats marked this conversation as resolved.
Show resolved Hide resolved
sevenrats marked this conversation as resolved.
Show resolved Hide resolved
'v = ParseJson(si).ProductName = "Jellyfin Server"
print "THE SI SENT TO THE ISJELLYFIN CHECK"
print si
d = ParseJson(si)
if isValid(d) and isValid(d.ProductName)
return d.ProductName = "Jellyfin Server"
sevenrats marked this conversation as resolved.
Show resolved Hide resolved
end if
return False
sevenrats marked this conversation as resolved.
Show resolved Hide resolved
end function