Upload gcode from plugin

Hi,

I'm currently working on a plugin that allows the user to select gcode-files stored in a database and print them.
Since I'd like to keep most logic in the backend, my current approach is to provide an endpoint ("UploadGcode") via the BlueprintPlugin, that can be triggered by the frontend and uploads the respective gcode via POSTing to api/files/local.
However, the POST request stays pending and is never finished.
I assume this is because flask is run in single thread mode and I'm unaware of how or where to change this to multithreading (searching for app.run() that would accept a threading parameter was unsuccessful).

Could someone provide some insights into how to configure it for multithreading, or another workaround?
Is it possible/good practice to directly call the methods executed by POSTing to api/files/local? And if so where can they be found?

As an alternative, I could return the gcode to the FE and then call api/files/local from there, but this seems like a suboptimal solution since it causes a lot of unneccessary traffic.

Thanks in advance!

Short update: I figured out where the files endpoint is located (src/octoprint/server/api/files) but failed in creating a flask request/werkzeug environ that satisfies the uploadGcodeFile method.
Any input is welcome :wink:

Here is the documentation for the file upload but I assume that you've seen this before.

  • Make sure that the MIME type is multipart/form-data rather than application/json
  • Make sure that the header includes X-Api-Key: YOURAPIKEY
  • Make sure that the body includes the expected boundary markers to separate it since it's "multipart"
  • One of the parts should tell OctoPrint whether or not to also SELECT the file
  • One of the parts should tell OctoPrint whether or not to immediately PRINT the file
  • Each boundary marker should be separated from the content by two hard returns (which often needs to be \r\n rather than just \n but I could be wrong in this case).
  • Above a certain filesize, this might technically also need an indication in the part with the gcode file to indicate the size of this section (application/octet-stream) as seen in the Content-Disposition itself.

Thanks for your reply!
I've seen the documentation and have no problem with the request itself but rather with the fact that it is not possible to send a request from one Octoprint endpoint to another - probably due to flask running in single threaded mode.
I implemented an ugly workaround, launching a thread on plugin initialization that is able to send the request but I'd hardly consider that a propper solution.

I'm not sure if this helps.

But I suppose spinning up another thread would be a way of working around the single-thread limitation.

Do I understand that correctly that you are circumventing the default upload API and kinda want to implement your own? In that case take a look at how the upload API endpoint uses the file manager object that also gets injected into every plugin as self._file_manager.

Going over HTTP for an internal operation is definitely not the best approach.

Since the plugin is local to the OctoPrint instance, wouldn't it be easier to extract the file from the database and drop it into the ~/.octoprint/watched folder and then select the file after?

@OutsourcedGuru
the idea to just drop it in that specific place sounds good, guess I'll do that.

@foosel
kinda. Thanks for the hint, that's definitely a better solution. I wasn't aware of the file manager and that it is already injected!

It might be something like...

self._file_manager.select_file(fileSelected, False, True)