Skip to content

Commit

Permalink
Added a way to load local files using an URL parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
0hDEADBEAF committed Oct 25, 2024
1 parent 8dccbd8 commit 160fbb5
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 9 deletions.
5 changes: 4 additions & 1 deletion source/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ def main():
parser.add_argument('--verbosity',
metavar='LEVEL', help='output verbosity (quiet, default, debug)',
choices=[ 'quiet', 'default', 'debug', '0', '1', '2' ], default='default')
parser.add_argument('--allow-local-files',
help='Allows serving local files', action='store_true')
parser.add_argument('--version', help="print version", action='store_true')
args = parser.parse_args()
if args.file and not os.path.exists(args.file):
Expand All @@ -33,7 +35,8 @@ def main():
print(__version__)
sys.exit(0)
address = (args.host, args.port) if args.host else args.port if args.port else None
start(args.file, address=address, browse=args.browse, verbosity=args.verbosity)
start(args.file, address=address, browse=args.browse,
verbosity=args.verbosity, allow_local_files=args.allow_local_files)
wait()
sys.exit(0)

Expand Down
33 changes: 33 additions & 0 deletions source/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ host.BrowserHost = class {
}
}
}
const file_path = params.get("file_path");
if (file_path) {
this._openModelFromFile(file_path);
return;
}
const gist = params.get('gist');
if (gist) {
this._openGist(gist);
Expand Down Expand Up @@ -414,6 +419,34 @@ host.BrowserHost = class {
return `${location.protocol}//${location.host}${pathname}${file}`;
}

async _openModelFromFile(file_path) {
this._view.show('welcome spinner');
let final_buffer = new Uint8Array();
await fetch(`/load_file/${file_path}`, {
method: 'GET'
}).then((response) => {
const reader = response.body.getReader();
reader.read().then(function pump({ done, value }) {
if (done) {
return final_buffer;
}
const old_buffer = final_buffer;
final_buffer = new Uint8Array(old_buffer.length + value.length);
final_buffer.set(old_buffer);
final_buffer.set(value, old_buffer.length);
return reader.read().then(pump);
}).then((buffer) => {
if (response.status === 200) {
const stream = new base.BinaryStream(buffer);
const context = new host.BrowserHost.Context(this, '', file_path, null, stream);
this._openContext(context);
} else {
this._view.error(new Error(String.fromCharCode.apply(null, buffer), 'Model load request failed.'));
}
});
});
}

async _openModel(url, identifier, name) {
url = url.startsWith('data:') ? url : `${url + ((/\?/).test(url) ? '&' : '?')}cb=${(new Date()).getTime()}`;
this._view.show('welcome spinner');
Expand Down
34 changes: 26 additions & 8 deletions source/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,21 @@ def do_GET(self): # pylint: disable=invalid-name
status_code = 404
content = None
content_type = None
if path.startswith('/data/'):
if path.startswith('/load_file/'):
if self.allow_local_files:
file_path = urllib.parse.unquote(path[len('/load_file/'):])
try:
with open(file_path, 'rb') as file:
content = file.read()
content_type = 'application/octet-stream'
status_code = 200
except:
status_code = 404
content = f'Could not load file {file_path}.'.encode('utf-8')
else:
status_code = 403
content = 'Serving local files is not allowed.'.encode('utf-8')
elif path.startswith('/data/'):
path = urllib.parse.unquote(path[len('/data/'):])
content = self.content.read(path)
if content:
Expand Down Expand Up @@ -117,16 +131,16 @@ def _write(self, status_code, content_type, content):
self.send_header('Content-Length', len(content))
self.end_headers()
if self.command != 'HEAD':
if status_code == 404 and content is None:
if status_code != 200 and content is None:
self.wfile.write(str(status_code).encode('utf-8'))
elif (status_code in (200, 404)) and content is not None:
elif (status_code in (200, 403, 404)) and content is not None:
self.wfile.write(content)

class _ThreadedHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
pass

class _HTTPServerThread(threading.Thread):
def __init__(self, content, address, verbosity):
def __init__(self, content, address, verbosity, allow_local_files):
threading.Thread.__init__(self)
self.verbosity = verbosity
self.address = address
Expand All @@ -136,6 +150,7 @@ def __init__(self, content, address, verbosity):
self.server.block_on_close = False
self.server.RequestHandlerClass.content = content
self.server.RequestHandlerClass.verbosity = verbosity
self.server.RequestHandlerClass.allow_local_files = allow_local_files
self.terminate_event = threading.Event()
self.terminate_event.set()
self.stop_event = threading.Event()
Expand Down Expand Up @@ -267,7 +282,7 @@ def wait():
_log(True, '\n')
stop()

def serve(file, data=None, address=None, browse=False, verbosity=1):
def serve(file, data=None, address=None, browse=False, verbosity=1, allow_local_files=False):
'''Start serving model from file or data buffer at address and open in web browser.
Args:
Expand All @@ -276,6 +291,8 @@ def serve(file, data=None, address=None, browse=False, verbosity=1):
address (tuple, optional): A (host, port) tuple, or a port number.
browse (bool, optional): Launch web browser. Default: True
log (bool, optional): Log details to console. Default: False
allow_local_files(bool, optional): Allow the server to read local files. Default: False
Returns:
A (host, port) address tuple.
Expand All @@ -300,7 +317,7 @@ def serve(file, data=None, address=None, browse=False, verbosity=1):
else:
address = _make_port(address)

thread = _HTTPServerThread(content, address, verbosity)
thread = _HTTPServerThread(content, address, verbosity, allow_local_files)
thread.start()
while not thread.alive():
time.sleep(0.01)
Expand All @@ -311,19 +328,20 @@ def serve(file, data=None, address=None, browse=False, verbosity=1):

return address

def start(file=None, address=None, browse=True, verbosity=1):
def start(file=None, address=None, browse=True, verbosity=1, allow_local_files=False):
'''Start serving model file at address and open in web browser.
Args:
file (string): Model file to serve.
log (bool, optional): Log details to console. Default: False
browse (bool, optional): Launch web browser, Default: True
address (tuple, optional): A (host, port) tuple, or a port number.
allow_local_files(bool, optional): Allow the server to read local files. Default: False
Returns:
A (host, port) address tuple.
'''
return serve(file, None, browse=browse, address=address, verbosity=verbosity)
return serve(file, None, browse=browse, address=address, verbosity=verbosity, allow_local_files=allow_local_files)

def widget(address, height=800):
''' Open address as Jupyter Notebook IFrame.
Expand Down

0 comments on commit 160fbb5

Please sign in to comment.