From 69340a97b5d65453674a4e594110c3264d60b3ee Mon Sep 17 00:00:00 2001 From: Hendrik Wolff Date: Tue, 2 Sep 2025 18:32:54 +0200 Subject: [PATCH] Add pwa support, allow pwa-ing sub-folders Signed-off-by: Hendrik Wolff --- copyparty/__main__.py | 1 + copyparty/httpcli.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index ccda07f6..a60d960f 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1794,6 +1794,7 @@ def add_ui(ap, retry: int): ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files/folders matching \033[33mREGEX\033[0m in file list. WARNING: Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)") ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable") ap2.add_argument("--ufavico", metavar="TXT", type=u, default="", help="URL to .ico/png/gif/svg file; \033[33m--favico\033[0m takes precedence unless disabled (volflag=ufavico)") + ap2.add_argument("--pwa", action="store_true", help="announce webmanifest; use folders as PWA") ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="\033[34mREPEATABLE:\033[0m use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)") ap2.add_argument("--mpmc", type=u, default="", help=argparse.SUPPRESS) ap2.add_argument("--spinner", metavar="TXT", type=u, default="🌲", help="\033[33memoji\033[0m or \033[33memoji,css\033[0m Example: [\033[32m🥖,padding:0\033[0m]") diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 4c841397..56b49031 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -761,6 +761,11 @@ class HttpCli(object): if "html_head_s" in vn.flags: self.html_head += vn.flags["html_head_s"] + if self.args.pwa: + proto = "https" if self.is_https else "http" + url = "{}://{}/{}".format(proto, self.host, self.vpath) + self.html_head += ''.format(url) + try: cors_k = self._cors() if self.mode in ("GET", "HEAD"): @@ -1385,6 +1390,9 @@ class HttpCli(object): if "rss" in self.uparam: return self.tx_rss() + if "pwa" in self.uparam: + return self.tx_pwa() + return self.tx_browser() def tx_rss(self) -> bool: @@ -1589,6 +1597,20 @@ class HttpCli(object): raise Pebkac(404, "requested file is not a valid zip file") return True + def tx_pwa(self) -> bool: + proto = "https" if self.is_https else "http" + url = "{}://{}/{}".format(proto, self.host, self.vpath) + name = "{} {} {}".format(self.args.favico.split(" ")[0], self.args.name, self.vpath) + ret = { + "name": name, "short_name": name, + "start_url": "{}".format(url), + "theme_color": "#000000", "background_color": "#000000", + "display": "standalone" + } + zs = json.dumps(ret, separators=(",\n", ": ")) + self.reply(zs.encode("utf-8", "replace"), mime="application/json") + return True + def handle_propfind(self) -> bool: if self.do_log: self.log("PFIND %s @%s" % (self.req, self.uname))