Added the earliest possible version of Http-Server support.

This commit is contained in:
Victorious Children Studios 2023-12-16 23:17:56 +02:00
parent 2f6f56a643
commit d766371266
10 changed files with 674 additions and 68 deletions

388
network/http_client.py Normal file
View file

@ -0,0 +1,388 @@
# (c) J.Y.Amihud 2023
# GPL-3 or any later version
import os
import json
import time
import hashlib
import urllib.request
import urllib.parse
import subprocess
# GTK module ( Graphical interface
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gdk
import cairo
# Own modules
from settings import settings
from settings import talk
from project_manager import pm_project
#UI modules
from UI import UI_elements
from UI import UI_color
from studio import story
from studio import analytics
# This is a http client. To work on projects remotely.
def csize(x):
x = float(x)
l = ["B","KB", "MB", "GB", "TB"]
for i in range(5):
if x > 1024:
x = x / 1024
else:
return str(round(x, 2))+" "+l[i]
return str(round(x, 2))+" "+l[i]
def go(win, website, path):
url = website+path
print("url", url)
response = urllib.request.urlopen(url)
data = response.read()
text = data.decode("utf-8")
data = json.loads(text)
return data
def down(win, website, filename, fsize=100000):
# If it is a blend file, we are in trouble.
if filename.endswith(".blend"):
dp = go(win, website, "/blend"+filename)
dplen = len(dp.get("files", {}))
for f in dp.get("files", {}):
try:
hashis = hashlib.md5(open(win.project+f,'rb').read()).hexdigest()
except:
hashis = ""
if hashis != dp.get("files", {}).get(f, {}).get("md5"):
win.current["http-server"]["message"] = f[f.rfind("/")+1:]
filesize = dp.get("files", {}).get(f, {}).get("filesize")
print("trying to get:", f)
pp = down(win, website, f, filesize)
# Making folder for the files
try:
os.makedirs(win.project+filename[:filename.rfind("/")+1])
except:
pass
# downloading the file
url = website+"/download"+filename
response = urllib.request.urlopen(url)
savef = open(win.project+filename, "wb")
sofar = 0
chunk = response.read(8192)
while chunk:
savef.write(chunk)
chunk = response.read(8192)
sofar = sofar + 8192
win.current["http-server"]["message"] = filename[filename.rfind("/")+1:]
win.current["http-server"]["fileprog"] = sofar / fsize
savef.close()
def get_folders(win):
# This function will get specified subfolders
win.current["http-server"]["message"] = "Getting Remote Folders"
folder = win.current["http-server"]["args"]
website = win.analytics["remote-server-url"]
call = win.current["http-server"]["call"]
try:
answer = go(win, website, "/list"+folder)
for n, i in enumerate(answer.get("folders", [])):
win.current["http-server"]["progress"] = n/len(answer.get("folders", []))
win.current["http-server"]["message"] = i
try:
os.makedirs(win.project+folder+"/"+i)
except:
pass
except Exception as e:
error = "Error: "+ str(e)
print(error)
win.current["http-server"]["message"] = error
time.sleep(1)
# Ending the process
win.current["calls"][call]["var"] = False
def get_asset_files(win):
# This function will get specified subfolders
win.current["http-server"]["message"] = "Getting / Updating Files"
folder = win.current["http-server"]["args"]
website = win.analytics["remote-server-url"]
call = win.current["http-server"]["call"]
folder = folder.replace("//", "/")
if folder.endswith("/"):
folder = folder[:-1]
try:
answer = go(win, website, "/list"+folder)
for n, i in enumerate(answer.get("files", [])):
win.current["http-server"]["progress"] = n/len(answer.get("files", []))
win.current["http-server"]["message"] = i
filename = folder+"/"+i
try:
hashis = hashlib.md5(open(win.project+filename,'rb').read()).hexdigest()
except:
hashis = ""
if hashis != answer.get("files", {}).get(i, {}).get("md5"):
filesize = answer.get("files", {}).get(i, {}).get("filesize")
down(win, website, filename, filesize)
win.current["http-server"]["message"] = "Getting Thumbnail"
other_folder = folder+"/renders"
print("otherfolder 1", other_folder)
answer = go(win, website, "/list"+other_folder)
for n, i in enumerate(answer.get("files", [])):
win.current["http-server"]["progress"] = n/len(answer.get("files", []))
win.current["http-server"]["message"] = i
if i.startswith("Preview"):
print("getting", i)
filename = other_folder+"/"+i
try:
hashis = hashlib.md5(open(win.project+filename,'rb').read()).hexdigest()
except:
hashis = ""
if hashis != answer.get("files", {}).get(i, {}).get("md5"):
filesize = answer.get("files", {}).get(i, {}).get("filesize")
down(win, website, filename, filesize)
win.current["http-server"]["message"] = "Getting Asset Blend"
other_folder = folder[:folder.rfind("/")+1].replace("/dev/", "/ast/")
print("otherfolder 2", other_folder, folder)
answer = go(win, website, "/list"+other_folder)
for n, i in enumerate(answer.get("files", [])):
win.current["http-server"]["progress"] = n/len(answer.get("files", []))
win.current["http-server"]["message"] = i
if i.startswith(folder[folder.rfind("/")+1:]):
print("getting", i)
filename = other_folder+"/"+i
try:
hashis = hashlib.md5(open(win.project+filename,'rb').read()).hexdigest()
except:
hashis = ""
if hashis != answer.get("files", {}).get(i, {}).get("md5"):
filesize = answer.get("files", {}).get(i, {}).get("filesize")
down(win, website, filename, filesize)
except Exception as e:
error = "Error: "+ str(e)
print(error)
win.current["http-server"]["message"] = error
time.sleep(1)
# Ending the process
win.current["calls"][call]["var"] = False
def get_files(win):
# This function will get specified subfolders
win.current["http-server"]["message"] = "Getting / Updating Files"
folder = win.current["http-server"]["args"]
website = win.analytics["remote-server-url"]
call = win.current["http-server"]["call"]
try:
answer = go(win, website, "/list"+folder)
for n, i in enumerate(answer.get("files", [])):
win.current["http-server"]["progress"] = n/len(answer.get("files", []))
win.current["http-server"]["message"] = i
filename = folder+"/"+i
try:
hashis = hashlib.md5(open(win.project+filename,'rb').read()).hexdigest()
except:
hashis = ""
if hashis != answer.get("files", {}).get(i, {}).get("md5"):
filesize = answer.get("files", {}).get(i, {}).get("filesize")
down(win, website, filename, filesize)
except Exception as e:
error = "Error: "+ str(e)
print(error)
win.current["http-server"]["message"] = error
time.sleep(1)
# Ending the process
win.current["calls"][call]["var"] = False
def get_essentials(win):
# This function will get essential parts of the project.
# Essentials are: Story, Analitycs, Project.progress and Banner Image.
essentials = [
"/pln/story.vcss",
"/set/analytics.json",
"/set/project.progress",
"/set/banner.png"]
website = win.analytics["remote-server-url"]
call = win.current["http-server"]["call"]
for n, i in enumerate(essentials):
win.current["http-server"]["progress"] = n/len(essentials)
win.current["http-server"]["message"] = i
try:
down(win, website, i)
except Exception as e:
error = "Error: "+ str(e)
print(error)
win.current["http-server"]["message"] = error
time.sleep(1)
win.story = story.load(win.project)
win.analytics = analytics.load(win.project)
win.analytics["remote-server-url"] = website
win.analytics["from-remote-server"] = True
analytics.save(win.project, win.analytics)
# Ending the process
win.current["calls"][call]["var"] = False
def layer(win, call):
# Making the layer
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'],
win.current['h'])
layer = cairo.Context(surface)
#text setting
layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
UI_color.set(layer, win, "dark_overdrop")
layer.rectangle(
0,
0,
win.current["w"],
win.current["h"],
)
layer.fill()
# So it's going to be like a little window in the center of the VCStudio
# with a simple UI. Probably like 2 things. Folder and a projectname.
UI_color.set(layer, win, "node_background")
UI_elements.roundrect(layer, win,
win.current["w"]/2-250,
win.current["h"]/2-60,
500,
150,
10)
# Title of the operation. Incase the user forgot.
UI_elements.text(layer, win, "scan_project_title",
win.current["w"]/2-250,
win.current["h"]/2-35,
500,
30,
10,
fill=False,
centered=True,
editable=False)
win.text["scan_project_title"]["text"] = win.current["http-server"]["message"]
# File Progressbar
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
win.current["w"]/2-200,
win.current["h"]/2+10,
400,
20,
10,
tip="Hello")
UI_color.set(layer, win, "progress_active")
UI_elements.roundrect(layer, win,
win.current["w"]/2-200,
win.current["h"]/2+10,
400*max(0, min(1, win.current["http-server"]["fileprog"])),
20,
10,)
# Progressbar
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
win.current["w"]/2-200,
win.current["h"]/2+40,
400,
20,
10,
tip="Hello")
UI_color.set(layer, win, "progress_active")
UI_elements.roundrect(layer, win,
win.current["w"]/2-200,
win.current["h"]/2+40,
400*max(0, min(1, win.current["http-server"]["progress"])),
20,
10,)
return surface

