mirror of
				https://github.com/9001/copyparty.git
				synced 2025-10-31 03:53:31 +00:00 
			
		
		
		
	Compare commits
	
		
			28 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | e897df3b34 | ||
|  | 8cd97ab0e7 | ||
|  | bf4949353d | ||
|  | 98a944f7cc | ||
|  | 7c10f81c92 | ||
|  | 126ecc55c3 | ||
|  | 1034a51bd2 | ||
|  | a2657887cc | ||
|  | c14b17bfaf | ||
|  | 59ebc795e7 | ||
|  | 8e128d917e | ||
|  | ea762b05e0 | ||
|  | db374b19f1 | ||
|  | ab3839ef36 | ||
|  | 9886c442f2 | ||
|  | c8d1926d52 | ||
|  | a6bd699e52 | ||
|  | 12143f2702 | ||
|  | 480705dee9 | ||
|  | 781d5094f4 | ||
|  | 5615cb94cd | ||
|  | 302302a2ac | ||
|  | 9761b4e3e9 | ||
|  | 0cf6924dca | ||
|  | 5fd81e9f90 | ||
|  | 52bf6f892b | ||
|  | f3cce232a4 | ||
|  | 53d3c8b28e | 
							
								
								
									
										2
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							| @@ -14,6 +14,8 @@ | ||||
|                 "-emp", | ||||
|                 "-e2dsa", | ||||
|                 "-e2ts", | ||||
|                 "-mtp", | ||||
|                 ".bpm=f,bin/mtag/audio-bpm.py", | ||||
|                 "-a", | ||||
|                 "ed:wark", | ||||
|                 "-v", | ||||
|   | ||||
							
								
								
									
										27
									
								
								.vscode/launch.py
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.vscode/launch.py
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| # takes arguments from launch.json | ||||
| # is used by no_dbg in tasks.json | ||||
| # launches 10x faster than mspython debugpy | ||||
| # and is stoppable with ^C | ||||
|  | ||||
| import os | ||||
| import sys | ||||
|  | ||||
| sys.path.insert(0, os.getcwd()) | ||||
|  | ||||
| import jstyleson | ||||
| from copyparty.__main__ import main as copyparty | ||||
|  | ||||
| with open(".vscode/launch.json", "r") as f: | ||||
|     tj = f.read() | ||||
|  | ||||
| oj = jstyleson.loads(tj) | ||||
| argv = oj["configurations"][0]["args"] | ||||
| argv = [os.path.expanduser(x) if x.startswith("~") else x for x in argv] | ||||
| try: | ||||
|     copyparty(argv) | ||||
| except SystemExit as ex: | ||||
|     if ex.code: | ||||
|         raise | ||||
|  | ||||
| print("\n\033[32mokke\033[0m") | ||||
| sys.exit(1) | ||||
							
								
								
									
										4
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							| @@ -9,9 +9,7 @@ | ||||
|         { | ||||
|             "label": "no_dbg", | ||||
|             "type": "shell", | ||||
|             "command": "${config:python.pythonPath} -m copyparty -ed -emp -e2dsa -e2ts -a ed:wark -v srv::r:aed:cnodupe -v dist:dist:r ;exit 1" | ||||
|             // -v ~/Music/mt:mt:r:cmtp=.bpm=~/dev/copyparty/bin/mtag/audio-bpm.py:cmtp=key=~/dev/copyparty/bin/mtag/audio-key.py:ce2tsr  | ||||
|             // -v ~/Music/mt:mt:r:cmtp=.bpm=~/dev/copyparty/bin/mtag/audio-bpm.py:ce2tsr | ||||
|             "command": "${config:python.pythonPath} .vscode/launch.py" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										52
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								README.md
									
									
									
									
									
								
							| @@ -27,6 +27,7 @@ turn your phone or raspi into a portable file server with resumable uploads/down | ||||
|     * [metadata from audio files](#metadata-from-audio-files) | ||||
|     * [file parser plugins](#file-parser-plugins) | ||||
|     * [complete examples](#complete-examples) | ||||
| * [browser support](#browser-support) | ||||
| * [client examples](#client-examples) | ||||
| * [dependencies](#dependencies) | ||||
|     * [optional gpl stuff](#optional-gpl-stuff) | ||||
| @@ -196,6 +197,41 @@ copyparty can invoke external programs to collect additional metadata for files | ||||
|   `python copyparty-sfx.py -v /mnt/nas/music:/music:r -e2dsa -e2ts -mtp .bpm=f,audio-bpm.py -mtp key=f,audio-key.py` | ||||
|  | ||||
|  | ||||
| # browser support | ||||
|  | ||||
| `ie` = internet-explorer, `ff` = firefox, `c` = chrome, `iOS` = iPhone/iPad, `Andr` = Android | ||||
|  | ||||
| | feature         | ie6 | ie9 | ie10 | ie11 | ff 52 | c 49 | iOS | Andr | | ||||
| | --------------- | --- | --- | ---- | ---- | ----- | ---- | --- | ---- | | ||||
| | browse files    | yep | yep | yep  | yep  | yep   | yep  | yep | yep  | | ||||
| | basic uploader  | yep | yep | yep  | yep  | yep   | yep  | yep | yep  | | ||||
| | make directory  | yep | yep | yep  | yep  | yep   | yep  | yep | yep  | | ||||
| | send message    | yep | yep | yep  | yep  | yep   | yep  | yep | yep  | | ||||
| | set sort order  |  -  | yep | yep  | yep  | yep   | yep  | yep | yep  | | ||||
| | zip selection   |  -  | yep | yep  | yep  | yep   | yep  | yep | yep  | | ||||
| | directory tree  |  -  |  -  | `*1` | yep  | yep   | yep  | yep | yep  | | ||||
| | up2k            |  -  |  -  | yep  | yep  | yep   | yep  | yep | yep  | | ||||
| | icons work      |  -  |  -  | yep  | yep  | yep   | yep  | yep | yep  | | ||||
| | markdown editor |  -  |  -  | yep  | yep  | yep   | yep  | yep | yep  | | ||||
| | markdown viewer |  -  |  -  | yep  | yep  | yep   | yep  | yep | yep  | | ||||
| | play mp3/m4a    |  -  | yep | yep  | yep  | yep   | yep  | yep | yep  | | ||||
| | play ogg/opus   |  -  |  -  |  -   |  -   | yep   | yep  | `*2` | yep | | ||||
|  | ||||
| * internet explorer 6 to 8 behave the same | ||||
| * firefox 52 and chrome 49 are the last winxp versions | ||||
| * `*1` only public folders (login session is dropped) and no history / back-button | ||||
| * `*2` using a wasm decoder which can sometimes get stuck and consumes a bit more power | ||||
|  | ||||
| quick summary of more eccentric web-browsers trying to view a directory index: | ||||
| * safari (14.0.3/macos) is chrome with janky wasm, so playing opus can deadlock the javascript engine | ||||
| * safari (14.0.1/iOS) same as macos, except it recovers from the deadlocks if you poke it a bit | ||||
| * links (2.21/macports) can browse, login, upload/mkdir/msg | ||||
| * lynx (2.8.9/macports) can browse, login, upload/mkdir/msg | ||||
| * w3m (0.5.3/macports) can browse, login, upload at 100kB/s, mkdir/msg | ||||
| * netsurf (3.10/arch) is basically ie6 with much better css (javascript has almost no effect) | ||||
| * netscape 4.0 and 4.5 can browse (text is yellow on white), upload with `?b=u` | ||||
| * SerenityOS (22d13d8) hits a page fault, works with `?b=u`, file input not-impl, url params are multiplying | ||||
|  | ||||
| # client examples | ||||
|  | ||||
| * javascript: dump some state into a file (two separate examples) | ||||
| @@ -303,15 +339,21 @@ in the `scripts` folder: | ||||
|  | ||||
| roughly sorted by priority | ||||
|  | ||||
| * audio link with timestamp | ||||
| * separate sqlite table per tag | ||||
| * audio fingerprinting | ||||
| * readme.md as epilogue | ||||
| * reduce up2k roundtrips | ||||
|   * start from a chunk index and just go | ||||
|   * terminate client on bad data | ||||
| * drop onto folders | ||||
| * `os.copy_file_range` for up2k cloning | ||||
| * up2k partials ui | ||||
| * support pillow-simd | ||||
| * cache sha512 chunks on client | ||||
| * comment field | ||||
| * ~~look into android thumbnail cache file format~~ bad idea | ||||
| * figure out the deal with pixel3a not being connectable as hotspot | ||||
|   * pixel3a having unpredictable 3sec latency in general :|||| | ||||
|  | ||||
| discarded ideas | ||||
|  | ||||
| * up2k partials ui | ||||
| * cache sha512 chunks on client | ||||
| * comment field | ||||
| * look into android thumbnail cache file format | ||||
|   | ||||
| @@ -12,7 +12,6 @@ import re | ||||
| import os | ||||
| import sys | ||||
| import time | ||||
| import signal | ||||
| import shutil | ||||
| import filecmp | ||||
| import locale | ||||
| @@ -56,6 +55,12 @@ class RiceFormatter(argparse.HelpFormatter): | ||||
|         return "".join(indent + line + "\n" for line in text.splitlines()) | ||||
|  | ||||
|  | ||||
| class Dodge11874(RiceFormatter): | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         kwargs["width"] = 9003 | ||||
|         super(Dodge11874, self).__init__(*args, **kwargs) | ||||
|  | ||||
|  | ||||
| def warn(msg): | ||||
|     print("\033[1mwarning:\033[0;33m {}\033[0m\n".format(msg)) | ||||
|  | ||||
| @@ -167,7 +172,7 @@ def configure_ssl_ciphers(al): | ||||
|         sys.exit(0) | ||||
|  | ||||
|  | ||||
| def sighandler(signal=None, frame=None): | ||||
| def sighandler(sig=None, frame=None): | ||||
|     msg = [""] * 5 | ||||
|     for th in threading.enumerate(): | ||||
|         msg.append(str(th)) | ||||
| @@ -177,37 +182,9 @@ def sighandler(signal=None, frame=None): | ||||
|     print("\n".join(msg)) | ||||
|  | ||||
|  | ||||
| def main(argv=None): | ||||
|     time.strptime("19970815", "%Y%m%d")  # python#7980 | ||||
|     if WINDOWS: | ||||
|         os.system("rem")  # enables colors | ||||
|  | ||||
|     if argv is None: | ||||
|         argv = sys.argv | ||||
|  | ||||
|     desc = py_desc().replace("[", "\033[1;30m[") | ||||
|  | ||||
|     f = '\033[36mcopyparty v{} "\033[35m{}\033[36m" ({})\n{}\033[0m\n' | ||||
|     print(f.format(S_VERSION, CODENAME, S_BUILD_DT, desc)) | ||||
|  | ||||
|     ensure_locale() | ||||
|     if HAVE_SSL: | ||||
|         ensure_cert() | ||||
|  | ||||
|     deprecated = [["-e2s", "-e2ds"]] | ||||
|     for dk, nk in deprecated: | ||||
|         try: | ||||
|             idx = argv.index(dk) | ||||
|         except: | ||||
|             continue | ||||
|  | ||||
|         msg = "\033[1;31mWARNING:\033[0;1m\n  {} \033[0;33mwas replaced with\033[0;1m {} \033[0;33mand will be removed\n\033[0m" | ||||
|         print(msg.format(dk, nk)) | ||||
|         argv[idx] = nk | ||||
|         time.sleep(2) | ||||
|  | ||||
| def run_argparse(argv, formatter): | ||||
|     ap = argparse.ArgumentParser( | ||||
|         formatter_class=RiceFormatter, | ||||
|         formatter_class=formatter, | ||||
|         prog="copyparty", | ||||
|         description="http file sharing hub v{} ({})".format(S_VERSION, S_BUILD_DT), | ||||
|         epilog=dedent( | ||||
| @@ -219,6 +196,9 @@ def main(argv=None): | ||||
|              | ||||
|             list of cflags: | ||||
|               "cnodupe" rejects existing files (instead of symlinking them) | ||||
|               "ce2d" sets -e2d (all -e2* args can be set using ce2* cflags) | ||||
|               "cd2t" disables metadata collection, overrides -e2t* | ||||
|               "cd2d" disables all database stuff, overrides -e2* | ||||
|  | ||||
|             example:\033[35m | ||||
|               -a ed:hunter2 -v .::r:aed -v ../inc:dump:w:aed:cnodupe  \033[36m | ||||
| @@ -293,9 +273,44 @@ def main(argv=None): | ||||
|     ap2.add_argument("--ssl-dbg", action="store_true", help="dump some tls info") | ||||
|     ap2.add_argument("--ssl-log", metavar="PATH", help="log master secrets") | ||||
|      | ||||
|     al = ap.parse_args(args=argv[1:]) | ||||
|     return ap.parse_args(args=argv[1:]) | ||||
|     # fmt: on | ||||
|  | ||||
|  | ||||
| def main(argv=None): | ||||
|     time.strptime("19970815", "%Y%m%d")  # python#7980 | ||||
|     if WINDOWS: | ||||
|         os.system("rem")  # enables colors | ||||
|  | ||||
|     if argv is None: | ||||
|         argv = sys.argv | ||||
|  | ||||
|     desc = py_desc().replace("[", "\033[1;30m[") | ||||
|  | ||||
|     f = '\033[36mcopyparty v{} "\033[35m{}\033[36m" ({})\n{}\033[0m\n' | ||||
|     print(f.format(S_VERSION, CODENAME, S_BUILD_DT, desc)) | ||||
|  | ||||
|     ensure_locale() | ||||
|     if HAVE_SSL: | ||||
|         ensure_cert() | ||||
|  | ||||
|     deprecated = [["-e2s", "-e2ds"]] | ||||
|     for dk, nk in deprecated: | ||||
|         try: | ||||
|             idx = argv.index(dk) | ||||
|         except: | ||||
|             continue | ||||
|  | ||||
|         msg = "\033[1;31mWARNING:\033[0;1m\n  {} \033[0;33mwas replaced with\033[0;1m {} \033[0;33mand will be removed\n\033[0m" | ||||
|         print(msg.format(dk, nk)) | ||||
|         argv[idx] = nk | ||||
|         time.sleep(2) | ||||
|  | ||||
|     try: | ||||
|         al = run_argparse(argv, RiceFormatter) | ||||
|     except AssertionError: | ||||
|         al = run_argparse(argv, Dodge11874) | ||||
|  | ||||
|     # propagate implications | ||||
|     for k1, k2 in IMPLICATIONS: | ||||
|         if getattr(al, k1): | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| # coding: utf-8 | ||||
|  | ||||
| VERSION = (0, 10, 5) | ||||
| VERSION = (0, 10, 8) | ||||
| CODENAME = "zip it" | ||||
| BUILD_DT = (2021, 3, 31) | ||||
| BUILD_DT = (2021, 4, 11) | ||||
|  | ||||
| S_VERSION = ".".join(map(str, VERSION)) | ||||
| S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT) | ||||
|   | ||||
| @@ -168,8 +168,13 @@ class VFS(object): | ||||
|         for vpath, apath, files, rd, vd in self.walk("", vrem, uname, dots, scandir): | ||||
|             if flt: | ||||
|                 files = [x for x in files if x[0] in flt] | ||||
|                 rd = [x for x in rd if x[0] in flt] | ||||
|                 vd = {x: y for x, y in vd.items() if x in flt} | ||||
|  | ||||
|                 rm = [x for x in rd if x[0] not in flt] | ||||
|                 [rd.remove(x) for x in rm] | ||||
|  | ||||
|                 rm = [x for x in vd.keys() if x not in flt] | ||||
|                 [vd.pop(x) for x in rm] | ||||
|  | ||||
|                 flt = None | ||||
|  | ||||
|             # print(repr([vpath, apath, [x[0] for x in files]])) | ||||
| @@ -228,12 +233,6 @@ class AuthSrv(object): | ||||
|     def log(self, msg, c=0): | ||||
|         self.log_func("auth", msg, c) | ||||
|  | ||||
|     def invert(self, orig): | ||||
|         if PY2: | ||||
|             return {v: k for k, v in orig.iteritems()} | ||||
|         else: | ||||
|             return {v: k for k, v in orig.items()} | ||||
|  | ||||
|     def laggy_iter(self, iterable): | ||||
|         """returns [value,isFinalValue]""" | ||||
|         it = iter(iterable) | ||||
| @@ -490,7 +489,7 @@ class AuthSrv(object): | ||||
|         with self.mutex: | ||||
|             self.vfs = vfs | ||||
|             self.user = user | ||||
|             self.iuser = self.invert(user) | ||||
|             self.iuser = {v: k for k, v in user.items()} | ||||
|  | ||||
|         # import pprint | ||||
|         # pprint.pprint({"usr": user, "rd": mread, "wr": mwrite, "mnt": mount}) | ||||
|   | ||||
| @@ -74,7 +74,7 @@ class HttpCli(object): | ||||
|                 headerlines.pop(0) | ||||
|  | ||||
|             try: | ||||
|                 self.mode, self.req, _ = headerlines[0].split(" ") | ||||
|                 self.mode, self.req, self.http_ver = headerlines[0].split(" ") | ||||
|             except: | ||||
|                 raise Pebkac(400, "bad headers:\n" + "\n".join(headerlines)) | ||||
|  | ||||
| @@ -93,30 +93,13 @@ class HttpCli(object): | ||||
|             self.headers[k.lower()] = v.strip() | ||||
|  | ||||
|         v = self.headers.get("connection", "").lower() | ||||
|         self.keepalive = not v.startswith("close") | ||||
|         self.keepalive = not v.startswith("close") and self.http_ver != "HTTP/1.0" | ||||
|  | ||||
|         v = self.headers.get("x-forwarded-for", None) | ||||
|         if v is not None and self.conn.addr[0] in ["127.0.0.1", "::1"]: | ||||
|             self.ip = v.split(",")[0] | ||||
|             self.log_src = self.conn.set_rproxy(self.ip) | ||||
|  | ||||
|         self.uname = "*" | ||||
|         if "cookie" in self.headers: | ||||
|             cookies = self.headers["cookie"].split(";") | ||||
|             for k, v in [x.split("=", 1) for x in cookies]: | ||||
|                 if k.strip() != "cppwd": | ||||
|                     continue | ||||
|  | ||||
|                 v = unescape_cookie(v) | ||||
|                 if v in self.auth.iuser: | ||||
|                     self.uname = self.auth.iuser[v] | ||||
|  | ||||
|                 break | ||||
|  | ||||
|         if self.uname: | ||||
|             self.rvol = self.auth.vfs.user_tree(self.uname, readable=True) | ||||
|             self.wvol = self.auth.vfs.user_tree(self.uname, writable=True) | ||||
|  | ||||
|         # split req into vpath + uparam | ||||
|         uparam = {} | ||||
|         if "?" not in self.req: | ||||
| @@ -140,6 +123,22 @@ class HttpCli(object): | ||||
|         self.uparam = uparam | ||||
|         self.vpath = unquotep(vpath) | ||||
|  | ||||
|         pwd = None | ||||
|         if "cookie" in self.headers: | ||||
|             cookies = self.headers["cookie"].split(";") | ||||
|             for k, v in [x.split("=", 1) for x in cookies]: | ||||
|                 if k.strip() != "cppwd": | ||||
|                     continue | ||||
|  | ||||
|                 pwd = unescape_cookie(v) | ||||
|                 break | ||||
|  | ||||
|         pwd = uparam.get("pw", pwd) | ||||
|         self.uname = self.auth.iuser.get(pwd, "*") | ||||
|         if self.uname: | ||||
|             self.rvol = self.auth.vfs.user_tree(self.uname, readable=True) | ||||
|             self.wvol = self.auth.vfs.user_tree(self.uname, writable=True) | ||||
|  | ||||
|         ua = self.headers.get("user-agent", "") | ||||
|         if ua.startswith("rclone/"): | ||||
|             uparam["raw"] = False | ||||
| @@ -160,7 +159,9 @@ class HttpCli(object): | ||||
|         except Pebkac as ex: | ||||
|             try: | ||||
|                 # self.log("pebkac at httpcli.run #2: " + repr(ex)) | ||||
|                 self.keepalive = self._check_nonfatal(ex) | ||||
|                 if not self._check_nonfatal(ex): | ||||
|                     self.keepalive = False | ||||
|  | ||||
|                 self.log("{}\033[0m, {}".format(str(ex), self.vpath), 3) | ||||
|                 msg = "<pre>{}\r\nURL: {}\r\n".format(str(ex), self.vpath) | ||||
|                 self.reply(msg.encode("utf-8", "replace"), status=ex.code) | ||||
| @@ -169,7 +170,7 @@ class HttpCli(object): | ||||
|                 return False | ||||
|  | ||||
|     def send_headers(self, length, status=200, mime=None, headers={}): | ||||
|         response = ["HTTP/1.1 {} {}".format(status, HTTPCODE[status])] | ||||
|         response = ["{} {} {}".format(self.http_ver, status, HTTPCODE[status])] | ||||
|  | ||||
|         if length is not None: | ||||
|             response.append("Content-Length: " + unicode(length)) | ||||
| @@ -213,6 +214,20 @@ class HttpCli(object): | ||||
|         self.log(body.rstrip()) | ||||
|         self.reply(b"<pre>" + body.encode("utf-8") + b"\r\n", *list(args), **kwargs) | ||||
|  | ||||
|     def urlq(self, add={}, rm=[]): | ||||
|         """ | ||||
|         generates url query based on uparam (b, pw, all others) | ||||
|         removing anything in rm, adding pairs in add | ||||
|         """ | ||||
|  | ||||
|         kv = {k: v for k, v in self.uparam.items() if k not in rm} | ||||
|         kv.update(add) | ||||
|         if not kv: | ||||
|             return "" | ||||
|  | ||||
|         r = ["{}={}".format(k, quotep(v)) if v else k for k, v in kv.items()] | ||||
|         return "?" + "&".join(r) | ||||
|  | ||||
|     def handle_get(self): | ||||
|         logmsg = "{:4} {}".format(self.mode, self.req) | ||||
|  | ||||
| @@ -321,8 +336,19 @@ class HttpCli(object): | ||||
|             elif "print" in opt: | ||||
|                 reader, _ = self.get_body_reader() | ||||
|                 for buf in reader: | ||||
|                     buf = buf.decode("utf-8", "replace") | ||||
|                     self.log("urlform @ {}\n  {}\n".format(self.vpath, buf)) | ||||
|                     orig = buf.decode("utf-8", "replace") | ||||
|                     m = "urlform_raw {} @ {}\n  {}\n" | ||||
|                     self.log(m.format(len(orig), self.vpath, orig)) | ||||
|                     try: | ||||
|                         plain = unquote(buf.replace(b"+", b" ")) | ||||
|                         plain = plain.decode("utf-8", "replace") | ||||
|                         if buf.startswith(b"msg="): | ||||
|                             plain = plain[4:] | ||||
|  | ||||
|                         m = "urlform_dec {} @ {}\n  {}\n" | ||||
|                         self.log(m.format(len(plain), self.vpath, plain)) | ||||
|                     except Exception as ex: | ||||
|                         self.log(repr(ex)) | ||||
|  | ||||
|             if "get" in opt: | ||||
|                 return self.handle_get() | ||||
| @@ -1202,9 +1228,10 @@ class HttpCli(object): | ||||
|         return True | ||||
|  | ||||
|     def tx_mounts(self): | ||||
|         suf = self.urlq(rm=["h"]) | ||||
|         rvol = [x + "/" if x else x for x in self.rvol] | ||||
|         wvol = [x + "/" if x else x for x in self.wvol] | ||||
|         html = self.j2("splash", this=self, rvol=rvol, wvol=wvol) | ||||
|         html = self.j2("splash", this=self, rvol=rvol, wvol=wvol, url_suf=suf) | ||||
|         self.reply(html.encode("utf-8")) | ||||
|         return True | ||||
|  | ||||
| @@ -1334,6 +1361,8 @@ class HttpCli(object): | ||||
|             idx = self.conn.get_u2idx() | ||||
|             icur = idx.get_cur(vn.realpath) | ||||
|  | ||||
|         url_suf = self.urlq() | ||||
|  | ||||
|         dirs = [] | ||||
|         files = [] | ||||
|         for fn in vfs_ls: | ||||
| @@ -1486,8 +1515,12 @@ class HttpCli(object): | ||||
|  | ||||
|         dirs.extend(files) | ||||
|  | ||||
|         tpl = "browser" | ||||
|         if "b" in self.uparam: | ||||
|             tpl = "browser2" | ||||
|  | ||||
|         html = self.j2( | ||||
|             "browser", | ||||
|             tpl, | ||||
|             vdir=quotep(self.vpath), | ||||
|             vpnodes=vpnodes, | ||||
|             files=dirs, | ||||
| @@ -1500,6 +1533,8 @@ class HttpCli(object): | ||||
|             have_up2k_idx=("e2d" in vn.flags), | ||||
|             have_tags_idx=("e2t" in vn.flags), | ||||
|             have_zip=(not self.args.no_zip), | ||||
|             have_b_u=(self.writable and self.uparam.get("b") == "u"), | ||||
|             url_suf=url_suf, | ||||
|             logues=logues, | ||||
|             title=html_escape(self.vpath), | ||||
|             srv_info=srv_info, | ||||
|   | ||||
| @@ -52,7 +52,7 @@ class HttpSrv(object): | ||||
|         env.loader = jinja2.FileSystemLoader(os.path.join(E.mod, "web")) | ||||
|         self.j2 = { | ||||
|             x: env.get_template(x + ".html") | ||||
|             for x in ["splash", "browser", "msg", "md", "mde"] | ||||
|             for x in ["splash", "browser", "browser2", "msg", "md", "mde"] | ||||
|         } | ||||
|  | ||||
|         cert_path = os.path.join(E.cfg, "cert.pem") | ||||
|   | ||||
| @@ -183,9 +183,9 @@ a, #files tbody div a:last-child { | ||||
| 	text-shadow: 0 0 .3em #b80; | ||||
| } | ||||
| #files tbody tr.sel td { | ||||
| 	background: #80b; | ||||
| 	color: #fff; | ||||
| 	border-color: #a3d; | ||||
| 	background: #925; | ||||
| 	border-color: #c37; | ||||
| } | ||||
| #blocked { | ||||
| 	position: fixed; | ||||
| @@ -243,7 +243,7 @@ a, #files tbody div a:last-child { | ||||
| 	height: 100%; | ||||
| 	background: #3c3c3c; | ||||
| } | ||||
| #wtoggle { | ||||
| #wtico { | ||||
| 	cursor: url(/.cpr/dd/1.png), pointer; | ||||
| 	animation: cursor 500ms infinite; | ||||
| } | ||||
| @@ -273,24 +273,32 @@ a, #files tbody div a:last-child { | ||||
| 	padding: .2em 0 0 .07em; | ||||
| 	color: #fff; | ||||
| } | ||||
| #wtoggle>span { | ||||
| #wzip { | ||||
| 	display: none; | ||||
| 	margin-right: .3em; | ||||
| 	padding-right: .3em; | ||||
| 	border-right: .1em solid #555; | ||||
| } | ||||
| #wtoggle, | ||||
| #wtoggle * { | ||||
| 	line-height: 1em; | ||||
| } | ||||
| #wtoggle.sel { | ||||
| 	width: 4.27em; | ||||
| 	width: 6em; | ||||
| } | ||||
| #wtoggle.sel>span { | ||||
| #wtoggle.sel #wzip { | ||||
| 	display: inline-block; | ||||
| 	line-height: 0; | ||||
| } | ||||
| #wtoggle.sel>span a { | ||||
| #wtoggle.sel #wzip a { | ||||
| 	font-size: .4em; | ||||
| 	margin: -.3em 0; | ||||
| 	padding: 0 .3em; | ||||
| 	margin: -.3em .2em; | ||||
| 	position: relative; | ||||
| 	display: inline-block; | ||||
| } | ||||
| #wtoggle.sel>span #selzip { | ||||
| #wtoggle.sel #wzip #selzip { | ||||
| 	top: -.6em; | ||||
| 	padding: .4em .3em; | ||||
| } | ||||
| #barpos, | ||||
| #barbuf { | ||||
| @@ -487,7 +495,7 @@ input[type="checkbox"]:checked+label { | ||||
| } | ||||
| #tree { | ||||
| 	display: none; | ||||
| 	position: fixed; | ||||
| 	position: absolute; | ||||
| 	left: 0; | ||||
| 	bottom: 0; | ||||
| 	top: 7em; | ||||
| @@ -622,7 +630,8 @@ input[type="checkbox"]:checked+label { | ||||
| #files td.min a { | ||||
| 	display: none; | ||||
| } | ||||
| #files tr.play td { | ||||
| #files tr.play td, | ||||
| #files tr.play div a { | ||||
| 	background: #fc4; | ||||
| 	border-color: transparent; | ||||
| 	color: #400; | ||||
|   | ||||
| @@ -113,12 +113,12 @@ | ||||
|  | ||||
|     <div id="widget"> | ||||
|         <div id="wtoggle"> | ||||
|             <span> | ||||
|             <span id="wzip"> | ||||
|                 <a href="#" id="selall">sel.<br />all</a> | ||||
|                 <a href="#" id="selinv">sel.<br />inv.</a> | ||||
|                 <a href="#" id="selzip">zip</a> | ||||
|             </span> | ||||
|             ♫ | ||||
|             <a href="#" id="wtico">♫</a> | ||||
|         </div> | ||||
|         <div id="widgeti"> | ||||
|             <div id="pctl"><a href="#" id="bprev">⏮</a><a href="#" id="bplay">▶</a><a href="#" id="bnext">⏭</a></div> | ||||
|   | ||||
| @@ -75,7 +75,7 @@ makeSortable(ebi('files'), mp.read_order.bind(mp)); | ||||
| var widget = (function () { | ||||
| 	var ret = {}; | ||||
| 	var widget = ebi('widget'); | ||||
| 	var wtoggle = ebi('wtoggle'); | ||||
| 	var wtico = ebi('wtico'); | ||||
| 	var touchmode = false; | ||||
| 	var side_open = false; | ||||
| 	var was_paused = true; | ||||
| @@ -113,14 +113,7 @@ var widget = (function () { | ||||
|  | ||||
| 		return false; | ||||
| 	}; | ||||
| 	if (window.Touch) { | ||||
| 		var touch_handler = function (e) { | ||||
| 			touchmode = true; | ||||
| 			return ret.toggle(e); | ||||
| 		}; | ||||
| 		wtoggle.addEventListener('touchstart', touch_handler, false); | ||||
| 	} | ||||
| 	wtoggle.onclick = click_handler; | ||||
| 	wtico.onclick = click_handler; | ||||
| 	return ret; | ||||
| })(); | ||||
|  | ||||
| @@ -324,7 +317,7 @@ function seek_au_sec(seek) { | ||||
| 	if (mp.au === mp.au_native) | ||||
| 		// hack: ogv.js breaks on .play() during playback | ||||
| 		mp.au.play(); | ||||
| }; | ||||
| } | ||||
|  | ||||
|  | ||||
| function song_skip(n) { | ||||
| @@ -336,7 +329,7 @@ function song_skip(n) { | ||||
| 		play(mp.order.indexOf(tid) + n); | ||||
| 	else | ||||
| 		play(mp.order[0]); | ||||
| }; | ||||
| } | ||||
|  | ||||
|  | ||||
| // hook up the widget buttons | ||||
| @@ -500,7 +493,7 @@ function play(tid, call_depth) { | ||||
| 	setclass(oid, 'play act'); | ||||
| 	var trs = ebi('files').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); | ||||
| 	for (var a = 0, aa = trs.length; a < aa; a++) { | ||||
| 		trs[a].className = trs[a].className.replace(/ *play */, ""); | ||||
| 		clmod(trs[a], 'play'); | ||||
| 	} | ||||
| 	ebi(oid).parentElement.parentElement.className += ' play'; | ||||
|  | ||||
| @@ -625,13 +618,16 @@ function tree_neigh(n) { | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	a += n; | ||||
| 	if (a < 0) | ||||
| 		a = links.length - 1; | ||||
| 	if (a >= links.length) | ||||
| 		a = 0; | ||||
| 	if (act == -1) | ||||
| 		return; | ||||
|  | ||||
| 	links[a].click(); | ||||
| 	act += n; | ||||
| 	if (act < 0) | ||||
| 		act = links.length - 1; | ||||
| 	if (act >= links.length) | ||||
| 		act = 0; | ||||
|  | ||||
| 	links[act].click(); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -649,10 +645,13 @@ function tree_up() { | ||||
|  | ||||
|  | ||||
| document.onkeydown = function (e) { | ||||
| 	if (document.activeElement != document.body && document.activeElement.nodeName.toLowerCase() != 'a') | ||||
| 	if (!document.activeElement || document.activeElement != document.body && document.activeElement.nodeName.toLowerCase() != 'a') | ||||
| 		return; | ||||
|  | ||||
| 	var k = e.code, pos = -1; | ||||
| 	if (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey || e.isComposing) | ||||
| 		return; | ||||
|  | ||||
| 	var k = (e.code + ''), pos = -1; | ||||
| 	if (k.indexOf('Digit') === 0) | ||||
| 		pos = parseInt(k.slice(-1)) * 0.1; | ||||
|  | ||||
| @@ -772,6 +771,7 @@ document.onkeydown = function (e) { | ||||
| 		// ebi('srch_q').textContent = JSON.stringify(params, null, 4); | ||||
| 		var xhr = new XMLHttpRequest(); | ||||
| 		xhr.open('POST', '/?srch', true); | ||||
| 		xhr.setRequestHeader('Content-Type', 'text/plain'); | ||||
| 		xhr.onreadystatechange = xhr_search_results; | ||||
| 		xhr.ts = new Date().getTime(); | ||||
| 		xhr.send(JSON.stringify(params)); | ||||
| @@ -869,6 +869,7 @@ document.onkeydown = function (e) { | ||||
| 		oldcfg = []; | ||||
| 		ebi('files').innerHTML = orig_html; | ||||
| 		orig_html = null; | ||||
| 		msel.render(); | ||||
| 		reload_browser(); | ||||
| 	} | ||||
| })(); | ||||
| @@ -877,12 +878,16 @@ document.onkeydown = function (e) { | ||||
| var treectl = (function () { | ||||
| 	var treectl = { | ||||
| 		"hidden": false | ||||
| 	}; | ||||
| 	var dyn = bcfg_get('dyntree', true); | ||||
| 	var treesz = icfg_get('treesz', 16); | ||||
| 	}, | ||||
| 		entreed = false, | ||||
| 		fixedpos = false, | ||||
| 		prev_atop = null, | ||||
| 		prev_winh = null, | ||||
| 		dyn = bcfg_get('dyntree', true), | ||||
| 		treesz = icfg_get('treesz', 16); | ||||
|  | ||||
| 	treesz = Math.min(Math.max(treesz, 4), 50); | ||||
| 	console.log('treesz [' + treesz + ']'); | ||||
| 	var entreed = false; | ||||
|  | ||||
| 	function entree(e) { | ||||
| 		ev(e); | ||||
| @@ -914,13 +919,43 @@ var treectl = (function () { | ||||
| 		if (!entreed || treectl.hidden) | ||||
| 			return; | ||||
|  | ||||
| 		var top = ebi('wrap').getBoundingClientRect().top; | ||||
| 		ebi('tree').style.top = Math.max(0, parseInt(top)) + 'px'; | ||||
| 		var tree = ebi('tree'), | ||||
| 			wrap = ebi('wrap'), | ||||
| 			atop = wrap.getBoundingClientRect().top, | ||||
| 			winh = window.innerHeight; | ||||
|  | ||||
| 		if (atop === prev_atop && winh === prev_winh) | ||||
| 			return; | ||||
|  | ||||
| 		prev_atop = atop; | ||||
| 		prev_winh = winh; | ||||
|  | ||||
| 		if (fixedpos && atop >= 0) { | ||||
| 			tree.style.position = 'absolute'; | ||||
| 			tree.style.bottom = ''; | ||||
| 			fixedpos = false; | ||||
| 		} | ||||
| 		else if (!fixedpos && atop < 0) { | ||||
| 			tree.style.position = 'fixed'; | ||||
| 			tree.style.height = 'auto'; | ||||
| 			fixedpos = true; | ||||
| 		} | ||||
|  | ||||
| 		if (fixedpos) { | ||||
| 			tree.style.top = Math.max(0, parseInt(atop)) + 'px'; | ||||
| 		} | ||||
| 		else { | ||||
| 			var top = Math.max(0, parseInt(wrap.offsetTop)), | ||||
| 				treeh = (winh - atop) - 4; | ||||
|  | ||||
| 			tree.style.top = top + 'px'; | ||||
| 			tree.style.height = treeh < 10 ? '' : treeh + 'px'; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	function periodic() { | ||||
| 		onscroll(); | ||||
| 		setTimeout(periodic, document.visibilityState ? 200 : 5000); | ||||
| 		setTimeout(periodic, document.visibilityState ? 100 : 5000); | ||||
| 	} | ||||
| 	periodic(); | ||||
|  | ||||
| @@ -997,8 +1032,6 @@ var treectl = (function () { | ||||
| 					var o = links[a].parentNode; | ||||
| 					if (!o.getElementsByTagName('li').length) | ||||
| 						o.innerHTML = html; | ||||
| 					//else | ||||
| 					//	links[a].previousSibling.textContent = '-'; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @@ -1282,7 +1315,7 @@ function find_file_col(txt) { | ||||
|  | ||||
| function mk_files_header(taglist) { | ||||
| 	var html = [ | ||||
| 		'<thead>', | ||||
| 		'<thead><tr>', | ||||
| 		'<th name="lead"><span>c</span></th>', | ||||
| 		'<th name="href"><span>File Name</span></th>', | ||||
| 		'<th name="sz" sort="int"><span>Size</span></th>' | ||||
| @@ -1301,7 +1334,7 @@ function mk_files_header(taglist) { | ||||
| 	html = html.concat([ | ||||
| 		'<th name="ext"><span>T</span></th>', | ||||
| 		'<th name="ts"><span>Date</span></th>', | ||||
| 		'</thead>', | ||||
| 		'</tr></thead>', | ||||
| 	]); | ||||
| 	return html; | ||||
| } | ||||
| @@ -1335,13 +1368,13 @@ var filecols = (function () { | ||||
| 				continue; | ||||
|  | ||||
| 			var name = span[0].textContent, | ||||
| 				cls = ''; | ||||
| 				cls = false; | ||||
|  | ||||
| 			if (has(hidden, name)) { | ||||
| 				ohidden.push(a); | ||||
| 				cls = ' min'; | ||||
| 				cls = true; | ||||
| 			} | ||||
| 			ths[a].className = ths[a].className.replace(/ *min */, " ") + cls; | ||||
| 			clmod(ths[a], 'min', cls) | ||||
| 		} | ||||
| 		for (var a = 0; a < ncols; a++) { | ||||
| 			var cls = has(ohidden, a) ? 'min' : ''; | ||||
| @@ -1512,12 +1545,12 @@ var mukey = (function () { | ||||
|  | ||||
| function addcrc() { | ||||
| 	var links = document.querySelectorAll( | ||||
| 		'#files>tbody>tr>td:nth-child(2)>' + ( | ||||
| 		'#files>tbody>tr>td:first-child+td>' + ( | ||||
| 			ebi('unsearch') ? 'div>a:last-child' : 'a')); | ||||
|  | ||||
| 	for (var a = 0, aa = links.length; a < aa; a++) | ||||
| 		if (!links[a].getAttribute('id')) | ||||
| 			links[a].setAttribute('id', 'f-' + crc32(links[a].textContent)); | ||||
| 			links[a].setAttribute('id', 'f-' + crc32(links[a].textContent || links[a].innerText)); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -1620,27 +1653,26 @@ var msel = (function () { | ||||
| 		return names; | ||||
| 	} | ||||
| 	function selui() { | ||||
| 		var fun = getsel().length ? "add" : "remove"; | ||||
| 		ebi('wtoggle').classList[fun]('sel'); | ||||
| 		clmod(ebi('wtoggle'), 'sel', getsel().length); | ||||
| 	} | ||||
| 	function seltgl(e) { | ||||
| 		ev(e); | ||||
| 		var tr = this.parentNode; | ||||
| 		tr.classList.toggle('sel'); | ||||
| 		clmod(tr, 'sel', 't'); | ||||
| 		selui(); | ||||
| 	} | ||||
| 	function evsel(e, fun) { | ||||
| 		ev(e); | ||||
| 		var trs = document.querySelectorAll('#files tbody tr'); | ||||
| 		for (var a = 0, aa = trs.length; a < aa; a++) | ||||
| 			trs[a].classList[fun]('sel'); | ||||
| 			clmod(trs[a], 'sel', fun); | ||||
| 		selui(); | ||||
| 	} | ||||
| 	ebi('selall').onclick = function (e) { | ||||
| 		evsel(e, "add"); | ||||
| 	}; | ||||
| 	ebi('selinv').onclick = function (e) { | ||||
| 		evsel(e, "toggle"); | ||||
| 		evsel(e, "t"); | ||||
| 	}; | ||||
| 	ebi('selzip').onclick = function (e) { | ||||
| 		ev(e); | ||||
|   | ||||
							
								
								
									
										55
									
								
								copyparty/web/browser2.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								copyparty/web/browser2.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|  | ||||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <title>{{ title }}</title> | ||||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=0.8"> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|     {%- if srv_info %} | ||||
|     <p><span>{{ srv_info }}</span></p> | ||||
|     {%- endif %} | ||||
|  | ||||
|     {%- if have_b_u %} | ||||
|     <form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}"> | ||||
|         <input type="hidden" name="act" value="bput" /> | ||||
|         <input type="file" name="f" multiple /><br /> | ||||
|         <input type="submit" value="start upload" /> | ||||
|     </form> | ||||
|     <br /> | ||||
|     {%- endif %} | ||||
|  | ||||
|     {%- if logues[0] %} | ||||
|     <div>{{ logues[0] }}</div><br /> | ||||
|     {%- endif %} | ||||
|  | ||||
|     <table id="files"> | ||||
|         <thead> | ||||
|             <tr> | ||||
|                 <th name="lead"><span>c</span></th> | ||||
|                 <th name="href"><span>File Name</span></th> | ||||
|                 <th name="sz" sort="int"><span>Size</span></th> | ||||
|                 <th name="ts"><span>Date</span></th> | ||||
|             </tr> | ||||
|         </thead> | ||||
|         <tbody> | ||||
|             <tr><td></td><td><a href="../{{ url_suf }}">parent folder</a></td><td>-</td><td>-</td></tr> | ||||
|  | ||||
| {%- for f in files %} | ||||
|     <tr><td>{{ f.lead }}</td><td><a href="{{ f.href }}{{ url_suf }}">{{ f.name|e }}</a></td><td>{{ f.sz }}</td><td>{{ f.dt }}</td></tr> | ||||
| {%- endfor %} | ||||
|  | ||||
|         </tbody> | ||||
|     </table> | ||||
|      | ||||
|     {%- if logues[1] %} | ||||
|     <div>{{ logues[1] }}</div><br /> | ||||
|     {%- endif %} | ||||
|      | ||||
|     <h2><a href="{{ url_suf }}&h">control-panel</a></h2> | ||||
|  | ||||
| </body> | ||||
| </html> | ||||
| @@ -16,20 +16,20 @@ | ||||
|         <h1>you can browse these:</h1> | ||||
|         <ul> | ||||
|             {% for mp in rvol %} | ||||
|             <li><a href="/{{ mp }}">/{{ mp }}</a></li> | ||||
|             <li><a href="/{{ mp }}{{ url_suf }}">/{{ mp }}</a></li> | ||||
|             {% endfor %} | ||||
|         </ul> | ||||
|  | ||||
|         <h1>you can upload to:</h1> | ||||
|         <ul> | ||||
|             {% for mp in wvol %} | ||||
|             <li><a href="/{{ mp }}">/{{ mp }}</a></li> | ||||
|             <li><a href="/{{ mp }}{{ url_suf }}">/{{ mp }}</a></li> | ||||
|             {% endfor %} | ||||
|         </ul> | ||||
|  | ||||
|         <h1>login for more:</h1> | ||||
|         <ul> | ||||
|             <form method="post" enctype="multipart/form-data" action="/"> | ||||
|             <form method="post" enctype="multipart/form-data" action="/{{ url_suf }}"> | ||||
|                 <input type="hidden" name="act" value="login" /> | ||||
|                 <input type="password" name="cppwd" /> | ||||
|                 <input type="submit" value="Login" /> | ||||
|   | ||||
| @@ -31,6 +31,7 @@ catch (ex) { | ||||
|     } | ||||
|     catch (ex) { } | ||||
| } | ||||
| treectl.onscroll(); | ||||
|  | ||||
|  | ||||
| function up2k_flagbus() { | ||||
| @@ -374,7 +375,7 @@ function up2k_init(have_crypto) { | ||||
|             for (var a = 0, aa = Math.min(20, bad_files.length); a < aa; a++) | ||||
|                 msg += '-- ' + bad_files[a] + '\n'; | ||||
|  | ||||
|             if (files.length - bad_files.length <= 1 && /(android)/i.test(navigator.userAgent)) | ||||
|             if (good_files.length - bad_files.length <= 1 && /(android)/i.test(navigator.userAgent)) | ||||
|                 msg += '\nFirefox-Android has a bug which prevents selecting multiple files. Try selecting one file at a time. For more info, see firefox bug 1456557'; | ||||
|  | ||||
|             alert(msg); | ||||
| @@ -414,7 +415,8 @@ function up2k_init(have_crypto) { | ||||
|  | ||||
|             var tr = document.createElement('tr'); | ||||
|             tr.innerHTML = '<td id="f{0}n"></td><td id="f{0}t">hashing</td><td id="f{0}p" class="prog"></td>'.format(st.files.length); | ||||
|             tr.getElementsByTagName('td')[0].innerHTML = fsearch ? entry.name : linksplit(esc(entry.purl + entry.name)).join(' '); | ||||
|             tr.getElementsByTagName('td')[0].innerHTML = fsearch ? esc(entry.name) : linksplit( | ||||
|                 esc(uricom_dec(entry.purl)[0] + entry.name)).join(' '); | ||||
|             ebi('u2tab').appendChild(tr); | ||||
|  | ||||
|             st.files.push(entry); | ||||
| @@ -524,7 +526,7 @@ function up2k_init(have_crypto) { | ||||
|  | ||||
|                 if (st.todo.handshake.length > 0 && | ||||
|                     st.busy.handshake.length == 0 && ( | ||||
|                         st.todo.handshake[0].t3 || ( | ||||
|                         st.todo.handshake[0].t4 || ( | ||||
|                             handshakes_permitted() && | ||||
|                             st.busy.upload.length < parallel_uploads | ||||
|                         ) | ||||
| @@ -810,7 +812,7 @@ function up2k_init(have_crypto) { | ||||
|                     else { | ||||
|                         smsg = 'found'; | ||||
|                         var hit = response.hits[0], | ||||
|                             msg = linksplit(hit.rp).join(''), | ||||
|                             msg = linksplit(esc(hit.rp)).join(''), | ||||
|                             tr = unix2iso(hit.ts), | ||||
|                             tu = unix2iso(t.lmod), | ||||
|                             diff = parseInt(t.lmod) - parseInt(hit.ts), | ||||
| @@ -868,27 +870,32 @@ function up2k_init(have_crypto) { | ||||
|                     t.done = true; | ||||
|                     st.bytes.uploaded += t.size - t.bytes_uploaded; | ||||
|                     var spd1 = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.); | ||||
|                     var spd2 = (t.size / ((t.t3 - t.t2) / 1000.)) / (1024 * 1024.); | ||||
|                     var spd2 = (t.size / ((t.t4 - t.t3) / 1000.)) / (1024 * 1024.); | ||||
|                     ebi('f{0}p'.format(t.n)).innerHTML = 'hash {0}, up {1} MB/s'.format( | ||||
|                         spd1.toFixed(2), spd2.toFixed(2)); | ||||
|                 } | ||||
|                 else t.t3 = undefined; | ||||
|                 else t.t4 = undefined; | ||||
|  | ||||
|                 tasker(); | ||||
|             } | ||||
|             else { | ||||
|                 var err = ""; | ||||
|                 var rsp = (xhr.responseText + ''); | ||||
|                 var err = "", | ||||
|                     rsp = (xhr.responseText + ''), | ||||
|                     ofs = rsp.lastIndexOf('\nURL: '); | ||||
|  | ||||
|                 if (ofs !== -1) | ||||
|                     rsp = rsp.slice(0, ofs); | ||||
|  | ||||
|                 if (rsp.indexOf('<pre>') === 0) | ||||
|                     rsp = rsp.slice(5); | ||||
|  | ||||
|                 st.bytes.uploaded += t.size; | ||||
|                 if (rsp.indexOf('partial upload exists') !== -1 || | ||||
|                     rsp.indexOf('file already exists') !== -1) { | ||||
|                     err = rsp; | ||||
|                     var ofs = err.lastIndexOf(' : '); | ||||
|                     if (ofs > 0) | ||||
|                         err = err.slice(0, ofs); | ||||
|  | ||||
|                     ofs = err.indexOf('\n/'); | ||||
|                     if (ofs !== -1) { | ||||
|                         err = err.slice(0, ofs + 1) + linksplit(err.slice(ofs + 2, -1)).join(' '); | ||||
|                         err = err.slice(0, ofs + 1) + linksplit(err.slice(ofs + 2)).join(' '); | ||||
|                     } | ||||
|                 } | ||||
|                 if (err != "") { | ||||
| @@ -961,7 +968,7 @@ function up2k_init(have_crypto) { | ||||
|                     st.busy.upload.splice(st.busy.upload.indexOf(upt), 1); | ||||
|                     t.postlist.splice(t.postlist.indexOf(npart), 1); | ||||
|                     if (t.postlist.length == 0) { | ||||
|                         t.t3 = new Date().getTime(); | ||||
|                         t.t4 = new Date().getTime(); | ||||
|                         ebi('f{0}t'.format(t.n)).innerHTML = 'verifying'; | ||||
|                         st.todo.handshake.unshift(t); | ||||
|                     } | ||||
| @@ -979,9 +986,14 @@ function up2k_init(have_crypto) { | ||||
|             xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart]); | ||||
|             xhr.setRequestHeader("X-Up2k-Wark", t.wark); | ||||
|             xhr.setRequestHeader('Content-Type', 'application/octet-stream'); | ||||
|             xhr.overrideMimeType('Content-Type', 'application/octet-stream'); | ||||
|             if (xhr.overrideMimeType) | ||||
|                 xhr.overrideMimeType('Content-Type', 'application/octet-stream'); | ||||
|  | ||||
|             xhr.responseType = 'text'; | ||||
|             xhr.send(e.target.result); | ||||
|  | ||||
|             if (!t.t3) | ||||
|                 t.t3 = new Date().getTime(); | ||||
|         }; | ||||
|  | ||||
|         reader.readAsArrayBuffer(bobslice.call(t.fobj, car, cdr)); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
|  | ||||
|     <div id="op_bup" class="opview opbox act"> | ||||
|         <div id="u2err"></div> | ||||
|         <form method="post" enctype="multipart/form-data" accept-charset="utf-8"> | ||||
|         <form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}"> | ||||
|             <input type="hidden" name="act" value="bput" /> | ||||
|             <input type="file" name="f" multiple><br /> | ||||
|             <input type="submit" value="start upload"> | ||||
| @@ -9,7 +9,7 @@ | ||||
|     </div> | ||||
|  | ||||
|     <div id="op_mkdir" class="opview opbox act"> | ||||
|         <form method="post" enctype="multipart/form-data" accept-charset="utf-8"> | ||||
|         <form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}"> | ||||
|             <input type="hidden" name="act" value="mkdir" /> | ||||
|             <input type="text" name="name" size="30"> | ||||
|             <input type="submit" value="mkdir"> | ||||
| @@ -17,15 +17,15 @@ | ||||
|     </div> | ||||
|  | ||||
|     <div id="op_new_md" class="opview opbox"> | ||||
|         <form method="post" enctype="multipart/form-data" accept-charset="utf-8"> | ||||
|         <form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}"> | ||||
|             <input type="hidden" name="act" value="new_md" /> | ||||
|             <input type="text" name="name" size="30"> | ||||
|             <input type="submit" value="create doc"> | ||||
|         </form> | ||||
|     </div> | ||||
|  | ||||
|     <div id="op_msg" class="opview opbox"> | ||||
|         <form method="post" enctype="application/x-www-form-urlencoded" accept-charset="utf-8"> | ||||
|     <div id="op_msg" class="opview opbox act"> | ||||
|         <form method="post" enctype="application/x-www-form-urlencoded" accept-charset="utf-8" action="{{ url_suf }}"> | ||||
|             <input type="text" name="msg" size="30"> | ||||
|             <input type="submit" value="send msg"> | ||||
|         </form> | ||||
|   | ||||
| @@ -1,5 +1,14 @@ | ||||
| "use strict"; | ||||
|  | ||||
| if (!window['console']) | ||||
|     window['console'] = { | ||||
|         "log": function (msg) { } | ||||
|     }; | ||||
|  | ||||
|  | ||||
| var clickev = window.Touch ? 'touchstart' : 'click'; | ||||
|  | ||||
|  | ||||
| // error handler for mobile devices | ||||
| function hcroak(msg) { | ||||
|     document.body.innerHTML = msg; | ||||
| @@ -110,7 +119,16 @@ function crc32(str) { | ||||
|         crc = (crc >>> 8) ^ crctab[(crc ^ str.charCodeAt(i)) & 0xFF]; | ||||
|     } | ||||
|     return ((crc ^ (-1)) >>> 0).toString(16); | ||||
| }; | ||||
| } | ||||
|  | ||||
|  | ||||
| function clmod(obj, cls, add) { | ||||
|     var re = new RegExp('\\s*\\b' + cls + '\\s*\\b', 'g'); | ||||
|     if (add == 't') | ||||
|         add = !re.test(obj.className); | ||||
|  | ||||
|     obj.className = obj.className.replace(re, ' ') + (add ? ' ' + cls : ''); | ||||
| } | ||||
|  | ||||
|  | ||||
| function sortfiles(nodes) { | ||||
| @@ -149,7 +167,7 @@ function sortfiles(nodes) { | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var onodes = nodes.map((x) => x); | ||||
|             var onodes = nodes.map(function (x) { return x; }); | ||||
|             nodes.sort(function (n1, n2) { | ||||
|                 var v1 = n1._sv, | ||||
|                     v2 = n2._sv; | ||||
| @@ -280,16 +298,16 @@ function opclick(e) { | ||||
| function goto(dest) { | ||||
|     var obj = document.querySelectorAll('.opview.act'); | ||||
|     for (var a = obj.length - 1; a >= 0; a--) | ||||
|         obj[a].classList.remove('act'); | ||||
|         clmod(obj[a], 'act'); | ||||
|  | ||||
|     obj = document.querySelectorAll('#ops>a'); | ||||
|     for (var a = obj.length - 1; a >= 0; a--) | ||||
|         obj[a].classList.remove('act'); | ||||
|         clmod(obj[a], 'act'); | ||||
|  | ||||
|     if (dest) { | ||||
|         var ui = ebi('op_' + dest); | ||||
|         ui.classList.add('act'); | ||||
|         document.querySelector('#ops>a[data-dest=' + dest + ']').classList.add('act'); | ||||
|         clmod(ui, 'act', true); | ||||
|         document.querySelector('#ops>a[data-dest=' + dest + ']').className += " act"; | ||||
|  | ||||
|         var fn = window['goto_' + dest]; | ||||
|         if (fn) | ||||
| @@ -476,8 +494,7 @@ function bcfg_upd_ui(name, val) { | ||||
|     if (o.getAttribute('type') == 'checkbox') | ||||
|         o.checked = val; | ||||
|     else if (o) { | ||||
|         var fun = val ? 'add' : 'remove'; | ||||
|         o.classList[fun]('on'); | ||||
|         clmod(o, 'on', val); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -73,6 +73,13 @@ shab64() { sp=$1; f="$2"; v=0; sz=$(stat -c%s "$f"); while true; do w=$((v+sp*10 | ||||
| command -v gdate && date() { gdate "$@"; }; while true; do t=$(date +%s.%N); (time wget http://127.0.0.1:3923/?ls -qO- | jq -C '.files[]|{sz:.sz,ta:.tags.artist,tb:.tags.".bpm"}|del(.[]|select(.==null))' | awk -F\" '/"/{t[$2]++} END {for (k in t){v=t[k];p=sprintf("%" (v+1) "s",v);gsub(/ /,"#",p);printf "\033[36m%s\033[33m%s   ",k,p}}') 2>&1 | awk -v ts=$t 'NR==1{t1=$0} NR==2{sub(/.*0m/,"");sub(/s$/,"");t2=$0;c=2; if(t2>0.3){c=3} if(t2>0.8){c=1} } END{sub(/[0-9]{6}$/,"",ts);printf "%s   \033[3%dm%s   %s\033[0m\n",ts,c,t2,t1}'; sleep 0.1 || break; done | ||||
|  | ||||
|  | ||||
| ## | ||||
| ## js oneliners | ||||
|  | ||||
| # get all up2k search result URLs | ||||
| var t=[]; var b=document.location.href.split('#')[0].slice(0, -1); document.querySelectorAll('#u2tab .prog a').forEach((x) => {t.push(b+encodeURI(x.getAttribute("href")))}); console.log(t.join("\n")); | ||||
|  | ||||
|  | ||||
| ## | ||||
| ## sqlite3 stuff | ||||
|  | ||||
| @@ -139,6 +146,7 @@ dbg.asyncStore.pendingBreakpoints = {} | ||||
| # fix firefox phantom breakpoints | ||||
| about:config >> devtools.debugger.prefs-schema-version = -1 | ||||
|  | ||||
|  | ||||
| ## | ||||
| ## http 206 | ||||
|  | ||||
|   | ||||
| @@ -45,11 +45,13 @@ pybin=$(command -v python3 || command -v python) || { | ||||
| 	exit 1 | ||||
| } | ||||
|  | ||||
| use_gz= | ||||
| do_sh=1 | ||||
| do_py=1 | ||||
| while [ ! -z "$1" ]; do | ||||
| 	[ "$1" = clean  ] && clean=1  && shift && continue | ||||
| 	[ "$1" = re     ] && repack=1 && shift && continue | ||||
| 	[ "$1" = gz     ] && use_gz=1 && shift && continue | ||||
| 	[ "$1" = no-ogv ] && no_ogv=1 && shift && continue | ||||
| 	[ "$1" = no-cm  ] && no_cm=1  && shift && continue | ||||
| 	[ "$1" = no-sh  ] && do_sh=   && shift && continue | ||||
| @@ -204,16 +206,20 @@ args=(--owner=1000 --group=1000) | ||||
|  | ||||
| tar -cf tar "${args[@]}" --numeric-owner copyparty dep-j2 | ||||
|  | ||||
| pc=bzip2 | ||||
| pe=bz2 | ||||
| [ $use_gz ] && pc=gzip && pe=gz | ||||
|  | ||||
| echo compressing tar | ||||
| # detect best level; bzip2 -7 is usually better than -9 | ||||
| [ $do_py ] && { for n in {2..9}; do cp tar t.$n; bzip2 -$n t.$n & done; wait; mv -v $(ls -1S t.*.bz2 | tail -n 1) tar.bz2; } | ||||
| [ $do_sh ] && { for n in {2..9}; do cp tar t.$n;  xz -ze$n t.$n & done; wait; mv -v $(ls -1S t.*.xz  | tail -n 1) tar.xz; } | ||||
| [ $do_py ] && { for n in {2..9}; do cp tar t.$n; $pc  -$n t.$n & done; wait; mv -v $(ls -1S t.*.$pe | tail -n 1) tar.bz2; } | ||||
| [ $do_sh ] && { for n in {2..9}; do cp tar t.$n; xz -ze$n t.$n & done; wait; mv -v $(ls -1S t.*.xz  | tail -n 1) tar.xz; } | ||||
| rm t.* || true | ||||
| exts=() | ||||
|  | ||||
|  | ||||
| [ $do_sh ] && { | ||||
| exts+=(sh) | ||||
| exts+=(.sh) | ||||
| echo creating unix sfx | ||||
| ( | ||||
| 	sed "s/PACK_TS/$ts/; s/PACK_HTS/$hts/; s/CPP_VER/$ver/" <../scripts/sfx.sh | | ||||
| @@ -224,17 +230,30 @@ echo creating unix sfx | ||||
|  | ||||
|  | ||||
| [ $do_py ] && { | ||||
| exts+=(py) | ||||
| echo creating generic sfx | ||||
| $pybin ../scripts/sfx.py --sfx-make tar.bz2 $ver $ts | ||||
| mv sfx.out $sfx_out.py | ||||
| chmod 755 $sfx_out.* | ||||
| 	echo creating generic sfx | ||||
|  | ||||
| 	py=../scripts/sfx.py | ||||
| 	suf= | ||||
| 	[ $use_gz ] && { | ||||
| 		sed -r 's/"r:bz2"/"r:gz"/' <$py >$py.t | ||||
| 		py=$py.t | ||||
| 		suf=-gz | ||||
| 	} | ||||
|  | ||||
| 	$pybin $py --sfx-make tar.bz2 $ver $ts | ||||
| 	mv sfx.out $sfx_out$suf.py | ||||
| 	 | ||||
| 	exts+=($suf.py) | ||||
| 	[ $use_gz ] && | ||||
| 		rm $py | ||||
| } | ||||
|  | ||||
|  | ||||
| chmod 755 $sfx_out* | ||||
|  | ||||
| printf "done:\n" | ||||
| for ext in ${exts[@]}; do | ||||
| 	printf "  %s\n" "$(realpath $sfx_out)."$ext | ||||
| 	printf "  %s\n" "$(realpath $sfx_out)"$ext | ||||
| done | ||||
|  | ||||
| # apk add bash python3 tar xz bzip2 | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| # coding: latin-1 | ||||
| from __future__ import print_function, unicode_literals | ||||
|  | ||||
| import os, sys, time, shutil, runpy, tarfile, hashlib, platform, tempfile, traceback | ||||
| import os, sys, time, shutil, threading, tarfile, hashlib, platform, tempfile, traceback | ||||
|  | ||||
| """ | ||||
| run me with any version of python, i will unpack and run copyparty | ||||
| @@ -26,6 +26,7 @@ CKSUM = None | ||||
| STAMP = None | ||||
|  | ||||
| PY2 = sys.version_info[0] == 2 | ||||
| WINDOWS = sys.platform == "win32" | ||||
| sys.dont_write_bytecode = True | ||||
| me = os.path.abspath(os.path.realpath(__file__)) | ||||
| cpp = None | ||||
| @@ -343,6 +344,21 @@ def get_payload(): | ||||
|                 break | ||||
|  | ||||
|  | ||||
| def utime(top): | ||||
|     i = 0 | ||||
|     files = [os.path.join(dp, p) for dp, dd, df in os.walk(top) for p in dd + df] | ||||
|     while WINDOWS: | ||||
|         t = int(time.time()) | ||||
|         if i: | ||||
|             msg("utime {}, {}".format(i, t)) | ||||
|  | ||||
|         for f in files: | ||||
|             os.utime(f, (t, t)) | ||||
|  | ||||
|         i += 1 | ||||
|         time.sleep(78123) | ||||
|  | ||||
|  | ||||
| def confirm(rv): | ||||
|     msg() | ||||
|     msg(traceback.format_exc()) | ||||
| @@ -362,15 +378,20 @@ def run(tmp, j2ver): | ||||
|     msg("sfxdir:", tmp) | ||||
|     msg() | ||||
|  | ||||
|     # "systemd-tmpfiles-clean.timer"?? HOW do you even come up with this shit | ||||
|     # block systemd-tmpfiles-clean.timer | ||||
|     try: | ||||
|         import fcntl | ||||
|  | ||||
|         fd = os.open(tmp, os.O_RDONLY) | ||||
|         fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) | ||||
|         tmp = os.readlink(tmp)  # can't flock a symlink, even with O_NOFOLLOW | ||||
|     except: | ||||
|         pass | ||||
|     except Exception as ex: | ||||
|         if not WINDOWS: | ||||
|             msg("\033[31mflock:", repr(ex)) | ||||
|  | ||||
|     t = threading.Thread(target=utime, args=(tmp,)) | ||||
|     t.daemon = True | ||||
|     t.start() | ||||
|  | ||||
|     ld = [tmp, os.path.join(tmp, "dep-j2")] | ||||
|     if j2ver: | ||||
| @@ -380,7 +401,10 @@ def run(tmp, j2ver): | ||||
|         sys.path.insert(0, x) | ||||
|  | ||||
|     try: | ||||
|         runpy.run_module(str("copyparty"), run_name=str("__main__")) | ||||
|         from copyparty.__main__ import main as copyparty | ||||
|  | ||||
|         copyparty() | ||||
|  | ||||
|     except SystemExit as ex: | ||||
|         if ex.code: | ||||
|             confirm(ex.code) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user