files: Updates to not cache thumbnails locally. Also get notified by inotify

This commit is contained in:
Jordan 2021-05-09 13:57:18 -04:00
parent f8ca339de5
commit bd1650fd18
6 changed files with 124 additions and 34 deletions

View File

@ -3,7 +3,7 @@ import gi
import logging
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, Pango
from gi.repository import Gtk, Gdk, GdkPixbuf, Gio, GLib, Pango
import os
klipperscreendir = os.getcwd()
@ -13,7 +13,8 @@ class KlippyGtk:
width_ratio = 16
height_ratio = 9.375
def __init__(self, width, height):
def __init__(self, screen, width, height):
self.screen = screen
self.width = width
self.height = height
@ -98,6 +99,16 @@ class KlippyGtk:
return pixbuf
def PixbufFromHttp(self, resource, style=False, width_scale=1, height_scale=1):
response = self.screen.apiclient.get_thumbnail_stream(resource)
if response == False:
return None
stream = Gio.MemoryInputStream.new_from_data(response, None)
pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(stream, int(round(self.img_width * width_scale)),
int(round(self.img_height * height_scale)), True)
return pixbuf
def ProgressBar(self, style=False):
bar = Gtk.ProgressBar()

View File

@ -20,6 +20,14 @@ class KlippyRest:
def get_printer_info(self):
return self.send_request("printer/info")
def get_thumbnail_stream(self, thumbnail):
url = "http://%s:%s/server/files/gcodes/%s" % (self.ip, self.port, thumbnail)
response = requests.get(url, stream=True)
if response.status_code == 200:
response.raw.decode_content = True
return response.content
return False
def send_request(self, method):
url = "http://%s:%s/%s" % (self.ip, self.port, method)
logging.debug("Sending request to %s" % url)

View File

@ -22,6 +22,16 @@ class KlippyFiles():
if not os.path.exists(self.thumbnail_dir):
os.makedirs(self.thumbnail_dir)
self.gcodes_path = None
def initialize(self):
self.gcodes_path = None
if "virtual_sdcard" in self._screen.printer.get_config_section_list():
vsd = self._screen.printer.get_config_section("virtual_sdcard")
if "path" in vsd:
self.gcodes_path = vsd['path']
logging.info("Gcodes path: %s" % self.gcodes_path)
def _callback(self, result, method, params):
if method == "server.files.list":
if "result" in result and isinstance(result['result'],list):
@ -32,44 +42,57 @@ class KlippyFiles():
deletedfiles.remove(item['filename'])
else:
newfiles.append(item['filename'])
#logging.debug("New file: %s", item['filename'])
self.filelist.append(item['filename'])
self.files[item['filename']] = {
"size": item['size'],
"modified": item['modified']
}
self.request_metadata(item['filename'])
self.add_file(item, False)
if len(self.callbacks) > 0 and (len(newfiles) > 0 or len(deletedfiles) > 0):
for cb in self.callbacks:
cb(newfiles, deletedfiles, [])
if len(newfiles) > 0 or len(deletedfiles) > 0:
self.run_callbacks(newfiles, deletedfiles)
if len(deletedfiles) > 0:
#logging.debug("Deleted files: %s", deletedfiles)
for file in deletedfiles:
self.filelist.remove(file)
self.files.pop(file, None)
self.remove_file(file)
elif method == "server.files.metadata":
if "error" in result.keys():
logging.debug("Error in getting metadata for %s. Retrying in 6 seconds" %(params['filename']))
return
#logging.debug("Got metadata for %s" % (result['result']['filename']))
for x in result['result']:
self.files[params['filename']][x] = result['result'][x]
if "thumbnails" in self.files[params['filename']]:
self.files[params['filename']]['thumbnails'].sort(key=lambda x: x['size'], reverse=True)
for thumbnail in self.files[params['filename']]['thumbnails']:
f = open("%s/%s-%s" % (self.thumbnail_dir, params['filename'].split('/')[-1], thumbnail['size']),
"wb")
f.write(base64.b64decode(thumbnail['data']))
f.close()
for cb in self.callbacks:
logging.debug("Running metadata callbacks")
cb([], [], [params['filename']])
thumbnail['local'] = False
if self.gcodes_path != None:
fpath = os.path.join(self.gcodes_path, params['filename'])
fdir = os.path.dirname(fpath)
path = os.path.join(fdir, thumbnail['relative_path'])
if os.access(path, os.R_OK):
thumbnail['local'] = True
thumbnail['path'] = path
if thumbnail['local'] == False:
fdir = os.path.dirname(params['filename'])
thumbnail['path'] = os.path.join(fdir, thumbnail['relative_path'])
self.run_callbacks(mods=[params['filename']])
def add_file(self, item, notify=True):
if 'filename' not in item and 'path' not in item:
logging.info("Error adding item, unknown filename or path: %s" % item)
return
filename = item['path'] if "path" in item else item['filename']
if filename in self.filelist:
logging.info("File already exists: %s" % filename)
return
self.filelist.append(filename)
self.files[filename] = {
"size": item['size'],
"modified": item['modified']
}
self.request_metadata(filename)
if notify == True:
self.run_callbacks(newfiles=[filename])
def add_file_callback(self, callback):
try:
@ -77,6 +100,19 @@ class KlippyFiles():
except:
logging.debug("Callback not found: %s" % callback)
def process_update(self, data):
if 'item' in data and data['item']['root'] != 'gcodes':
return
if data['action'] == "create_file":
self.add_file(data['item'])
elif data['action'] == "delete_file":
self.remove_file(data['item']['path'])
elif data['action'] == "move_file":
self.add_file(data['item'], False)
self.remove_file(data['source_item']['path'], False)
self.run_callbacks(newfiles=[data['item']['path']], deletedfiles=[data['source_item']['path']])
def remove_file_callback(self, callback):
if callback in self.callbacks:
self.callbacks.pop(self.callbacks.index(callback))
@ -94,7 +130,11 @@ class KlippyFiles():
def get_thumbnail_location(self, filename):
if not self.has_thumbnail(filename):
return None
return "%s/%s-%s" % (self.thumbnail_dir, filename.split('/')[-1], self.files[filename]['thumbnails'][0]['size'])
thumb = self.files[filename]['thumbnails'][0]
if thumb['local'] == False:
return ['http', thumb['path']]
return ['file', thumb['path']]
def has_thumbnail(self, filename):
if filename not in self.files:
@ -109,18 +149,32 @@ class KlippyFiles():
def refresh_files(self):
self._screen._ws.klippy.get_file_list(self._callback)
def remove_file(self, filename, notify=True):
if filename not in self.filelist:
return
self.filelist.remove(filename)
self.files.pop(filename, None)
if notify == True:
self.run_callbacks(deletedfiles=[filename])
def ret_file_data (self, filename):
print("Getting file info for %s" % (filename))
self._screen._ws.klippy.get_file_metadata(filename, self._callback)
def run_callbacks(self, newfiles=[], deletedfiles=[], mods=[]):
if len(self.callbacks) <= 0:
return
for cb in self.callbacks:
GLib.idle_add(cb, newfiles, deletedfiles, mods)
def get_file_list(self):
return self.filelist
def get_file_info(self, filename):
if filename not in self.files:
return None
return {"path":None,"modified":0,"size":0}
return self.files[filename]
def add_file(self, file_name, size, modified, old_file_name = None):
print(file_name)