View file

@ -98,6 +98,7 @@ class handler(BaseHTTPRequestHandler):
# Basic security measure
self.path = self.path.replace("/..", "/")
self.path = self.path.replace("//", "/")
if self.path == "/":
self.start_page(200)
@ -131,9 +132,12 @@ class handler(BaseHTTPRequestHandler):
filename = self.path[6:]
fullfilename = PROJECT+filename
print(fullfilename)
if os.path.exists(fullfilename):
try:
linked = subprocess.check_output([blender, "-b", fullfilename, "-P", os.getcwd()+"/network/get_linked_files.py"]).decode("utf-8")
except:
linked = ""
linked = linked[linked.find("!START_DATA!")+13:linked.rfind("!END_DATA!")]
@ -174,14 +178,14 @@ class handler(BaseHTTPRequestHandler):
else:
self.start_page(404)
self.wfile.write(b"File: "+filename.encode("utf-8")+b" doen't exist")
self.wfile.write(b"File: "+filename.encode("utf-8")+b" doesn't exist on server.")
elif self.path.startswith("/download/"):
filename = self.path[9:]
fullfilename = PROJECT+filename
print(fullfilename)
if os.path.exists(fullfilename):
self.start_page(200)

View file

@ -199,8 +199,8 @@ def pmdrawing(pmdrawing, main_layer, win):
settings.write("check-for-updates", True)
win.settings["check-for-updates"] = True
if win.settings["check-for-updates"]:
GLib.timeout_add(1 , update_reader.get_update_info, win)
#if win.settings["check-for-updates"]:
# GLib.timeout_add(1 , update_reader.get_update_info, win)
# Current frame (for animations and things like this)

