diff --git a/.vscode/settings.json b/.vscode/settings.json index de19fdfc..e5d1c852 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -52,6 +52,7 @@ "matroska", "mbed", "mccabe", + "MEIPASS", "Microformat", "microformats", "mkvtoolsnix", diff --git a/app.spec b/app.spec index 2ef14c1f..9857a34d 100644 --- a/app.spec +++ b/app.spec @@ -31,6 +31,7 @@ hidden = [ "engineio", "engineio.async_drivers.aiohttp", "socketio.async_drivers.aiohttp", + "app" ] hidden = [f.replace("-", "_") for f in hidden] @@ -41,7 +42,7 @@ a = Analysis( # noqa: F821 # type: ignore binaries=binaries, datas=[ ("ui/exported", "ui/exported"), - ("app/migrations", "migrations"), + ("app/", "app/"), ], hiddenimports=hidden, hookspath=[], @@ -62,7 +63,7 @@ exe = EXE( # type: ignore # noqa: F821 debug=False, strip=False, upx=True, - console=False, + console=True, # Turn on to True if you want a console window for debugging. icon="ui/public/favicon.ico", onefile=True, ) diff --git a/app/library/Utils.py b/app/library/Utils.py index 92505b47..f2be102f 100644 --- a/app/library/Utils.py +++ b/app/library/Utils.py @@ -94,8 +94,7 @@ def calc_download_path(base_path: str | Path, folder: str | None = None, create_ if not folder: return str(base_path) - if folder.startswith("/"): - folder = folder[1:] + folder = folder.removeprefix("/") realBasePath = base_path.resolve() download_path = Path(realBasePath).joinpath(folder).resolve(strict=False) @@ -179,8 +178,7 @@ def extract_info( else: log_wrapper.add_target(target=params["callback"], level=logging.ERROR, name="callback") - if "callback" in params: - del params["callback"] + params.pop("callback", None) if log_wrapper.has_targets(): if "logger" in params: diff --git a/app/native.py b/app/native.py index dc60dd5f..716b4e93 100644 --- a/app/native.py +++ b/app/native.py @@ -1,38 +1,50 @@ #!/usr/bin/env python3 +import asyncio +import json +import logging import os - -os.environ["PYTHONUTF8"] = "1" - +import queue +import socket import sys +import threading +import traceback from pathlib import Path +import platformdirs + +sys.path.insert(0, os.path.join(getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__))), "app")) + +APP_NAME = "YTPTube" APP_ROOT = str((Path(__file__).parent / "..").resolve()) if APP_ROOT not in sys.path: sys.path.insert(0, APP_ROOT) - -import json -import queue -import socket -import threading - -import platformdirs - -ready = threading.Event() -exception_holder = queue.Queue() - -APP_NAME = "YTPTube" - try: import webview # type: ignore except ImportError as e: - if "nt" == os.name: - msg = "Please run 'uv pip install pywebview[edgechromium]' package to run YTPTube in native mode." - else: - msg = "Please run 'uv pip install pywebview[qt]' package to run YTPTube in native mode." + pkgs = "pywebview[edgechromium]" if os.name == "nt" else "pywebview[qt]" + msg: str = f"Please run 'uv pip install {pkgs}' to run YTPTube in native mode." raise ImportError(msg) from e +def error_window(exc: Exception | str) -> None: + trace: str = "\n".join(traceback.format_exception(exc)) if isinstance(exc, Exception) else exc + webview.create_window( + f"{APP_NAME} - Error", + html=f"
{trace}",
+ width=600,
+ height=400,
+ resizable=True,
+ )
+ webview.start(
+ gui="edgechromium" if os.name == "nt" else "qt",
+ debug=False,
+ storage_path=str(Path(os.getenv("YTP_TEMP_PATH", os.getcwd())) / "webview"),
+ private_mode=False,
+ )
+ sys.exit(1)
+
+
def set_env():
dct = {}
@@ -52,32 +64,31 @@ def set_env():
os.environ.update(dct)
-def app_start(host: str, port: int) -> None:
- import asyncio
-
- from main import Main
-
+def run_backend(host: str, port: int, ready_event: threading.Event, error_queue: queue.Queue):
try:
- loop = asyncio.get_running_loop()
- except RuntimeError:
- loop = asyncio.new_event_loop()
- asyncio.set_event_loop(loop)
+ try:
+ loop = asyncio.get_running_loop()
+ except RuntimeError:
+ loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(loop)
- try:
- Main(is_native=True).start(host, port, cb=lambda: ready.set())
+ from app.main import Main
+
+ Main(is_native=True).start(host, port, cb=lambda: ready_event.set())
except Exception as e:
- exception_holder.put(e)
- ready.set()
+ logging.exception(e)
+ error_queue.put(e)
+ ready_event.set()
-if __name__ == "__main__":
+def main():
host = "127.0.0.1"
set_env()
cfg_path: Path = Path(os.getenv("YTP_CONFIG_PATH")) / "webview.json"
-
port = None
win_conf: dict[str, int] = {}
+
if cfg_path.exists():
data = json.loads(cfg_path.read_text())
port = data.get("port")
@@ -91,18 +102,21 @@ if __name__ == "__main__":
port = s.getsockname()[1]
cfg_path.write_text(json.dumps({"port": port}))
- threading.Thread(target=app_start, args=(host, port), daemon=True).start()
+ ready = threading.Event()
+ errors: queue.Queue = queue.Queue()
+ threading.Thread(target=run_backend, args=(host, port, ready, errors), daemon=False).start()
- ready.wait()
+ ready.wait(timeout=5)
- if not exception_holder.empty():
- raise exception_holder.get()
+ if not errors.empty():
+ error_window(errors.get())
+ return
+ from app.library.version import APP_VERSION
+
+ gui = "edgechromium" if os.name == "nt" else "qt"
create_kwargs = {**win_conf, "resizable": True}
-
- webview.settings["ALLOW_DOWNLOADS"] = True
- webview.settings["OPEN_DEVTOOLS_IN_DEBUG"] = False
- window = webview.create_window(APP_NAME, f"http://{host}:{port}", **create_kwargs)
+ window = webview.create_window(f"{APP_NAME} - {APP_VERSION}", f"http://{host}:{port}", **create_kwargs)
def save_geometry():
cfg = {
@@ -116,13 +130,22 @@ if __name__ == "__main__":
window.events.resized += lambda *_: save_geometry()
window.events.moved += lambda *_: save_geometry()
+ window.events.closing += lambda *_: os._exit(0)
- gui = os.getenv("YTP_WV_GUI", None)
- gui = "edgechromium" if os.name == "nt" else "qt"
-
+ webview.settings["ALLOW_DOWNLOADS"] = True
+ webview.settings["OPEN_DEVTOOLS_IN_DEBUG"] = False
webview.start(
gui=gui,
debug=True,
storage_path=str(Path(os.getenv("YTP_TEMP_PATH", os.getcwd())) / "webview"),
private_mode=False,
)
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except Exception as e:
+ logging.exception(e)
+ error_window(e)
+ os._exit(1)
diff --git a/ui/components/NewDownload.vue b/ui/components/NewDownload.vue
index 581d714e..1bb75a0b 100644
--- a/ui/components/NewDownload.vue
+++ b/ui/components/NewDownload.vue
@@ -76,7 +76,8 @@
Opts
-