123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876 |
- # (c) J.Y.Amihud 2023
- # GPL-3 or any later version
- import os
- import json
- import zlib
- import time
- import fnmatch
- import threading
- 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 fileformats
- from settings import talk
- from project_manager import pm_project
- #UI modules
- from UI import UI_elements
- from UI import UI_color
- from UI import UI_math
- 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_z"+filename
- response = urllib.request.urlopen(url)
- for line in str(response.info()).split("\n"):
- if line.startswith("Content-length: "):
- try:
- fsize = int(line[line.find(" ")+1:])
- except:
- pass
-
- savef = open(win.project+filename, "wb")
- zd = b""
- sofar = 0
- chunk = response.read(8192)
-
- while chunk:
- zd = zd + chunk
- chunk = response.read(8192)
- sofar = sofar + 8192
- win.current["http-server"]["message"] = filename[filename.rfind("/")+1:]
- win.current["http-server"]["fileprog"] = sofar / fsize
- unz = zlib.decompress(zd)
-
- savef.write(unz)
- savef.close()
- def download_missing_changed(win, cur, call):
- # This function downloads files from remote server using the
- # remote-folder-data data.
- website = win.analytics["remote-server-url"]
- for t in ["missing", "changed"]:
- while win.current["remote-folder-data"][cur][t]: # IDK WHAT I'M DOING!!!
- try:
- for f in win.current["remote-folder-data"][cur][t]:
- fdata = win.current["remote-folder-data"][cur][t][f]
- if fdata.get("to_download", True):
- print("Downloading", f)
- try:
- down(win, website, f, fdata.get("filesize", 10000))
- except Exception as e:
- print(e)
- del win.current["remote-folder-data"][cur][t][f]
- except:
- pass
- win.current["remote-folder-data"][cur]["downloading"] = False
- win.current["calls"][call]["var"] = False
-
- def get_folder_info(win, folders, cur):
- # This function gets an information of what files should be changed. And outputs
- # the found data into the the global win thing, so it could accessed later.
- # Making sure we have the infrastructure.
- print("Getting Update Info About Folders")
- website = win.analytics["remote-server-url"]
-
-
- if "remote-folder-data" not in win.current:
- win.current["remote-folder-data"] = {}
- if cur not in win.current["remote-folder-data"]:
- win.current["remote-folder-data"][cur] = {"missing":{},
- "changed":{}}
- win.current["remote-folder-data"][cur]["loaded"] = False
- def sort_file(filename, files, f, filedata=None):
- if not filedata:
- filedata = files.get(f, {})
- filedata["finished"] = False
- try:
- hashis = hashlib.md5(open(win.project+filename,'rb').read()).hexdigest()
- except:
- hashis = ""
- if not os.path.exists(win.project+filename):
- win.current["remote-folder-data"][cur]["missing"][filename] = filedata
- elif hashis != filedata.get("md5"):
- filedata["to_download"] = True
- win.current["remote-folder-data"][cur]["changed"][filename] = filedata
-
-
- # Part one: Getting all the data about the files.
-
- for foldername in folders:
- try:
- answer = go(win, website, "/list"+foldername)
- files = list(answer.get("files", {}).keys())
- for f in fnmatch.filter(files, folders.get(foldername, "*")):
- # Now we need to figure out whether we have the file
- # , whether we don't even have it ,
- # or whether we have a different version.
- fullfilename = foldername+"/"+f
- sort_file(fullfilename, answer.get("files", {}), f)
-
- except Exception as e:
- error = "Error: "+ str(e)
- print(error)
- # Part two: Unwrapping all the blend dependancies data.
- # Here we are going to store blend file that were checked already.
- win.current["remote-folder-data"][cur]["blend_checked"] = []
- # A function checking if we still have some unchecked .blends
- def not_checked():
- for t in ["missing", "changed"]:
- for i in win.current["remote-folder-data"][cur][t]:
- if i not in win.current["remote-folder-data"][cur]["blend_checked"] and i.endswith(".blend"):
- return i
- return False
- nc = not_checked()
-
- while nc:
- try:
- dp = go(win, website, "/blend"+nc)
-
- for f in dp.get("files", {}):
-
- try:
- sort_file(f,
- dp.get("files", {}),
- f[f.rfind("/")+1:],
- filedata=dp.get("files", {})[f])
- except Exception as e:
- print(e)
- except Exception as e:
- print(e)
-
- win.current["remote-folder-data"][cur]["blend_checked"].append(nc)
-
- nc = not_checked()
- for t in ["missing", "changed"]:
- print(t+":")
- for i in win.current["remote-folder-data"][cur][t]:
- print(" "+i)
- win.current["remote-folder-data"][cur]["loaded"] = True
- 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:
- print("missing", filename)
- 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
- def prompt_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-350,
- 100,
- 700,
- win.current["h"]-200,
- 10)
-
-
- cur = win.current["remote-folder-data"]["prompt"]
- if "downloading" not in win.current["remote-folder-data"][cur]:
- win.current["remote-folder-data"][cur]["downloading"] = False
-
- if not win.current["remote-folder-data"][cur]["downloading"]:
- # Exit button
- def do():
- win.current["calls"][call]["var"] = False
- UI_elements.roundrect(layer, win,
- win.current["w"]/2+310,
- win.current["h"]-140,
- 40,
- 40,
- 10,
- button=do,
- icon="cancel",
- tip=talk.text("cancel"))
- # Download button
- if win.current["remote-folder-data"][cur]["loaded"]:
- def do():
- win.current["remote-folder-data"][cur]["downloading"] = True
- start_downaloding = threading.Thread(target=download_missing_changed,
- args=(win, cur, call, ))
- start_downaloding.setDaemon(True)
- start_downaloding.start()
- UI_elements.roundrect(layer, win,
- win.current["w"]/2+270,
- win.current["h"]-140,
- 40,
- 40,
- 10,
- button=do,
- icon="download",
- tip=talk.text("DownloadRemoteServer"))
- else:
- UI_color.set(layer, win, "text_normal")
- layer.set_font_size(15)
- layer.move_to(win.current["w"]/2,
- win.current["h"]-120,)
- layer.show_text(talk.text("StillLoadingRemoteData"))
- else:
- # Progress bar
- UI_color.set(layer, win, "progress_background")
- UI_elements.roundrect(layer, win,
- win.current["w"]/2-200,
- win.current["h"]-140,
- 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"]-140,
- 400*max(0, min(1, win.current["http-server"]["fileprog"])),
- 20,
- 10,)
- UI_color.set(layer, win, "text_normal")
- layer.set_font_size(15)
- layer.move_to(win.current["w"]/2-180,
- win.current["h"]-145)
- layer.show_text(win.current["http-server"]["message"])
- # Top Tabs
-
- if "active_tab" not in win.current["remote-folder-data"][cur]:
- win.current["remote-folder-data"][cur]["active_tab"] = "changed"
- # Missing
- def do():
- win.current["remote-folder-data"][cur]["active_tab"] = "missing"
- if win.current["remote-folder-data"][cur]["active_tab"] == "missing":
- UI_color.set(layer, win, "progress_time")
- UI_elements.roundrect(layer, win,
- win.current["w"]/2-340,
- 110,
- 330,
- 40,
- 10)
-
- UI_elements.roundrect(layer, win,
- win.current["w"]/2-340,
- 110,
- 330,
- 40,
- 10,
- button=do,
- icon="new_file",
- tip=talk.text("MissingFiles"))
-
-
-
- UI_color.set(layer, win, "text_normal")
- layer.set_font_size(20)
- layer.move_to(win.current["w"]/2-290,
- 135)
- layer.show_text(str(len(win.current["remote-folder-data"][cur]["missing"]))+" "+talk.text("MissingFiles"))
- # Changed
- def do():
- win.current["remote-folder-data"][cur]["active_tab"] = "changed"
- if win.current["remote-folder-data"][cur]["active_tab"] == "changed":
- UI_color.set(layer, win, "progress_time")
- UI_elements.roundrect(layer, win,
- win.current["w"]/2+10,
- 110,
- 330,
- 40,
- 10)
-
- UI_elements.roundrect(layer, win,
- win.current["w"]/2+10,
- 110,
- 330,
- 40,
- 10,
- button=do,
- icon="configure_file",
- tip=talk.text("ChangedFiles"))
-
- UI_color.set(layer, win, "text_normal")
- layer.set_font_size(20)
- layer.move_to(win.current["w"]/2+60,
- 135)
- layer.show_text(str(len(win.current["remote-folder-data"][cur]["changed"]))+" "+talk.text("ChangedFiles"))
-
- # Search
-
- UI_elements.image(layer, win, "settings/themes/"\
- +win.settings["Theme"]+"/icons/search.png",
- win.current["w"]/2,
- 160,
- 40,
- 40)
-
- UI_elements.text(layer, win, "http_client_search",
- win.current["w"]/2+50,
- 160,
- 250,
- 40)
- # Toggle Visible
- if "toggle_visible" not in win.current["remote-folder-data"][cur]:
- win.current["remote-folder-data"][cur]["toggle_visible"] = True
- toggle = {"do":None}
-
- def do():
- win.current["remote-folder-data"][cur]["toggle_visible"] = not win.current["remote-folder-data"][cur]["toggle_visible"]
- toggle["do"] = win.current["remote-folder-data"][cur]["toggle_visible"]
- icon = "unchecked"
- if win.current["remote-folder-data"][cur]["toggle_visible"]:
- icon = "checked"
-
- UI_elements.roundrect(layer, win,
- win.current["w"]/2-340,
- 160,
- 330,
- 40,
- 10,
- button=do,
- icon=icon,
- tip=talk.text("ToggleVisible"))
- UI_color.set(layer, win, "text_normal")
- layer.set_font_size(20)
- layer.move_to(win.current["w"]/2-290,
- 185)
- layer.show_text(talk.text("ToggleVisible"))
-
- # Clipping everything
- UI_elements.roundrect(layer, win,
- win.current["w"]/2-350,
- 200,
- 700,
- win.current["h"]-360,
- 10,
- fill=False)
- layer.clip()
-
- clip = [
- win.current["w"]/2-350,
- 200,
- 700,
- win.current["h"]-360]
-
- # Setting up the scroll
- if "http-prompt" not in win.scroll:
- win.scroll["http-prompt"] = 0
-
-
- current_Y = 100
- # Display list
- DisplayList = {}
- ActiveTab = win.current["remote-folder-data"][cur]["active_tab"]
- DisplayList = win.current["remote-folder-data"][cur][ActiveTab]
-
- try:
- for f in DisplayList:
- fdata = DisplayList[f]
-
- if fdata.get("finished"):
- continue
- if win.text["http_client_search"]["text"] and not UI_math.found(f, win.text["http_client_search"]["text"]):
- continue
-
- ticon = "unchecked"
- if fdata.get("to_download", True):
- ticon = "checked"
- if toggle["do"] != None:
- fdata["to_download"] = toggle["do"]
-
- def do():
- fdata["to_download"] = not fdata.get("to_download", True)
- # Filetype icon
- ext = f[f.rfind(".")+1:]
- if ext in fileformats.images:
- icon = "image"
- elif ext in fileformats.videos:
- icon = "video"
- elif ext in fileformats.sounds:
- icon = "mus"
- elif ext == "blend":
- icon = "blender"
- elif ext == "progress":
- icon = "checklist"
- else:
- icon = "file"
-
-
- UI_elements.roundrect(layer, win,
- win.current["w"]/2-315,
- 110 + current_Y + win.scroll["http-prompt"],
- 660,
- 40,
- 10,
- button=do,
- icon=ticon,
- tip=f)
- UI_elements.image(layer, win, "settings/themes/"+win.settings["Theme"]+"/icons/"+icon+".png",
- win.current["w"]/2-275,
- 110 + current_Y + win.scroll["http-prompt"],
- 40,
- 40)
- UI_color.set(layer, win, "text_normal")
- layer.set_font_size(20)
- layer.move_to(win.current["w"]/2-220,
- current_Y + win.scroll["http-prompt"] + 135)
- layer.show_text(f[f.rfind("/")+1:])
- current_Y += 50
- except Exception as e:
- print(e)
- pass
-
- UI_elements.scroll_area(layer, win, "http-prompt",
- int(win.current["w"]/2-350),
- 200,
- 700,
- win.current["h"]-360,
- current_Y,
- bar=True,
- mmb=True,
- url="http-server-prompt"
- )
-
- return surface
|