52
run.py
View file

@ -48,33 +48,33 @@ if len(sys.argv) > 1:
# Trouble shooter. Why not.
from troubleshooter import troubleshooter
elif command == "-c":
# Console mode. Why not.
from project_manager import pm_console
pm_console.run()
# elif command == "-c":
# # Console mode. Why not.
# from project_manager import pm_console
# pm_console.run()
elif command == "-ms":
# elif command == "-ms":
# This one is a bit harder. Because we need a project name.
if len(sys.argv) > 2:
projectname = sys.argv[2]
else:
projectname = input("Project Name: ")
# # This one is a bit harder. Because we need a project name.
# if len(sys.argv) > 2:
# projectname = sys.argv[2]
# else:
# projectname = input("Project Name: ")
# Then will launch it.
subprocess.Popen(["python3", "network/multiuser_server.py", projectname])
# # Then will launch it.
# subprocess.Popen(["python3", "network/multiuser_server.py", projectname])
elif command == "-rm":
# The read messages terminal app.
from network import read_messages
# elif command == "-rm":
# # The read messages terminal app.
# from network import read_messages
elif command == "-sm":
# The read messages terminal app.
from network import send_messages
# elif command == "-sm":
# # The read messages terminal app.
# from network import send_messages
elif command == "-tc":
# The read messages terminal app.
from network import test_client
# elif command == "-tc":
# # The read messages terminal app.
# from network import test_client
else:
@ -84,12 +84,12 @@ if len(sys.argv) > 1:
print("VCStudio help page. For those of you, nerdy people.")
print()
print(" --help Gives you this help page. I know obvious.")
print(" -c Console mode. Not all functions available.")
# print(" -c Console mode. Not all functions available.")
print(" -t Troubleshooter. Run Troubleshooter.")
print(" -ms Multiuser Server in terminal.")
print(" -tc Test Multiuser Client.")
print(" -rm Test Broadcast Reader.")
print(" -sm Test Broadcast Writer.")
# print(" -ms Multiuser Server in terminal.")
# print(" -tc Test Multiuser Client.")
# print(" -rm Test Broadcast Reader.")
# print(" -sm Test Broadcast Writer.")
print()
print()