View File

@ -100,7 +100,15 @@ class ScreenPanel:
def get_file_image(self, filename, width=1.6, height=1.6):
if not self._files.has_thumbnail(filename):
return None
return self._gtk.PixbufFromFile(self._files.get_thumbnail_location(filename), None, width, height)
loc = self._files.get_thumbnail_location(filename)
if loc == None:
return None
if loc[0] == "file":
return self._gtk.PixbufFromFile(loc[1], None, width, height)
if loc[0] == "http":
return self._gtk.PixbufFromHttp(loc[1], None, width, height)
return None
def home(self, widget):
self._screen._ws.klippy.gcode_script(KlippyGcodes.HOME)

View File

@ -394,8 +394,9 @@ class PrintPanel(ScreenPanel):
for child in self.dir_panels[dirpan].get_children():
self.dir_panels[dirpan].remove(child)
for file in self._screen.files.get_file_list():
self.add_file(file)
flist = sorted(self._screen.files.get_file_list(), key=lambda item: '/' in item)
for file in flist:
GLib.idle_add(self.add_file, file)
def update_file(self, filename):
if filename not in self.labels['files']:

View File

@ -109,7 +109,7 @@ class KlipperScreen(Gtk.Window):
self.set_resizable(False)
logging.info("Screen resolution: %sx%s" % (self.width, self.height))
self.gtk = KlippyGtk(self.width, self.height)
self.gtk = KlippyGtk(self, self.width, self.height)
self.init_style()
self.printer_initializing(_("Initializing"))
@ -586,7 +586,8 @@ class KlipperScreen(Gtk.Window):
self.printer.process_update(data)
elif action == "notify_filelist_changed":
logging.debug("Filelist changed: %s", json.dumps(data,indent=2))
#self.files.add_file()
if self.files != None:
self.files.process_update(data)
elif action == "notify_metadata_update":
self.files.request_metadata(data['filename'])
elif action == "notify_power_changed":
@ -652,7 +653,13 @@ class KlipperScreen(Gtk.Window):
_ = self.lang.gettext
printer_info = self.apiclient.get_printer_info()
if printer_info == False:
logging.info("Unable to get printer info from moonraker")
return False
data = self.apiclient.send_request("printer/objects/query?" + "&".join(PRINTER_BASE_STATUS_OBJECTS))
if data == False:
logging.info("Error getting printer object data")
return False
powerdevs = self.apiclient.send_request("machine/device_power/devices")
data = data['result']['status']
@ -660,6 +667,7 @@ class KlipperScreen(Gtk.Window):
self.printer.reinit(printer_info['result'], data)
self.ws_subscribe()
self.files.initialize()
self.files.refresh_files()
if powerdevs != False: