Skip to content

Commit

Permalink
Gooey compatibility WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
mos9527 committed Feb 5, 2025
1 parent 8b3eda0 commit 8479ae9
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 23 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ dist
venv/
test.py
.DS_Store
tmp.*
7 changes: 6 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
{
"version": "0.2.0",
"configurations": [

{
"name": "GUI Test",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/sssekai/__gui__.py",
}
{
"name": "Python: SpineExtract test",
"type": "debugpy",
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[tool.black]
line-length = 88
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
"requests",
"pyaxmlparser",
],
extras_require={
"gui": ["Gooey @ git+https://github.com/nicolasbraun/Gooey@11bd412"]
},
entry_points={
"console_scripts": ["sssekai = sssekai.__main__:__main__"],
"fsspec.specs": ["abcache = sssekai.abcache.fs.AbCacheFilesystem"],
Expand Down
136 changes: 136 additions & 0 deletions sssekai/__gui__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
from dataclasses import dataclass, field
from sssekai.__main__ import create_parser
from typing import Any, List
from argparse import ArgumentParser
from logging import basicConfig

try:
from gooey import Gooey, GooeyParser
except ImportError as e:
print("Please install sssekai[gui] to use the GUI")
raise e


# https://github.com/chriskiehl/Gooey/issues/826#issuecomment-1240180894
def __Gooey_120_patch_TaskBar():
from rewx import widgets
from rewx.widgets import set_basic_props, dirname
from rewx.dispatch import update
import wx
import sys

@update.register(wx.Frame)
def frame(element, instance: wx.Frame):
props = element["props"]
set_basic_props(instance, props)
if "title" in props:
instance.SetTitle(props["title"])
if "show" in props:
instance.Show(props["show"])
if "icon_uri" in props:
pass # No icons for now
if "on_close" in props:
instance.Bind(wx.EVT_CLOSE, props["on_close"])

return instance

widgets.frame = frame


def __Gooey_120_patch_wxTimer():
from gooey.gui.util.time import get_current_time, Timing

def __patch(self: Timing):
self.startTime = get_current_time()
self.estimatedRemaining = None
self.wxTimer.Start(milliseconds=1)

Timing.start = __patch


def __Gooey_120_patch_tqdm():
from subprocess import Popen
from gooey.gui import events
from gooey.gui.processor import ProcessController, pub

def __patch(self, process: Popen):
"""
Reads the stdout of `process` and forwards lines and progress
to any interested subscribers
"""
while True:
line = []
while ch := process.stdout.read(1):
if ch in (b"\r", b"\n"):
line.append(b"\n")
break
line.append(ch)
if not line:
break
line = b"".join(line)
_progress = line.find(b"%")
if _progress in range(0, 4):
_progress = int(line[:_progress].strip())
else:
_progress = None
pub.send_message(events.PROGRESS_UPDATE, progress=_progress)
if _progress is None or self.hide_progress_msg is False:
pub.send_message(events.CONSOLE_UPDATE, msg=line.decode(self.encoding))
pub.send_message(events.EXECUTION_COMPLETE)

ProcessController._forward_stdout = __patch
pass


from sssekai.unity import sssekai_set_unity_version


class GooeyParser(GooeyParser):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# It's guaranteed that with file input/outputs
# that we have
# infile, outfile, indir, outdir as names
# Map them to File Widgets
def add_argument(self, name, *args, **kwargs):
WIDGET_MAP = {
"infile": "FileChooser",
"outfile": "FileSaver",
"indir": "DirChooser",
"outdir": "DirChooser",
# Special cases
# AppHash
"--apk_src": "FileChooser",
"--ab_src": "FileChooser",
# AbCache
"--db": "FileChooser",
"--download_dir": "DirChooser",
"--dump_master_data": "DirChooser",
"--dump_user_data": "DirChooser",
}
kwargs |= {"widget": WIDGET_MAP.get(name, None)}
return super().add_argument(name, *args, **kwargs)


@Gooey(show_preview_warning=False)
def __main__():
__Gooey_120_patch_TaskBar()
__Gooey_120_patch_wxTimer()
__Gooey_120_patch_tqdm()
# ooh ooh aah aah monkey patching
parser = create_parser(GooeyParser)
args = parser.parse_args()
basicConfig(
level="DEBUG",
format="[%(levelname).4s] %(name)s %(message)s",
)
# override unity version
sssekai_set_unity_version(args.unity_version)
if "func" in args:
args.func(args)
pass


if __name__ in {"__main__", "__gui__"}:
__main__()
35 changes: 20 additions & 15 deletions sssekai/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,8 @@
from sssekai.unity import sssekai_get_unity_version, sssekai_set_unity_version


def __main__():
from tqdm.std import tqdm as tqdm_c

class SemaphoreStdout:
@staticmethod
def write(__s):
# Blocks tqdm's output until write on this stream is done
# Solves cases where progress bars gets re-rendered when logs
# spews out too fast
with tqdm_c.external_write_mode(file=sys.stdout, nolock=False):
return sys.stdout.write(__s)

parser = argparse.ArgumentParser(
def create_parser(clazz=argparse.ArgumentParser):
parser = clazz(
description="""Project SEKAI Asset Utility / PJSK 资源工具""",
formatter_class=argparse.RawTextHelpFormatter,
)
Expand Down Expand Up @@ -295,17 +284,33 @@ def write(__s):
"mvdata", help="""Extract MV Data from AssetBundle"""
)
mvdata_parser.add_argument(
"input", type=str, help="cache directory (live_pv/mv_data)"
"infile", type=str, help="cache directory (live_pv/mv_data)"
)
mvdata_parser.add_argument("output", type=str, help="output JSON file to dump into")
mvdata_parser.add_argument("outdir", type=str, help="output JSON file to dump into")
mvdata_parser.set_defaults(func=main_mvdata)
# moc3paths
moc3paths_parser = subparsers.add_parser(
"moc3paths", help="""Extract animation path CRCs from raw .moc3 binaries"""
)
moc3paths_parser.add_argument("indir", type=str, help="input directory")
moc3paths_parser.set_defaults(func=main_moc3paths)
return parser


def __main__():
from tqdm.std import tqdm as tqdm_c

class SemaphoreStdout:
@staticmethod
def write(__s):
# Blocks tqdm's output until write on this stream is done
# Solves cases where progress bars gets re-rendered when logs
# spews out too fast
with tqdm_c.external_write_mode(file=sys.stdout, nolock=False):
return sys.stdout.write(__s)

# parse args
parser = create_parser(argparse.ArgumentParser)
args = parser.parse_args()
# set logging level
import coloredlogs
Expand Down
1 change: 0 additions & 1 deletion sssekai/entrypoint/abcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class AbCacheDownloader(ThreadPoolExecutor):
def _ensure_progress(self):
if not self.progress:
self.progress = tqdm(
bar_format="{desc}: {percentage:.1f}%|{bar}| {n_fmt}/{total_fmt} {rate_fmt} {elapsed}<{remaining}",
total=0,
unit="B",
unit_scale=True,
Expand Down
9 changes: 6 additions & 3 deletions sssekai/entrypoint/apphash.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import UnityPy.enums
import UnityPy.enums.ClassIDType
from sssekai.unity.AssetBundle import load_assetbundle
from tqdm import tqdm

HASHREGEX = re.compile(b"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
REGION_MAP = {
Expand Down Expand Up @@ -51,9 +52,11 @@ def main_apphash(args):
stream=True,
)
size = resp.headers.get("Content-Length", -1)
for chunck in resp.iter_content(chunk_size=2**20):
src.write(chunck)
logger.debug("Downloading %d/%s" % (src.tell(), size))
with tqdm(total=int(size), unit="B", unit_scale=True) as progress:
progress.sp = print
for chunck in resp.iter_content(chunk_size=2**20):
src.write(chunck)
progress.update(len(chunck))
src.seek(0)
else:
src = open(args.apk_src, "rb")
Expand Down
6 changes: 3 additions & 3 deletions sssekai/entrypoint/mvdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
def main_mvdata(args):
from UnityPy.enums import ClassIDType

source = args.input
source = args.infile
source = os.path.expanduser(source)
source = os.path.abspath(source)
os.chdir(source)
Expand All @@ -29,8 +29,8 @@ def main_mvdata(args):
break
except Exception as e:
print(f"skipping {key}: {e}")
outdir = os.path.dirname(args.output)
outdir = os.path.dirname(args.outdir)
if outdir:
os.makedirs(outdir, exist_ok=True)
with open(args.output, "w", encoding="utf-8") as f:
with open(args.outdir, "w", encoding="utf-8") as f:
json.dump(mvdata_items, f, indent=4, ensure_ascii=False)

0 comments on commit 8479ae9

Please sign in to comment.