View file

@ -277,3 +277,11 @@ copy_checklist_to_clipboard = [Copy to Clipboard]
new_checklist_from_clipboard = [From Clipboard]
copy_to_checklist_from_clipboard = [Paste To The End]
days_a_week = [Days per Week]
back_up_tooltip = [Launch Automatic Backing Up Program.]
Remote_Server = [Remote Server]
Remote_Server_tip = [Work on a project hosted on a remote machine.]
remote-update-initial = [Update Project]
remote-update-initial-tip = [Download initial project files and replace
the ones on the computer with
the downloaded files.]
remote-server-url = [The hostname of the remote server.]

View file

@ -32,6 +32,8 @@ from studio import studio_dialogs
from studio import schedule
from studio import history
from network import http_client
def layer(win):
@ -142,6 +144,23 @@ def layer(win):
def do():
win.current["asset_cur_folder"] = cur
# Why?
if win.current["asset_cur_folder"] == "idea":
fl = "reference"
elif win.current["asset_cur_folder"] == "texture":
fl = "tex"
elif win.current["asset_cur_folder"] == "render":
fl = "renders"
else:
fl = ""
# Remote Server Stuff
if win.analytics["from-remote-server"]:
def after(win, var):
UI_elements.reload_images(win)
studio_dialogs.http_client_dialog(win, "http-client", after, http_client.get_files, "/dev/"+win.cur+"/"+fl)
UI_elements.roundrect(layer, win,
win.current["w"]/4+150+(40*num),
350,
@ -420,6 +439,14 @@ def layer(win):
"/dev"+win.cur+"/",
name+".blend")
if win.current["in-asset-remote-server"]:
# Remote Server Stuff
if win.analytics["from-remote-server"]:
def after(win, var):
UI_elements.reload_images(win)
studio_dialogs.http_client_dialog(win, "http-client", after, http_client.get_asset_files, "/dev/"+win.cur+"/")
win.current["in-asset-remote-server"] = False
if "asset_file_selected" not in win.current:

View file

@ -47,6 +47,8 @@
################################################################################
import os
import time
import threading
# GTK module ( Graphical interface
import gi
@ -77,6 +79,7 @@ from studio import studio_asset_configureLayer # # tual UI of dialo- #
from studio import studio_renderLayer # # gs that I was ta- #
from studio import studio_vseLayer # # lking about at #
from UI import UI_helpDialog # # the top. #
from network import http_client # # #
# # #
################################################### ########################
@ -329,3 +332,36 @@ def help(win, name, call, filename="", force=False, SEARCH=""):
"insert":False, # Whether the insert mode is on
"scroll":"in_help_search_scroll" # If multiline. The pointer for the scroll value.
}
def http_client_dialog(win, name, call, function, args=""):
# This function is going to be the UI for http-client.
if name not in win.current["calls"]:
win.current["calls"][name] = {
"var" :None, # This is the variable that we are waiting for
"call":call, # This is what it's going to run when it's done
"url" :"http-server",
"back":win.url,# This is where it's going to come back when it's done
"draw":http_client.layer
}
# Let's clear the LMB just in case
win.previous["LMB"] = False
# Some variables to pass so the function.
win.current["http-server"] = {
"args" :args, # arguments to the function
"progress":0, # progress of that function
"fileprog":0, # progress for a file download
"call" :name, # The call to stop the process
"message" :"", # message about the progress
"started" :time.time()# current time ( to calculate progress completion )
}
# Let's now run the function
function_run = threading.Thread(target=function, args=(win,))
function_run.setDaemon(True)
function_run.start()

