Getting base url


#1

Hello all,

I have a situation where I am generating a URL that links to the octoprint webpage. I have the tail end of the url (ex. /downloads/timelapse/687627682.mpg). How can I get the base url of the install? I'm coding in python if that matters.
ex. http:// + BASE URL + /downloads/timelapse/687627682.mpg
I did notice that when Octoprint starts, it spits out this line:
octoprint.server.preemptive_cache - INFO - Preemptively caching / (ui _default) for {'query_string': 'l10n=en', 'path': '/', 'base_url': 'REDACTED'}
with my base url. But I don't know how I can access this data.

Thanks in advance.


#2

The base URL depends on the URL you access OctoPrint through (it can have multiple valid addresses, e.g. a hostname, an ip v4 address, an ip v6 address, some external domain you have forwarded, ...).

When a request comes in to your plugin and you need to return an absolute URL, you can use

import flask
flask.url_for("index", _external=True)

to get the base URL the request was done for.

If however you need to generate an absolute URL outside of an existing request context, things get a bit more tricky - you'll need to somehow determine a valid external address for the instance.

What exactly do you want to do?


#3

The plugin is a plugin that reads commands from a chat like Discord or text messaging, and sends back a message with the response.

When the user enters this command, the plugin should find the download link for the timelapse file and send it back. All I'm trying to do is generate a valid URL like this:
"http://" + BASE_URL_HERE + "/downloads/timelapse/2354876.mpg"

I have the /downloads part, provided by the Octoprint API.

so for example:
http://myoctoprint.com/downloads/timelapse/283576.mpg
or
http://94.58.125.4/downloads/timelapse/283576.mpg

I could always add a settings option to enter the correct base url, but I wanted to see if I could do it using code. If this becomes an overly complex problem, then I may just use the settings option.


#4

The problem is that OctoPrint has no idea what the correct base URL would be in that case - unless it is accessed via a URL (which then by default must be a valid URL to access it) it has nothing to work with. See your example above - how should OctoPrint know to use myoctoprint.com vs 94.58.125.4 (or something like / vs /octoprint/) unless you access it via one of these right in the request context?

If I understand your use case correctly, you don't have a valid request context since the URL needs to be generated at a point where OctoPrint handles some kind of external message via something that isn't HTTP. In that case, a settings entry would probably be indeed the easiest way.


#5

Ah. I get it now. I've implemented the settings option.
Now, I have an error when I try to access the API.

websocket - ERROR
HTTPConnectionPool(host='REDACTED', port=5000): Max retries exceeded with url: /api/files/local/test.gcode (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x6c4e0630>: Failed to establish a new connection: [Errno 111] Connection refused',))

I can access other parts of the API, like the timelapse requests perfectly fine. It's just this request spitting out this error. I thought it might have been time related, so I waited about 12 or so hrs before trying again and I still get this message. I've also tried changing the filename to no avail.

I think it's because I made too many requests in a certain amount of time. When I use a API tester the result comes back just fine. How can I let my Pi make requests again?


#6

That URL is missing the base URL. /api/files/local/... should be http://<host>/api/files/local/.... The way it is now, it can't connect since half of the URL is missing.


#7

I'm getting the response like this:

header = {'X-Api-Key': api_key}
response = requests.get(
                "http://" + str(shared_vars.base_url) + (":%s/api/files/" % port) + params[1] + "/" + params[2],
                headers=header)

params[1] = local
params[2] = test.gcode

I'm using something similar to this to access the timelapse part of the API as well, which works well. I thought the /api/files/local/test.gcode part was just referring to the tail-end of the url...
How can I fix this since it seems like I am already accessing it correctly?


#8

I'd start with checking that shared_vars.base_url is in fact populated. I'm unsure how requests.get would behave if provided with something like http://:5000/api/files/local/some_file.gco (so a missing host). The exception certainly looks like it tried to connect to just /api/files/... and had to (understandably) give up on that. If it was having issues just with the path, you'd rather have seen something like a 404, not an outright connection failure.

Personally I'd also make the full base URL configurable (so e.g. https://octoprint.example.com:444), so you could do something like

response = requests.get("{}/api/files/{}/{}".format(base_url, location, path),
                        headers=headers)

#9

I made the full base url configurable, and that fixed the issue. I think it may have been a wrong port issue...
Thanks for all your help!