View file

@ -173,6 +173,14 @@ def run(project, win):
win.color = UI_color.get_table()
win.settings = settings.load_all()
# I will initilize the remote server settings here, because I need to make it
# as early as possible. Since I will need it in many places.
if "from-remote-server" not in win.analytics:
win.analytics["from-remote-server"] = False
if "remote-server-url" not in win.analytics:
win.analytics["remote-server-url"] = ""
# Default values
win.current["frame"] = 0
win.current["testing"] = False
@ -189,6 +197,9 @@ def run(project, win):
win.current["script_find"] = [0,0]
win.current["camera_arrived"] = False
# Remote server stuff
win.current["in-asset-remote-server"] = True
if "pointers" not in win.story:
win.story["pointers"] = {} # List of text pointers per scene
@ -224,15 +235,15 @@ def run(project, win):
scroll.add_with_viewport(pmdraw) # what they are doing. Really.
pmdraw.connect("draw", pmdrawing, win)
# Before we run the UI. Let's run the mutliuser client.
multiuser = threading.Thread(target=network_multiuser.client, args=(win,))
multiuser.setDaemon(True)
multiuser.start()
# # Before we run the UI. Let's run the mutliuser client.
# multiuser = threading.Thread(target=network_multiuser.client, args=(win,))
# multiuser.setDaemon(True)
# multiuser.start()
# And let's start also the multiuser_terminal
multiuser_term = threading.Thread(target=multiuser_terminal.listen, args=(win,))
multiuser_term.setDaemon(True)
multiuser_term.start()
# # And let's start also the multiuser_terminal
# multiuser_term = threading.Thread(target=multiuser_terminal.listen, args=(win,))
# multiuser_term.setDaemon(True)
# multiuser_term.start()
#run
win.show_all()
@ -246,6 +257,9 @@ def pmdrawing(pmdrawing, main_layer, win):
# be used in such an application. But to hell with it. I did the same on the
# Blender-Organizer altho with way less cairo. And it works well enought.
try:
@ -275,12 +289,15 @@ def pmdrawing(pmdrawing, main_layer, win):
if win.current["frame"] == 10 and not win.render_runtime.get("to_render"):
win.cur = "/set"
win.url = "analytics"
elif win.current["frame"] == 10:
elif win.current["frame"] == 10 and win.renders:
def after(win, var):
pass
studio_dialogs.render(win, "current_renders", after)
if not win.url == "assets" and not win.url == "http-server":
win.current["in-asset-remote-server"] = True
if not "scale" in win.settings:
settings.write("scale", 1) # Writing to file

View file

@ -16,6 +16,9 @@ import cairo
from settings import settings
from settings import talk
from settings import fileformats
from studio import studio_dialogs
from project_manager import pm_project
#UI modules
@ -26,6 +29,8 @@ from UI import UI_color
from studio import studio_dialogs
from studio import analytics
from network import http_client
def layer(win):
# Making the layer
@ -676,6 +681,98 @@ def layer(win):
current_Y += 50
current_Y += 50
# REMOTE SERVER
rserv_ok = "unchecked"
if win.analytics["from-remote-server"]:
rserv_ok = "checked"
def do():
win.analytics["from-remote-server"] = not win.analytics["from-remote-server"]
UI_elements.roundrect(layer, win,
win.current["w"]/2-240,
110 + current_Y + win.scroll["studio_settings"],
450,
40,
10,
button=do,
icon=rserv_ok,
tip=talk.text("Remote_Server_tip"))
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(win.current["w"]/2-180,
current_Y + win.scroll["studio_settings"] + 135)
layer.show_text(talk.text("Remote_Server"))
current_Y += 50
if win.analytics["from-remote-server"]:
# Remote Server Host Name
UI_elements.image(layer, win, "settings/themes/"\
+win.settings["Theme"]+"/icons/server.png",
win.current["w"]/2-240,
110 + current_Y + win.scroll["studio_settings"],
40,
40)
UI_elements.text(layer, win, "remote-server-url",
win.current["w"]/2-190,
110 + current_Y + win.scroll["studio_settings"],
420,
40,
set_text=win.analytics["remote-server-url"],
tip=talk.text("remote-server-url"))
if win.text["remote-server-url"]["text"] != win.analytics["remote-server-url"]:
def do():
win.analytics["remote-server-url"] = win.text["remote-server-url"]["text"]
UI_elements.roundrect(layer, win,
win.current["w"]/2-240+430,
110 + current_Y + win.scroll["studio_settings"],
40,
40,
10,
button=do,
icon="ok",
tip=talk.text("checked"))
current_Y += 50
if win.analytics["remote-server-url"]:
def do():
def after(win, var):
pass
studio_dialogs.http_client_dialog(win, "http-client", after, http_client.get_essentials)
UI_elements.roundrect(layer, win,
win.current["w"]/2-240,
110 + current_Y + win.scroll["studio_settings"],
450,
40,
10,
button=do,
icon="update",
tip=talk.text("remote-update-initial-tip"))
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(win.current["w"]/2-180,
current_Y + win.scroll["studio_settings"] + 135)
layer.show_text(talk.text("remote-update-initial"))
current_Y += 50
current_Y += 50
UI_elements.scroll_area(layer, win, "studio_settings",
int(win.current["w"]/2-250),
100,

View file

@ -30,6 +30,7 @@ from studio import schedule
from UI import UI_elements
from UI import UI_color
from network import http_client
def layer(win):
@ -575,7 +576,7 @@ def layer(win):
UI_elements.roundrect(layer, win,
5,
win.current["h"]-145,
win.current["h"]-95,
40,
40,
10,
@ -584,20 +585,20 @@ def layer(win):
talk.text("back_up_tooltip"),
url="story_editor")
# Multiuser
def do():
win.url = "multiuser"
# # Multiuser
# def do():
# win.url = "multiuser"
UI_elements.roundrect(layer, win,
5,
win.current["h"]-95,
40,
40,
10,
do,
"multiuser",
talk.text("multiuser_tooltip"),
url="story_editor")
# UI_elements.roundrect(layer, win,
# 5,
# win.current["h"]-95,
# 40,
# 40,
# 10,
# do,
# "multiuser",
# talk.text("multiuser_tooltip"),
# url="story_editor")
if win.multiuser["unread"]:
count = str(win.multiuser["unread"])
@ -652,6 +653,13 @@ def layer(win):
studio_dialogs.asset_select(win, "new_asset_story", select_character, force=True, cur="chr")
# Remote Server Stuff
if win.analytics["from-remote-server"]:
def after(win, var):
pass
studio_dialogs.http_client_dialog(win, "http-client", after, http_client.get_folders, "/dev/chr")
UI_elements.roundrect(layer, win,
win.current["w"]-45,
105,
@ -672,6 +680,13 @@ def layer(win):
def do():
studio_dialogs.asset_select(win, "new_asset_story", select_character, force=True, cur="veh")
# Remote Server Stuff
if win.analytics["from-remote-server"]:
def after(win, var):
pass
studio_dialogs.http_client_dialog(win, "http-client", after, http_client.get_folders, "/dev/veh")
UI_elements.roundrect(layer, win,
win.current["w"]-45,
155,
@ -692,6 +707,13 @@ def layer(win):
def do():
studio_dialogs.asset_select(win, "new_asset_story", select_character, force=True, cur="loc")
# Remote Server Stuff
if win.analytics["from-remote-server"]:
def after(win, var):
pass
studio_dialogs.http_client_dialog(win, "http-client", after, http_client.get_folders, "/dev/loc")
UI_elements.roundrect(layer, win,
win.current["w"]-45,
205,
@ -712,6 +734,13 @@ def layer(win):
def do():
studio_dialogs.asset_select(win, "new_asset_story", select_character, force=True, cur="obj")
# Remote Server Stuff
if win.analytics["from-remote-server"]:
def after(win, var):
pass
studio_dialogs.http_client_dialog(win, "http-client", after, http_client.get_folders, "/dev/obj")
UI_elements.roundrect(layer, win,
win.current["w"]-45,
255,