mirror of
				https://github.com/9001/copyparty.git
				synced 2025-10-31 03:53:31 +00:00 
			
		
		
		
	Compare commits
	
		
			54 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 04f1b2cf3a | ||
|  | c06d928bb5 | ||
|  | ab09927e7b | ||
|  | 779437db67 | ||
|  | 28cbdb652e | ||
|  | 2b2415a7d8 | ||
|  | 746a8208aa | ||
|  | a2a041a98a | ||
|  | 10b436e449 | ||
|  | 4d62b34786 | ||
|  | 0546210687 | ||
|  | f8c11faada | ||
|  | 16d6e9be1f | ||
|  | aff8185f2e | ||
|  | 217d15fe81 | ||
|  | 171e93c201 | ||
|  | acc1d2e9e3 | ||
|  | 49c2f37154 | ||
|  | 69e54497aa | ||
|  | 9aa1885669 | ||
|  | 4418508513 | ||
|  | 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 | ||
|  | 83fec3cca7 | ||
|  | 3cefc99b7d | ||
|  | 3a38dcbc05 | ||
|  | 7ff08bce57 | ||
|  | fd490af434 | 
							
								
								
									
										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", | ||||
|   | ||||
							
								
								
									
										35
									
								
								.vscode/launch.py
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								.vscode/launch.py
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| # 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 | ||||
| import shlex | ||||
|  | ||||
| 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"] | ||||
|  | ||||
| try: | ||||
|     sargv = " ".join([shlex.quote(x) for x in argv]) | ||||
|     print(sys.executable + " -m copyparty " + sargv + "\n") | ||||
| except: | ||||
|     pass | ||||
|  | ||||
| argv = [os.path.expanduser(x) if x.startswith("~") else x for x in argv] | ||||
| try: | ||||
|     copyparty(["a"] + 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" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										54
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								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) | ||||
| @@ -97,6 +98,7 @@ summary: it works! you can use it! (but technically not even close to beta) | ||||
| * Windows: python 3.7 and older cannot read tags with ffprobe, so use mutagen or upgrade | ||||
| * Windows: python 2.7 cannot index non-ascii filenames with `-e2d` | ||||
| * Windows: python 2.7 cannot handle filenames with mojibake | ||||
| * hiding the contents at url `/d1/d2/d3` using `-v :d1/d2/d3:cd2d` has the side-effect of creating databases (for files/tags) inside folders d1 and d2, and those databases take precedence over the main db at the top of the vfs - this means all files in d2 and below will be reindexed unless you already had a vfs entry at or below d2 | ||||
| * probably more, pls let me know | ||||
|  | ||||
|  | ||||
| @@ -109,6 +111,8 @@ the browser has the following hotkeys | ||||
| * `I/K` prev/next folder | ||||
| * `P` parent folder | ||||
|  | ||||
| you can link a particular timestamp in an audio file by adding it to the URL, such as `&20` / `&20s` / `&1m20` / `&1:20` after the `.../#af-c8960dab` | ||||
|  | ||||
|  | ||||
| ## zip downloads | ||||
|  | ||||
| @@ -195,6 +199,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) | ||||
| @@ -302,15 +341,20 @@ in the `scripts` folder: | ||||
|  | ||||
| roughly sorted by priority | ||||
|  | ||||
| * 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, 3) | ||||
| VERSION = (0, 10, 13) | ||||
| CODENAME = "zip it" | ||||
| BUILD_DT = (2021, 3, 29) | ||||
| BUILD_DT = (2021, 4, 20) | ||||
|  | ||||
| S_VERSION = ".".join(map(str, VERSION)) | ||||
| S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT) | ||||
|   | ||||
| @@ -111,7 +111,27 @@ class VFS(object): | ||||
|         if rem: | ||||
|             rp += "/" + rem | ||||
|  | ||||
|         return fsdec(os.path.realpath(fsenc(rp))) | ||||
|         try: | ||||
|             return fsdec(os.path.realpath(fsenc(rp))) | ||||
|         except: | ||||
|             if not WINDOWS: | ||||
|                 raise | ||||
|  | ||||
|             # cpython bug introduced in 3.8, still exists in 3.9.1; | ||||
|             # some win7sp1 and win10:20H2 boxes cannot realpath a | ||||
|             # networked drive letter such as b"n:" or b"n:\\" | ||||
|             # | ||||
|             # requirements to trigger: | ||||
|             #  * bytestring (not unicode str) | ||||
|             #  * just the drive letter (subfolders are ok) | ||||
|             #  * networked drive (regular disks and vmhgfs are ok) | ||||
|             #  * on an enterprise network (idk, cannot repro with samba) | ||||
|             # | ||||
|             # hits the following exceptions in succession: | ||||
|             #  * access denied at L601: "path = _getfinalpathname(path)" | ||||
|             #  * "cant concat str to bytes" at L621: "return path + tail" | ||||
|             # | ||||
|             return os.path.realpath(rp) | ||||
|  | ||||
|     def ls(self, rem, uname, scandir, lstat=False): | ||||
|         """return user-readable [fsdir,real,virt] items at vpath""" | ||||
| @@ -168,8 +188,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 +253,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 +509,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") | ||||
|   | ||||
| @@ -101,17 +101,18 @@ class Up2k(object): | ||||
|             thr.daemon = True | ||||
|             thr.start() | ||||
|  | ||||
|             thr = threading.Thread(target=self._tagger) | ||||
|             thr.daemon = True | ||||
|             thr.start() | ||||
|  | ||||
|             thr = threading.Thread(target=self._hasher) | ||||
|             thr.daemon = True | ||||
|             thr.start() | ||||
|  | ||||
|             thr = threading.Thread(target=self._run_all_mtp) | ||||
|             thr.daemon = True | ||||
|             thr.start() | ||||
|             if self.mtag: | ||||
|                 thr = threading.Thread(target=self._tagger) | ||||
|                 thr.daemon = True | ||||
|                 thr.start() | ||||
|  | ||||
|                 thr = threading.Thread(target=self._run_all_mtp) | ||||
|                 thr.daemon = True | ||||
|                 thr.start() | ||||
|  | ||||
|     def log(self, msg, c=0): | ||||
|         self.log_func("up2k", msg + "\033[K", c) | ||||
| @@ -1068,6 +1069,8 @@ class Up2k(object): | ||||
|         with self.mutex: | ||||
|             job = self.registry[ptop].get(wark, None) | ||||
|             if not job: | ||||
|                 known = " ".join([x for x in self.registry[ptop].keys()]) | ||||
|                 self.log("unknown wark [{}], known: {}".format(wark, known)) | ||||
|                 raise Pebkac(400, "unknown wark") | ||||
|  | ||||
|             if chash not in job["need"]: | ||||
|   | ||||
| @@ -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: 6.4em; | ||||
| } | ||||
| #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; | ||||
| @@ -676,3 +685,173 @@ input[type="checkbox"]:checked+label { | ||||
| 	font-family: monospace, monospace; | ||||
| 	line-height: 2em; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| html.light { | ||||
| 	color: #333; | ||||
| 	background: #eee; | ||||
| 	text-shadow: none; | ||||
| } | ||||
| html.light #ops, | ||||
| html.light .opbox, | ||||
| html.light #srch_form { | ||||
| 	background: #f7f7f7; | ||||
| 	box-shadow: 0 0 .3em #ddd; | ||||
| 	border-color: #f7f7f7; | ||||
| } | ||||
| html.light #ops a.act { | ||||
| 	box-shadow: 0 .2em .2em #ccc; | ||||
| 	background: #f7f7f7; | ||||
| 	border-color: #07a; | ||||
| 	padding-top: .4em; | ||||
| } | ||||
| html.light #op_cfg h3 { | ||||
| 	border-color: #ccc; | ||||
| } | ||||
| html.light .tglbtn, | ||||
| html.light #tree > a + a { | ||||
| 	color: #666; | ||||
| 	background: #ddd; | ||||
| 	box-shadow: none; | ||||
| } | ||||
| html.light .tglbtn:hover, | ||||
| html.light #tree > a + a:hover { | ||||
| 	background: #caf; | ||||
| } | ||||
| html.light .tglbtn.on, | ||||
| html.light #tree > a + a.on { | ||||
| 	background: #4a0; | ||||
| 	color: #fff; | ||||
| } | ||||
| html.light #srv_info { | ||||
| 	color: #c83; | ||||
| 	text-shadow: 1px 1px 0 #fff; | ||||
| } | ||||
| html.light #srv_info span { | ||||
| 	color: #000; | ||||
| } | ||||
| html.light #treeul a+a { | ||||
| 	background: inherit; | ||||
| 	color: #06a; | ||||
| } | ||||
| html.light #treeul a.hl { | ||||
| 	background: #07a; | ||||
| 	color: #fff; | ||||
| } | ||||
| html.light #tree li { | ||||
| 	border-color: #ddd #fff #f7f7f7 #fff; | ||||
| } | ||||
| html.light #tree ul { | ||||
| 	border-color: #ccc; | ||||
| } | ||||
| html.light a, | ||||
| html.light #ops a, | ||||
| html.light #files tbody div a:last-child { | ||||
| 	color: #06a; | ||||
| } | ||||
| html.light #files tbody { | ||||
| 	background: #f7f7f7; | ||||
| } | ||||
| html.light #files { | ||||
| 	box-shadow: 0 0 .3em #ccc; | ||||
| } | ||||
| html.light #files thead th { | ||||
| 	background: #eee; | ||||
| } | ||||
| html.light #files tr+tr td { | ||||
| 	border-top: 1px solid #ddd; | ||||
| } | ||||
| html.light #files td { | ||||
| 	border-bottom: 1px solid #f7f7f7; | ||||
| } | ||||
| html.light #files tbody tr:last-child td { | ||||
| 	border-bottom: .2em solid #ccc; | ||||
| } | ||||
| html.light #files td:nth-child(2n) { | ||||
| 	color: #d38; | ||||
| } | ||||
| html.light #files tr:hover td { | ||||
| 	background: #fff; | ||||
| } | ||||
| html.light #files tbody a.play { | ||||
| 	color: #c0f; | ||||
| } | ||||
| html.light tr.play td { | ||||
| 	background: #fc5; | ||||
| } | ||||
| html.light tr.play a { | ||||
| 	color: #406; | ||||
| } | ||||
| html.light #files > thead > tr > th.min span { | ||||
| 	background: linear-gradient(90deg, rgba(68,68,68,0), rgba(68,68,68,0.2) 70%, rgba(68,68,68,0.5)); | ||||
| } | ||||
| html.light #blocked { | ||||
| 	background: #eee; | ||||
| } | ||||
| html.light #blk_play a, | ||||
| html.light #blk_abrt a { | ||||
| 	background: #fff; | ||||
| 	box-shadow: 0 .2em .4em #ddd; | ||||
| } | ||||
| html.light #widget a { | ||||
| 	color: #fc5; | ||||
| } | ||||
| html.light #files tr.sel:hover td { | ||||
| 	background: #c37; | ||||
| } | ||||
| html.light #files tr.sel td { | ||||
| 	color: #fff; | ||||
| } | ||||
| html.light #files tr.sel a { | ||||
| 	color: #fff; | ||||
| } | ||||
| html.light input[type="checkbox"] + label { | ||||
| 	color: #333; | ||||
| } | ||||
| html.light .opview input[type="text"] { | ||||
| 	background: #fff; | ||||
| 	color: #333; | ||||
| 	box-shadow: 0 0 2px #888; | ||||
| 	border-color: #38d; | ||||
| } | ||||
| html.light #ops:hover #opdesc { | ||||
| 	background: #fff; | ||||
| 	box-shadow: 0 .3em 1em #ccc; | ||||
| } | ||||
| html.light #opdesc code { | ||||
| 	background: #060; | ||||
| 	color: #fff; | ||||
| } | ||||
| html.light #u2tab a>span, | ||||
| html.light #files td div span { | ||||
| 	color: #000; | ||||
| } | ||||
| html.light #path { | ||||
| 	background: #f7f7f7; | ||||
| 	text-shadow: none; | ||||
| 	box-shadow: 0 0 .3em #bbb; | ||||
| } | ||||
| html.light #path a { | ||||
| 	color: #333; | ||||
| } | ||||
| html.light #path a:not(:last-child)::after { | ||||
| 	border-color: #ccc; | ||||
| 	background: none; | ||||
| 	border-width: .1em .1em 0 0; | ||||
| 	margin: -.2em .3em -.2em -.3em; | ||||
| } | ||||
| html.light #path a:hover { | ||||
| 	background: none; | ||||
| 	color: #60a; | ||||
| } | ||||
| html.light #files tbody div a { | ||||
| 	color: #d38; | ||||
| } | ||||
| html.light #files a:hover, | ||||
| html.light #files tr.sel a:hover { | ||||
| 	color: #000; | ||||
| 	background: #fff; | ||||
| } | ||||
| @@ -13,8 +13,8 @@ | ||||
| <body> | ||||
|     <div id="ops"> | ||||
|         <a href="#" data-dest="" data-desc="close submenu">---</a> | ||||
|         <a href="#" data-perm="read" data-dest="search" data-desc="search for files by attributes, path/name, music tags, or any combination of those.<br /><br /><code>foo bar</code> = must contain both foo and bar,<br /><code>foo -bar</code> = must contain foo but not bar,<br /><code>^yana .opus$</code> = must start with yana and have the opus extension">🔎</a> | ||||
|         {%- if have_up2k_idx %} | ||||
|         <a href="#" data-perm="read" data-dest="search" data-desc="search for files by attributes, path/name, music tags, or any combination of those.<br /><br /><code>foo bar</code> = must contain both foo and bar,<br /><code>foo -bar</code> = must contain foo but not bar,<br /><code>^yana .opus$</code> = must start with yana and have the opus extension">🔎</a> | ||||
|         <a href="#" data-dest="up2k" data-desc="up2k: upload files (if you have write-access) or toggle into the search-mode and drag files onto the search button to see if they exist somewhere on the server">🚀</a> | ||||
|         {%- else %} | ||||
|         <a href="#" data-perm="write" data-dest="up2k" data-desc="up2k: upload files with resume support (close your browser and drop the same files in later)">🚀</a> | ||||
| @@ -39,14 +39,17 @@ | ||||
|     {%- include 'upload.html' %} | ||||
|  | ||||
|     <div id="op_cfg" class="opview opbox"> | ||||
|         <h3>key notation</h3> | ||||
|         <div id="key_notation"></div> | ||||
|         <h3>switches</h3> | ||||
|         <div> | ||||
|             <a id="tooltips" class="tglbtn" href="#">tooltips</a> | ||||
|             <a id="lightmode" class="tglbtn" href="#">lightmode</a> | ||||
|         </div> | ||||
|         {%- if have_zip %} | ||||
|         <h3>folder download</h3> | ||||
|         <div id="arc_fmt"></div> | ||||
|         {%- endif %} | ||||
|         <h3>tooltips</h3> | ||||
|         <div><a id="tooltips" class="tglbtn" href="#">enable</a></div> | ||||
|         <h3>key notation</h3> | ||||
|         <div id="key_notation"></div> | ||||
|     </div> | ||||
|      | ||||
|     <h1 id="path"> | ||||
| @@ -113,12 +116,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> | ||||
|             ♫ | ||||
|             </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; | ||||
| })(); | ||||
|  | ||||
| @@ -321,10 +314,10 @@ function seek_au_sec(seek) { | ||||
|  | ||||
| 	mp.au.currentTime = seek; | ||||
|  | ||||
| 	// ogv.js breaks on .play() during playback | ||||
| 	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 | ||||
| @@ -434,7 +427,7 @@ catch (ex) { } | ||||
|  | ||||
|  | ||||
| // plays the tid'th audio file on the page | ||||
| function play(tid, call_depth) { | ||||
| function play(tid, seek, call_depth) { | ||||
| 	if (mp.order.length == 0) | ||||
| 		return alert('no audio found wait what'); | ||||
|  | ||||
| @@ -456,7 +449,7 @@ function play(tid, call_depth) { | ||||
| 	} | ||||
|  | ||||
| 	// ogv.js breaks on .play() unless directly user-triggered | ||||
| 	var hack_attempt_play = true; | ||||
| 	var attempt_play = true; | ||||
|  | ||||
| 	var url = mp.tracks[tid]; | ||||
| 	if (need_ogv && /\.(ogg|opus)$/i.test(url)) { | ||||
| @@ -465,7 +458,7 @@ function play(tid, call_depth) { | ||||
| 		} | ||||
| 		else if (window['OGVPlayer']) { | ||||
| 			mp.au = mp.au_ogvjs = new OGVPlayer(); | ||||
| 			hack_attempt_play = false; | ||||
| 			attempt_play = false; | ||||
| 			mp.au.addEventListener('error', evau_error, true); | ||||
| 			mp.au.addEventListener('progress', pbar.drawpos, false); | ||||
| 			widget.open(); | ||||
| @@ -477,7 +470,7 @@ function play(tid, call_depth) { | ||||
| 			show_modal('<h1>loading ogv.js</h1><h2>thanks apple</h2>'); | ||||
|  | ||||
| 			import_js('/.cpr/deps/ogv.js', function () { | ||||
| 				play(tid, 1); | ||||
| 				play(tid, seek, 1); | ||||
| 			}); | ||||
|  | ||||
| 			return; | ||||
| @@ -500,26 +493,31 @@ 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'; | ||||
|  | ||||
| 	try { | ||||
| 		if (hack_attempt_play) | ||||
| 		if (attempt_play) | ||||
| 			mp.au.play(); | ||||
|  | ||||
| 		if (mp.au.paused) | ||||
| 			autoplay_blocked(); | ||||
| 			autoplay_blocked(seek); | ||||
| 		else if (seek) { | ||||
| 			seek_au_sec(seek); | ||||
| 		} | ||||
|  | ||||
| 		var o = ebi(oid); | ||||
| 		o.setAttribute('id', 'thx_js'); | ||||
| 		if (window.history && history.replaceState) { | ||||
| 			hist_replace(document.location.pathname + '#' + oid); | ||||
| 		if (!seek) { | ||||
| 			var o = ebi(oid); | ||||
| 			o.setAttribute('id', 'thx_js'); | ||||
| 			if (window.history && history.replaceState) { | ||||
| 				hist_replace(document.location.pathname + '#' + oid); | ||||
| 			} | ||||
| 			else { | ||||
| 				document.location.hash = oid; | ||||
| 			} | ||||
| 			o.setAttribute('id', oid); | ||||
| 		} | ||||
| 		else { | ||||
| 			document.location.hash = oid; | ||||
| 		} | ||||
| 		o.setAttribute('id', oid); | ||||
|  | ||||
| 		pbar.drawbuf(); | ||||
| 		return true; | ||||
| @@ -583,7 +581,7 @@ function unblocked() { | ||||
|  | ||||
|  | ||||
| // show ui to manually start playback of a linked song | ||||
| function autoplay_blocked() { | ||||
| function autoplay_blocked(seek) { | ||||
| 	show_modal( | ||||
| 		'<div id="blk_play"><a href="#" id="blk_go"></a></div>' + | ||||
| 		'<div id="blk_abrt"><a href="#" id="blk_na">Cancel<br />(show file list)</a></div>'); | ||||
| @@ -599,6 +597,8 @@ function autoplay_blocked() { | ||||
| 		if (e) e.preventDefault(); | ||||
| 		unblocked(); | ||||
| 		mp.au.play(); | ||||
| 		if (seek) | ||||
| 			seek_au_sec(seek); | ||||
| 	}; | ||||
| 	na.onclick = unblocked; | ||||
| } | ||||
| @@ -607,8 +607,20 @@ function autoplay_blocked() { | ||||
| // autoplay linked track | ||||
| (function () { | ||||
| 	var v = location.hash; | ||||
| 	if (v && v.length == 12 && v.indexOf('#af-') === 0) | ||||
| 		play(v.slice(2)); | ||||
| 	if (v && v.indexOf('#af-') === 0) { | ||||
| 		var id = v.slice(2).split('&'); | ||||
| 		if (id[0].length != 10) | ||||
| 			return; | ||||
|  | ||||
| 		if (id.length == 1) | ||||
| 			return play(id[0]); | ||||
|  | ||||
| 		var m = /^[Tt=0]*([0-9]+[Mm:])?0*([0-9]+)[Ss]?$/.exec(id[1]); | ||||
| 		if (!m) | ||||
| 			return play(id[0]); | ||||
|  | ||||
| 		return play(id[0], parseInt(m[1] || 0) * 60 + parseInt(m[2] || 0)); | ||||
| 	} | ||||
| })(); | ||||
|  | ||||
|  | ||||
| @@ -625,13 +637,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 +664,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; | ||||
|  | ||||
| @@ -753,7 +771,7 @@ document.onkeydown = function (e) { | ||||
| 		clearTimeout(search_timeout); | ||||
| 		var now = new Date().getTime(); | ||||
| 		if (now - search_in_progress > 30 * 1000) | ||||
| 			search_timeout = setTimeout(do_search, 100); | ||||
| 			search_timeout = setTimeout(do_search, 200); | ||||
| 	} | ||||
|  | ||||
| 	function do_search() { | ||||
| @@ -772,6 +790,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)); | ||||
| @@ -796,6 +815,8 @@ document.onkeydown = function (e) { | ||||
| 		var res = JSON.parse(this.responseText), | ||||
| 			tagord = res.tag_order; | ||||
|  | ||||
| 		sortfiles(res.hits); | ||||
|  | ||||
| 		var ofiles = ebi('files'); | ||||
| 		if (ofiles.getAttribute('ts') > this.ts) | ||||
| 			return; | ||||
| @@ -814,7 +835,7 @@ document.onkeydown = function (e) { | ||||
|  | ||||
| 		var html = mk_files_header(tagord); | ||||
| 		html.push('<tbody>'); | ||||
| 		html.push('<tr><td>-</td><td colspan="42"><a href="#" id="unsearch">close search results</a></td></tr>'); | ||||
| 		html.push('<tr><td>-</td><td colspan="42"><a href="#" id="unsearch">! close search results</a></td></tr>'); | ||||
| 		for (var a = 0; a < res.hits.length; a++) { | ||||
| 			var r = res.hits[a], | ||||
| 				ts = parseInt(r.ts), | ||||
| @@ -867,6 +888,7 @@ document.onkeydown = function (e) { | ||||
| 		oldcfg = []; | ||||
| 		ebi('files').innerHTML = orig_html; | ||||
| 		orig_html = null; | ||||
| 		msel.render(); | ||||
| 		reload_browser(); | ||||
| 	} | ||||
| })(); | ||||
| @@ -875,12 +897,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); | ||||
| @@ -912,13 +938,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(); | ||||
|  | ||||
| @@ -995,8 +1051,6 @@ var treectl = (function () { | ||||
| 					var o = links[a].parentNode; | ||||
| 					if (!o.getElementsByTagName('li').length) | ||||
| 						o.innerHTML = html; | ||||
| 					//else | ||||
| 					//	links[a].previousSibling.textContent = '-'; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @@ -1085,35 +1139,8 @@ var treectl = (function () { | ||||
| 		} | ||||
|  | ||||
| 		ebi('srv_info').innerHTML = '<span>' + res.srvinf + '</span>'; | ||||
| 		var nodes = res.dirs.concat(res.files), | ||||
| 			sopts = jread('fsort', []); | ||||
|  | ||||
| 		try { | ||||
| 			for (var a = sopts.length - 1; a >= 0; a--) { | ||||
| 				var name = sopts[a][0], rev = sopts[a][1], typ = sopts[a][2]; | ||||
| 				if (!name) | ||||
| 					continue; | ||||
|  | ||||
| 				if (name.indexOf('tags/') == -1) { | ||||
| 					nodes.sort(function (v1, v2) { | ||||
| 						if (!v1[name]) return -1 * rev; | ||||
| 						if (!v2[name]) return 1 * rev; | ||||
| 						return rev * (typ == 'int' ? (v1[name] - v2[name]) : (v1[name].localeCompare(v2[name]))); | ||||
| 					}); | ||||
| 				} | ||||
| 				else { | ||||
| 					name = name.slice(5); | ||||
| 					nodes.sort(function (v1, v2) { | ||||
| 						if (!v1.tags[name]) return -1 * rev; | ||||
| 						if (!v2.tags[name]) return 1 * rev; | ||||
| 						return rev * (typ == 'int' ? (v1.tags[name] - v2.tags[name]) : (v1.tags[name].localeCompare(v2.tags[name]))); | ||||
| 					}); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		catch (ex) { | ||||
| 			console.log("failed to apply sort config: " + ex); | ||||
| 		} | ||||
| 		var nodes = res.dirs.concat(res.files); | ||||
| 		nodes = sortfiles(nodes); | ||||
|  | ||||
| 		var top = this.top; | ||||
| 		var html = mk_files_header(res.taglist); | ||||
| @@ -1307,7 +1334,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>' | ||||
| @@ -1326,7 +1353,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; | ||||
| } | ||||
| @@ -1360,13 +1387,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' : ''; | ||||
| @@ -1483,8 +1510,11 @@ var mukey = (function () { | ||||
| 	} | ||||
|  | ||||
| 	function render() { | ||||
| 		var ci = find_file_col('Key'), | ||||
| 			i = ci[0], | ||||
| 		var ci = find_file_col('Key'); | ||||
| 		if (!ci) | ||||
| 			return; | ||||
|  | ||||
| 		var i = ci[0], | ||||
| 			min = ci[1], | ||||
| 			rows = ebi('files').tBodies[0].rows; | ||||
|  | ||||
| @@ -1533,10 +1563,16 @@ var mukey = (function () { | ||||
|  | ||||
|  | ||||
| function addcrc() { | ||||
| 	var links = document.querySelectorAll('#files>tbody>tr>td:nth-child(2)>a'); | ||||
| 	var links = document.querySelectorAll( | ||||
| 		'#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)); | ||||
| 		if (!links[a].getAttribute('id')) { | ||||
| 			var crc = crc32(links[a].textContent || links[a].innerText); | ||||
| 			crc = ('00000000' + crc).slice(-8); | ||||
| 			links[a].setAttribute('id', 'f-' + crc); | ||||
| 		} | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -1563,6 +1599,24 @@ function addcrc() { | ||||
| })(); | ||||
|  | ||||
|  | ||||
| (function () { | ||||
| 	var light = bcfg_get('lightmode', false); | ||||
|  | ||||
| 	function freshen() { | ||||
| 		document.documentElement.setAttribute("class", light ? "light" : ""); | ||||
| 	} | ||||
|  | ||||
| 	ebi('lightmode').onclick = function (e) { | ||||
| 		ev(e); | ||||
| 		light = !light; | ||||
| 		bcfg_set('lightmode', light); | ||||
| 		freshen(); | ||||
| 	}; | ||||
|  | ||||
| 	freshen(); | ||||
| })(); | ||||
|  | ||||
|  | ||||
| var arcfmt = (function () { | ||||
| 	if (!ebi('arc_fmt')) | ||||
| 		return { "render": function () { } }; | ||||
| @@ -1639,27 +1693,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> | ||||
| @@ -138,10 +138,10 @@ var md_opt = { | ||||
|         document.documentElement.setAttribute("class", dark ? "dark" : ""); | ||||
|         btn.innerHTML = "go " + (dark ? "light" : "dark"); | ||||
|         if (window.localStorage) | ||||
|             localStorage.setItem('darkmode', dark ? 1 : 0); | ||||
|             localStorage.setItem('lightmode', dark ? 0 : 1); | ||||
|     }; | ||||
|     btn.onclick = toggle; | ||||
|     if (window.localStorage && localStorage.getItem('darkmode') == 1) | ||||
|     if (window.localStorage && localStorage.getItem('lightmode') != 1) | ||||
| 		toggle(); | ||||
| })(); | ||||
|  | ||||
|   | ||||
| @@ -31,12 +31,12 @@ var md_opt = { | ||||
|  | ||||
| var lightswitch = (function () { | ||||
| 	var fun = function () { | ||||
| 		var dark = !!!document.documentElement.getAttribute("class"); | ||||
| 		var dark = !document.documentElement.getAttribute("class"); | ||||
| 		document.documentElement.setAttribute("class", dark ? "dark" : ""); | ||||
| 		if (window.localStorage) | ||||
| 			localStorage.setItem('darkmode', dark ? 1 : 0); | ||||
| 			localStorage.setItem('lightmode', dark ? 0 : 1); | ||||
| 	}; | ||||
| 	if (window.localStorage && localStorage.getItem('darkmode') == 1) | ||||
| 	if (window.localStorage && localStorage.getItem('lightmode') != 1) | ||||
| 		fun(); | ||||
| 	 | ||||
| 	return fun; | ||||
|   | ||||
| @@ -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" /> | ||||
| @@ -38,7 +38,7 @@ | ||||
|     </div> | ||||
|     <script> | ||||
|  | ||||
| if (window.localStorage && localStorage.getItem('darkmode') == 1) | ||||
| if (window.localStorage && localStorage.getItem('lightmode') != 1) | ||||
|     document.documentElement.setAttribute("class", "dark"); | ||||
|  | ||||
| </script> | ||||
|   | ||||
| @@ -31,6 +31,7 @@ catch (ex) { | ||||
|     } | ||||
|     catch (ex) { } | ||||
| } | ||||
| treectl.onscroll(); | ||||
|  | ||||
|  | ||||
| function up2k_flagbus() { | ||||
| @@ -131,9 +132,285 @@ function up2k_flagbus() { | ||||
|     return flag; | ||||
| } | ||||
|  | ||||
|  | ||||
| function U2pvis(act, btns) { | ||||
|     this.act = act; | ||||
|     this.ctr = { "ok": 0, "ng": 0, "bz": 0, "q": 0 }; | ||||
|     this.tab = []; | ||||
|     this.head = 0; | ||||
|     this.tail = -1; | ||||
|     this.wsz = 3; | ||||
|  | ||||
|     this.addfile = function (entry, sz) { | ||||
|         this.tab.push({ | ||||
|             "hn": entry[0], | ||||
|             "ht": entry[1], | ||||
|             "hp": entry[2], | ||||
|             "in": 'q', | ||||
|             "nh": 0, //hashed | ||||
|             "nd": 0, //done | ||||
|             "cb": [], // bytes done in chunk | ||||
|             "bt": sz, // bytes total | ||||
|             "bd": 0,  // bytes done | ||||
|             "bd0": 0  // upload start | ||||
|         }); | ||||
|         this.ctr["q"]++; | ||||
|         this.drawcard("q"); | ||||
|         if (this.act == "q") { | ||||
|             this.addrow(this.tab.length - 1); | ||||
|         } | ||||
|         if (this.act == "bz") { | ||||
|             this.bzw(); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     this.is_act = function (card) { | ||||
|         if (this.act == "done") | ||||
|             return card == "ok" || card == "ng"; | ||||
|  | ||||
|         return this.act == card; | ||||
|     } | ||||
|  | ||||
|     this.seth = function (nfile, field, html) { | ||||
|         var fo = this.tab[nfile]; | ||||
|         field = ['hn', 'ht', 'hp'][field]; | ||||
|         if (fo[field] === html) | ||||
|             return; | ||||
|  | ||||
|         fo[field] = html; | ||||
|         if (!this.is_act(fo.in)) | ||||
|             return; | ||||
|  | ||||
|         var obj = ebi('f{0}{1}'.format(nfile, field.slice(1))); | ||||
|         obj.innerHTML = html; | ||||
|         if (field == 'hp') { | ||||
|             obj.style.color = ''; | ||||
|             obj.style.background = ''; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     this.setab = function (nfile, nblocks) { | ||||
|         var t = []; | ||||
|         for (var a = 0; a < nblocks; a++) | ||||
|             t.push(0); | ||||
|  | ||||
|         this.tab[nfile].cb = t; | ||||
|     }; | ||||
|  | ||||
|     this.setat = function (nfile, blocktab) { | ||||
|         this.tab[nfile].cb = blocktab; | ||||
|  | ||||
|         var bd = 0; | ||||
|         for (var a = 0; a < blocktab.length; a++) | ||||
|             bd += blocktab[a]; | ||||
|  | ||||
|         this.tab[nfile].bd = bd; | ||||
|         this.tab[nfile].bd0 = bd; | ||||
|     }; | ||||
|  | ||||
|     this.perc = function (bd, bd0, sz, t0) { | ||||
|         var td = new Date().getTime() - t0, | ||||
|             p = bd * 100.0 / sz, | ||||
|             nb = bd - bd0, | ||||
|             spd = nb / (td / 1000), | ||||
|             eta = (sz - bd) / spd; | ||||
|  | ||||
|         return [p, s2ms(eta), spd / (1024 * 1024)]; | ||||
|     }; | ||||
|  | ||||
|     this.hashed = function (fobj) { | ||||
|         var fo = this.tab[fobj.n]; | ||||
|         var nb = fo.bt * (++fo.nh / fo.cb.length); | ||||
|         var p = this.perc(nb, 0, fobj.size, fobj.t1); | ||||
|         fo.hp = '{0}%, {1}, {2} MB/s'.format( | ||||
|             p[0].toFixed(2), p[1], p[2].toFixed(2) | ||||
|         ); | ||||
|         if (!this.is_act(fo.in)) | ||||
|             return; | ||||
|  | ||||
|         var obj = ebi('f{0}p'.format(fobj.n)); | ||||
|         obj.innerHTML = fo.hp; | ||||
|         obj.style.color = '#fff'; | ||||
|         var o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0]; | ||||
|         obj.style.background = 'linear-gradient(90deg, #025, #06a ' + o1 + '%, #09d ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)'; | ||||
|     }; | ||||
|  | ||||
|     this.prog = function (fobj, nchunk, cbd) { | ||||
|         var fo = this.tab[fobj.n]; | ||||
|         var delta = cbd - fo.cb[nchunk]; | ||||
|         fo.cb[nchunk] = cbd; | ||||
|         fo.bd += delta; | ||||
|  | ||||
|         var p = this.perc(fo.bd, fo.bd0, fo.bt, fobj.t3); | ||||
|         fo.hp = '{0}%, {1}, {2} MB/s'.format( | ||||
|             p[0].toFixed(2), p[1], p[2].toFixed(2) | ||||
|         ); | ||||
|  | ||||
|         if (!this.is_act(fo.in)) | ||||
|             return; | ||||
|  | ||||
|         var obj = ebi('f{0}p'.format(fobj.n)); | ||||
|         obj.innerHTML = fo.hp; | ||||
|         obj.style.color = '#fff'; | ||||
|         var o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0]; | ||||
|         obj.style.background = 'linear-gradient(90deg, #050, #270 ' + o1 + '%, #4b0 ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)'; | ||||
|     }; | ||||
|  | ||||
|     this.move = function (nfile, newcat) { | ||||
|         var fo = this.tab[nfile], | ||||
|             oldcat = fo.in, | ||||
|             bz_act = this.act == "bz"; | ||||
|  | ||||
|         if (oldcat == newcat) { | ||||
|             throw 42; | ||||
|         } | ||||
|  | ||||
|         fo.in = newcat; | ||||
|         this.ctr[oldcat]--; | ||||
|         this.ctr[newcat]++; | ||||
|         this.drawcard(oldcat); | ||||
|         this.drawcard(newcat); | ||||
|         if (this.is_act(newcat)) { | ||||
|             this.tail++; | ||||
|             if (!ebi('f' + nfile)) | ||||
|                 this.addrow(nfile); | ||||
|         } | ||||
|         else if (this.is_act(oldcat)) { | ||||
|             this.head++; | ||||
|             if (!bz_act) { | ||||
|                 var tr = ebi("f" + nfile); | ||||
|                 tr.parentNode.removeChild(tr); | ||||
|             } | ||||
|         } | ||||
|         if (bz_act) { | ||||
|             this.bzw(); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     this.bzw_log = function (first, last) { | ||||
|         console.log("first %d   head %d   tail %d   last %d", first, this.head, this.tail, last); | ||||
|         var trs = document.querySelectorAll('#u2tab>tbody>tr'), msg = []; | ||||
|         for (var a = 0; a < trs.length; a++) | ||||
|             msg.push(trs[a].getAttribute('id')); | ||||
|  | ||||
|         console.log(msg.join(' ')); | ||||
|     } | ||||
|  | ||||
|     this.bzw = function () { | ||||
|         var first = document.querySelector('#u2tab>tbody>tr:first-child'); | ||||
|         if (!first) | ||||
|             return; | ||||
|  | ||||
|         var last = document.querySelector('#u2tab>tbody>tr:last-child'); | ||||
|         first = parseInt(first.getAttribute('id').slice(1)); | ||||
|         last = parseInt(last.getAttribute('id').slice(1)); | ||||
|         //this.bzw_log(first, last); | ||||
|  | ||||
|         while (this.head - first > this.wsz) { | ||||
|             var obj = ebi('f' + (first++)); | ||||
|             obj.parentNode.removeChild(obj); | ||||
|         } | ||||
|         while (last - this.tail < this.wsz && last < this.tab.length - 2) { | ||||
|             var obj = ebi('f' + (++last)); | ||||
|             if (!obj) | ||||
|                 this.addrow(last); | ||||
|         } | ||||
|         //this.bzw_log(first, last); | ||||
|         //console.log('--'); | ||||
|     }; | ||||
|  | ||||
|     this.drawcard = function (cat) { | ||||
|         var cards = document.querySelectorAll('#u2cards>a>span'); | ||||
|  | ||||
|         if (cat == "q") { | ||||
|             cards[4].innerHTML = this.ctr[cat]; | ||||
|             return; | ||||
|         } | ||||
|         if (cat == "bz") { | ||||
|             cards[3].innerHTML = this.ctr[cat]; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         cards[2].innerHTML = this.ctr["ok"] + this.ctr["ng"]; | ||||
|  | ||||
|         if (cat == "ng") { | ||||
|             cards[1].innerHTML = this.ctr[cat]; | ||||
|         } | ||||
|         if (cat == "ok") { | ||||
|             cards[0].innerHTML = this.ctr[cat]; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     this.changecard = function (card) { | ||||
|         this.act = card; | ||||
|         var html = []; | ||||
|         this.head = -1; | ||||
|         this.tail = -1; | ||||
|         for (var a = 0; a < this.tab.length; a++) { | ||||
|             var rt = this.tab[a].in; | ||||
|             if (this.is_act(rt)) { | ||||
|                 html.push(this.genrow(a, true)); | ||||
|  | ||||
|                 this.tail = a; | ||||
|                 if (this.head == -1) | ||||
|                     this.head = a; | ||||
|             } | ||||
|         } | ||||
|         if (this.head == -1) { | ||||
|             this.head = this.tab.length; | ||||
|             this.tail = this.head - 1; | ||||
|         } | ||||
|         if (card == "bz") { | ||||
|             for (var a = this.head - 1; a >= this.head - this.wsz && a >= 0; a--) { | ||||
|                 html.unshift(this.genrow(a, true).replace(/><td>/, "><td>a ")); | ||||
|             } | ||||
|             for (var a = this.tail + 1; a <= this.tail + this.wsz && a < this.tab.length; a++) { | ||||
|                 html.push(this.genrow(a, true).replace(/><td>/, "><td>b ")); | ||||
|             } | ||||
|         } | ||||
|         ebi('u2tab').tBodies[0].innerHTML = html.join('\n'); | ||||
|     }; | ||||
|  | ||||
|     this.genrow = function (nfile, as_html) { | ||||
|         var r = this.tab[nfile], | ||||
|             td1 = '<td id="f' + nfile, | ||||
|             td = '</td>' + td1, | ||||
|             ret = td1 + 'n">' + r.hn + | ||||
|                 td + 't">' + r.ht + | ||||
|                 td + 'p" class="prog">' + r.hp + '</td>'; | ||||
|  | ||||
|         if (as_html) | ||||
|             return '<tr id="f' + nfile + '">' + ret + '</tr>'; | ||||
|  | ||||
|         var obj = document.createElement('tr'); | ||||
|         obj.setAttribute('id', 'f' + nfile); | ||||
|         obj.innerHTML = ret; | ||||
|         return obj; | ||||
|     }; | ||||
|  | ||||
|     this.addrow = function (nfile) { | ||||
|         var tr = this.genrow(nfile); | ||||
|         ebi('u2tab').tBodies[0].appendChild(tr); | ||||
|     }; | ||||
|  | ||||
|     var that = this; | ||||
|     btns = document.querySelectorAll(btns + '>a[act]'); | ||||
|     for (var a = 0; a < btns.length; a++) { | ||||
|         btns[a].onclick = function (e) { | ||||
|             ev(e); | ||||
|             var newtab = this.getAttribute('act'); | ||||
|             for (var b = 0; b < btns.length; b++) { | ||||
|                 btns[b].className = ( | ||||
|                     btns[b].getAttribute('act') == newtab) ? 'act' : ''; | ||||
|             } | ||||
|             that.changecard(newtab); | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| function up2k_init(have_crypto) { | ||||
|     //have_crypto = false; | ||||
|     var need_filereader_cache = undefined; | ||||
|  | ||||
|     // show modal message | ||||
|     function showmodal(msg) { | ||||
| @@ -215,10 +492,6 @@ function up2k_init(have_crypto) { | ||||
|     var flag_en = bcfg_get('flag_en', false); | ||||
|     var fsearch = bcfg_get('fsearch', false); | ||||
|  | ||||
|     var col_hashing = '#00bbff'; | ||||
|     var col_hashed = '#004466'; | ||||
|     var col_uploading = '#ffcc44'; | ||||
|     var col_uploaded = '#00bb00'; | ||||
|     var fdom_ctr = 0; | ||||
|     var st = { | ||||
|         "files": [], | ||||
| @@ -238,6 +511,8 @@ function up2k_init(have_crypto) { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     var pvis = new U2pvis("bz", '#u2cards'); | ||||
|  | ||||
|     var bobslice = null; | ||||
|     if (window.File) | ||||
|         bobslice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; | ||||
| @@ -374,7 +649,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); | ||||
| @@ -412,11 +687,12 @@ function up2k_init(have_crypto) { | ||||
|             if (skip) | ||||
|                 continue; | ||||
|  | ||||
|             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(' '); | ||||
|             ebi('u2tab').appendChild(tr); | ||||
|  | ||||
|             pvis.addfile([ | ||||
|                 fsearch ? esc(entry.name) : linksplit( | ||||
|                     esc(uricom_dec(entry.purl)[0] + entry.name)).join(' '), | ||||
|                 '📐 hash', | ||||
|                 '' | ||||
|             ], fobj.size); | ||||
|             st.files.push(entry); | ||||
|             st.todo.hash.push(entry); | ||||
|         } | ||||
| @@ -437,7 +713,10 @@ function up2k_init(have_crypto) { | ||||
|         for (var a = 0; a < st.files.length; a++) { | ||||
|             var t = st.files[a]; | ||||
|             if (t.done && t.name) { | ||||
|                 var tr = ebi('f{0}p'.format(t.n)).parentNode; | ||||
|                 var tr = ebi('f' + t.n); | ||||
|                 if (!tr) | ||||
|                     continue; | ||||
|  | ||||
|                 tr.parentNode.removeChild(tr); | ||||
|                 t.name = undefined; | ||||
|             } | ||||
| @@ -460,7 +739,8 @@ function up2k_init(have_crypto) { | ||||
|     function hashing_permitted() { | ||||
|         if (multitask) { | ||||
|             var ahead = st.bytes.hashed - st.bytes.uploaded; | ||||
|             return ahead < 1024 * 1024 * 128; | ||||
|             return ahead < 1024 * 1024 * 1024 * 4 && | ||||
|                 st.todo.handshake.length + st.busy.handshake.length < 16; | ||||
|         } | ||||
|         return handshakes_permitted() && 0 == | ||||
|             st.todo.handshake.length + | ||||
| @@ -524,7 +804,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 | ||||
|                         ) | ||||
| @@ -626,31 +906,6 @@ function up2k_init(have_crypto) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function test_filereader_speed(segm_err) { | ||||
|         var f = st.todo.hash[0].fobj, | ||||
|             sz = Math.min(2, f.size), | ||||
|             reader = new FileReader(), | ||||
|             t0, ctr = 0; | ||||
|  | ||||
|         var segm_next = function () { | ||||
|             var t = new Date().getTime(), | ||||
|                 td = t - t0; | ||||
|  | ||||
|             if (++ctr > 2) { | ||||
|                 need_filereader_cache = td > 50; | ||||
|                 st.busy.hash.pop(); | ||||
|                 return; | ||||
|             } | ||||
|             t0 = t; | ||||
|             reader.onload = segm_next; | ||||
|             reader.onerror = segm_err; | ||||
|             reader.readAsArrayBuffer( | ||||
|                 bobslice.call(f, 0, sz)); | ||||
|         }; | ||||
|  | ||||
|         segm_next(); | ||||
|     } | ||||
|  | ||||
|     function ensure_rendered(func) { | ||||
|         var hidden = false; | ||||
|         var keys = ['hidden', 'msHidden', 'webkitHidden']; | ||||
| @@ -665,122 +920,88 @@ function up2k_init(have_crypto) { | ||||
|     } | ||||
|  | ||||
|     function exec_hash() { | ||||
|         if (need_filereader_cache === undefined) { | ||||
|             st.busy.hash.push(1); | ||||
|             return test_filereader_speed(segm_err); | ||||
|         } | ||||
|  | ||||
|         var t = st.todo.hash.shift(); | ||||
|         st.busy.hash.push(t); | ||||
|         st.bytes.hashed += t.size; | ||||
|         t.bytes_uploaded = 0; | ||||
|         t.t1 = new Date().getTime(); | ||||
|  | ||||
|         var nchunk = 0; | ||||
|         var chunksize = get_chunksize(t.size); | ||||
|         var nchunks = Math.ceil(t.size / chunksize); | ||||
|         var bpend = 0, | ||||
|             nchunk = 0, | ||||
|             chunksize = get_chunksize(t.size), | ||||
|             nchunks = Math.ceil(t.size / chunksize), | ||||
|             hashtab = {}; | ||||
|  | ||||
|         // android-chrome has 180ms latency on FileReader calls, | ||||
|         // detect this and do 32MB at a time | ||||
|         var cache_buf = undefined, | ||||
|             cache_ofs = 0, | ||||
|             subchunks = 2; | ||||
|  | ||||
|         while (subchunks * chunksize <= 32 * 1024 * 1024) | ||||
|             subchunks++; | ||||
|  | ||||
|         subchunks--; | ||||
|         if (!need_filereader_cache) | ||||
|             subchunks = 1; | ||||
|  | ||||
|         var pb_html = ''; | ||||
|         var pb_perc = 99.9 / nchunks; | ||||
|         for (var a = 0; a < nchunks; a++) | ||||
|             pb_html += '<div id="f{0}p{1}" style="width:{2}%"><div></div></div>'.format( | ||||
|                 t.n, a, pb_perc); | ||||
|  | ||||
|         ebi('f{0}p'.format(t.n)).innerHTML = pb_html; | ||||
|  | ||||
|         var reader = new FileReader(); | ||||
|         pvis.setab(t.n, nchunks); | ||||
|         pvis.move(t.n, 'bz'); | ||||
|  | ||||
|         var segm_next = function () { | ||||
|             if (cache_buf) { | ||||
|                 return hash_calc(); | ||||
|             } | ||||
|             reader.onload = segm_load; | ||||
|             reader.onerror = segm_err; | ||||
|             if (nchunk >= nchunks || (bpend > chunksize && bpend >= 32 * 1024 * 1024)) | ||||
|                 return false; | ||||
|  | ||||
|             var reader = new FileReader(), | ||||
|                 nch = nchunk++, | ||||
|                 car = nch * chunksize, | ||||
|                 cdr = car + chunksize; | ||||
|  | ||||
|             var car = nchunk * chunksize; | ||||
|             var cdr = car + chunksize * subchunks; | ||||
|             if (cdr >= t.size) | ||||
|                 cdr = t.size; | ||||
|  | ||||
|             bpend += cdr - car; | ||||
|  | ||||
|             reader.onload = function (e) { | ||||
|                 hash_calc(nch, e.target.result); | ||||
|             }; | ||||
|             reader.onerror = segm_err; | ||||
|             reader.readAsArrayBuffer( | ||||
|                 bobslice.call(t.fobj, car, cdr)); | ||||
|  | ||||
|             prog(t.n, nchunk, col_hashing); | ||||
|             return true; | ||||
|         }; | ||||
|  | ||||
|         var segm_load = function (e) { | ||||
|             cache_buf = e.target.result; | ||||
|             cache_ofs = 0; | ||||
|             hash_calc(); | ||||
|         }; | ||||
|         var hash_calc = function (nch, buf) { | ||||
|             while (segm_next()); | ||||
|  | ||||
|         var hash_calc = function () { | ||||
|             var buf = cache_buf; | ||||
|             if (chunksize >= buf.byteLength) | ||||
|                 cache_buf = undefined; | ||||
|             else { | ||||
|                 var ofs = cache_ofs; | ||||
|                 var ofs2 = ofs + Math.min(chunksize, cache_buf.byteLength - cache_ofs); | ||||
|                 cache_ofs = ofs2; | ||||
|                 buf = new Uint8Array(cache_buf).subarray(ofs, ofs2); | ||||
|                 if (ofs2 >= cache_buf.byteLength) | ||||
|                     cache_buf = undefined; | ||||
|             } | ||||
|             var hash_done = function (hashbuf) { | ||||
|                 var hslice = new Uint8Array(hashbuf).subarray(0, 32); | ||||
|                 var b64str = buf2b64(hslice).replace(/=$/, ''); | ||||
|                 hashtab[nch] = b64str; | ||||
|                 t.hash.push(nch); | ||||
|                 pvis.hashed(t); | ||||
|  | ||||
|             var func = function () { | ||||
|                 if (have_crypto) | ||||
|                     crypto.subtle.digest('SHA-512', buf).then(hash_done); | ||||
|                 else { | ||||
|                     var hasher = new asmCrypto.Sha512(); | ||||
|                     hasher.process(new Uint8Array(buf)); | ||||
|                     hasher.finish(); | ||||
|                     hash_done(hasher.result); | ||||
|                 bpend -= buf.byteLength; | ||||
|                 if (t.hash.length < nchunks) { | ||||
|                     return segm_next(); | ||||
|                 } | ||||
|                 t.hash = []; | ||||
|                 for (var a = 0; a < nchunks; a++) { | ||||
|                     t.hash.push(hashtab[a]); | ||||
|                 } | ||||
|  | ||||
|                 t.t2 = new Date().getTime(); | ||||
|                 if (t.n == 0 && window.location.hash == '#dbg') { | ||||
|                     var spd = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.); | ||||
|                     alert('{0} ms, {1} MB/s\n'.format(t.t2 - t.t1, spd.toFixed(3)) + t.hash.join('\n')); | ||||
|                 } | ||||
|  | ||||
|                 pvis.seth(t.n, 2, 'hashing done'); | ||||
|                 pvis.seth(t.n, 1, '📦 wait'); | ||||
|                 st.busy.hash.splice(st.busy.hash.indexOf(t), 1); | ||||
|                 st.todo.handshake.push(t); | ||||
|             }; | ||||
|  | ||||
|             if (cache_buf) | ||||
|                 ensure_rendered(func); | ||||
|             else | ||||
|                 func(); | ||||
|         }; | ||||
|  | ||||
|         var hash_done = function (hashbuf) { | ||||
|             var hslice = new Uint8Array(hashbuf).subarray(0, 32); | ||||
|             var b64str = buf2b64(hslice).replace(/=$/, ''); | ||||
|             t.hash.push(b64str); | ||||
|  | ||||
|             prog(t.n, nchunk, col_hashed); | ||||
|             if (++nchunk < nchunks) { | ||||
|                 prog(t.n, nchunk, col_hashing); | ||||
|                 return segm_next(); | ||||
|             if (have_crypto) | ||||
|                 crypto.subtle.digest('SHA-512', buf).then(hash_done); | ||||
|             else { | ||||
|                 var hasher = new asmCrypto.Sha512(); | ||||
|                 hasher.process(new Uint8Array(buf)); | ||||
|                 hasher.finish(); | ||||
|                 hash_done(hasher.result); | ||||
|             } | ||||
|  | ||||
|             t.t2 = new Date().getTime(); | ||||
|             if (t.n == 0 && window.location.hash == '#dbg') { | ||||
|                 var spd = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.); | ||||
|                 alert('{0} ms, {1} MB/s\n'.format(t.t2 - t.t1, spd.toFixed(3)) + t.hash.join('\n')); | ||||
|             } | ||||
|  | ||||
|             ebi('f{0}t'.format(t.n)).innerHTML = 'connecting'; | ||||
|             st.busy.hash.splice(st.busy.hash.indexOf(t), 1); | ||||
|             st.todo.handshake.push(t); | ||||
|         }; | ||||
|  | ||||
|         var segm_err = function () { | ||||
|             alert('y o u   b r o k e    i t\n\n(was that a folder? just files please)'); | ||||
|             alert('y o u   b r o k e    i t\nerror: ' + reader.error); | ||||
|         }; | ||||
|  | ||||
|         segm_next(); | ||||
| @@ -810,7 +1031,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), | ||||
| @@ -819,8 +1040,9 @@ function up2k_init(have_crypto) { | ||||
|  | ||||
|                         msg += '<br /><small>' + tr + ' (srv), ' + tu + ' (You), ' + sdiff + '</span></span>'; | ||||
|                     } | ||||
|                     ebi('f{0}p'.format(t.n)).innerHTML = msg; | ||||
|                     ebi('f{0}t'.format(t.n)).innerHTML = smsg; | ||||
|                     pvis.seth(t.n, 2, msg); | ||||
|                     pvis.seth(t.n, 1, smsg); | ||||
|                     pvis.move(t.n, smsg == '404' ? 'ng' : 'ok'); | ||||
|                     st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1); | ||||
|                     st.bytes.uploaded += t.size; | ||||
|                     t.done = true; | ||||
| @@ -831,7 +1053,15 @@ function up2k_init(have_crypto) { | ||||
|                 if (response.name !== t.name) { | ||||
|                     // file exists; server renamed us | ||||
|                     t.name = response.name; | ||||
|                     ebi('f{0}n'.format(t.n)).innerHTML = linksplit(esc(t.purl + t.name)).join(' '); | ||||
|                     pvis.seth(t.n, 0, linksplit(esc(t.purl + t.name)).join(' ')); | ||||
|                 } | ||||
|  | ||||
|                 var chunksize = get_chunksize(t.size); | ||||
|                 var cdr_idx = Math.ceil(t.size / chunksize) - 1; | ||||
|                 var cdr_sz = (t.size % chunksize) || chunksize; | ||||
|                 var cbd = []; | ||||
|                 for (var a = 0; a <= cdr_idx; a++) { | ||||
|                     cbd.push(a == cdr_idx ? cdr_sz : chunksize); | ||||
|                 } | ||||
|  | ||||
|                 t.postlist = []; | ||||
| @@ -844,10 +1074,11 @@ function up2k_init(have_crypto) { | ||||
|                             missing[a], JSON.stringify(t))); | ||||
|  | ||||
|                     t.postlist.push(idx); | ||||
|                     cbd[idx] = 0; | ||||
|                 } | ||||
|                 for (var a = 0; a < t.hash.length; a++) | ||||
|                     prog(t.n, a, (t.postlist.indexOf(a) == -1) | ||||
|                         ? col_uploaded : col_hashed); | ||||
|  | ||||
|                 pvis.setat(t.n, cbd); | ||||
|                 pvis.prog(t, 0, cbd[0]); | ||||
|  | ||||
|                 var done = true; | ||||
|                 var msg = '🎷🐛'; | ||||
| @@ -861,49 +1092,56 @@ function up2k_init(have_crypto) { | ||||
|                     msg = 'uploading'; | ||||
|                     done = false; | ||||
|                 } | ||||
|                 ebi('f{0}t'.format(t.n)).innerHTML = msg; | ||||
|                 pvis.seth(t.n, 1, msg); | ||||
|                 st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1); | ||||
|  | ||||
|                 if (done) { | ||||
|                     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.); | ||||
|                     ebi('f{0}p'.format(t.n)).innerHTML = 'hash {0}, up {1} MB/s'.format( | ||||
|                         spd1.toFixed(2), spd2.toFixed(2)); | ||||
|                     var spd2 = (t.size / ((t.t4 - t.t3) / 1000.)) / (1024 * 1024.); | ||||
|                     pvis.seth(t.n, 2, 'hash {0}, up {1} MB/s'.format( | ||||
|                         spd1.toFixed(2), spd2.toFixed(2))); | ||||
|                     pvis.move(t.n, 'ok'); | ||||
|                 } | ||||
|                 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 != "") { | ||||
|                     ebi('f{0}t'.format(t.n)).innerHTML = "ERROR"; | ||||
|                     ebi('f{0}p'.format(t.n)).innerHTML = err; | ||||
|                     pvis.seth(t.n, 1, "ERROR"); | ||||
|                     pvis.seth(t.n, 2, err); | ||||
|                     pvis.move(t.n, 'ng'); | ||||
|  | ||||
|                     st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1); | ||||
|                     tasker(); | ||||
|                     return; | ||||
|                 } | ||||
|                 alert("server broke (error {0}):\n\"{1}\"\n".format( | ||||
|                     xhr.status, | ||||
|                     (xhr.response && xhr.response.err) || | ||||
|                     (xhr.responseText && xhr.responseText) || | ||||
|                     "no further information")); | ||||
|                 alert("server broke; hs-err {0} on file [{1}]:\n".format( | ||||
|                     xhr.status, t.name) + ( | ||||
|                         (xhr.response && xhr.response.err) || | ||||
|                         (xhr.responseText && xhr.responseText) || | ||||
|                         "no further information")); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
| @@ -932,8 +1170,10 @@ function up2k_init(have_crypto) { | ||||
|  | ||||
|         var npart = upt.npart; | ||||
|         var t = st.files[upt.nfile]; | ||||
|         if (!t.t3) | ||||
|             t.t3 = new Date().getTime(); | ||||
|  | ||||
|         prog(t.n, npart, col_uploading); | ||||
|         pvis.seth(t.n, 1, "🚀 send"); | ||||
|  | ||||
|         var chunksize = get_chunksize(t.size); | ||||
|         var car = npart * chunksize; | ||||
| @@ -941,68 +1181,40 @@ function up2k_init(have_crypto) { | ||||
|         if (cdr >= t.size) | ||||
|             cdr = t.size; | ||||
|  | ||||
|         var reader = new FileReader(); | ||||
|  | ||||
|         reader.onerror = function () { | ||||
|             alert('y o u   b r o k e    i t\n\n(was that a folder? just files please)'); | ||||
|         var xhr = new XMLHttpRequest(); | ||||
|         xhr.upload.onprogress = function (xev) { | ||||
|             pvis.prog(t, npart, xev.loaded); | ||||
|         }; | ||||
|  | ||||
|         reader.onload = function (e) { | ||||
|             var xhr = new XMLHttpRequest(); | ||||
|             xhr.upload.onprogress = function (xev) { | ||||
|                 var perc = xev.loaded / (cdr - car) * 100; | ||||
|                 prog(t.n, npart, '', perc); | ||||
|             }; | ||||
|             xhr.onload = function (xev) { | ||||
|                 if (xhr.status == 200) { | ||||
|                     prog(t.n, npart, col_uploaded); | ||||
|                     st.bytes.uploaded += cdr - car; | ||||
|                     t.bytes_uploaded += cdr - car; | ||||
|                     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(); | ||||
|                         ebi('f{0}t'.format(t.n)).innerHTML = 'verifying'; | ||||
|                         st.todo.handshake.unshift(t); | ||||
|                     } | ||||
|                     tasker(); | ||||
|         xhr.onload = function (xev) { | ||||
|             if (xhr.status == 200) { | ||||
|                 pvis.prog(t, npart, cdr - car); | ||||
|                 st.bytes.uploaded += cdr - car; | ||||
|                 t.bytes_uploaded += cdr - car; | ||||
|                 st.busy.upload.splice(st.busy.upload.indexOf(upt), 1); | ||||
|                 t.postlist.splice(t.postlist.indexOf(npart), 1); | ||||
|                 if (t.postlist.length == 0) { | ||||
|                     t.t4 = new Date().getTime(); | ||||
|                     pvis.seth(t.n, 1, 'verifying'); | ||||
|                     st.todo.handshake.unshift(t); | ||||
|                 } | ||||
|                 else | ||||
|                     alert("server broke (error {0}):\n\"{1}\"\n".format( | ||||
|                         xhr.status, | ||||
|                 tasker(); | ||||
|             } | ||||
|             else | ||||
|                 alert("server broke; cu-err {0} on file [{1}]:\n".format( | ||||
|                     xhr.status, t.name) + ( | ||||
|                         (xhr.response && xhr.response.err) || | ||||
|                         (xhr.responseText && xhr.responseText) || | ||||
|                         "no further information")); | ||||
|             }; | ||||
|             xhr.open('POST', t.purl + 'chunkpit.php', true); | ||||
|             //xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart].substr(1) + "x"); | ||||
|             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'); | ||||
|             xhr.responseType = 'text'; | ||||
|             xhr.send(e.target.result); | ||||
|         }; | ||||
|         xhr.open('POST', t.purl + 'chunkpit.php', true); | ||||
|         xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart]); | ||||
|         xhr.setRequestHeader("X-Up2k-Wark", t.wark); | ||||
|         xhr.setRequestHeader('Content-Type', 'application/octet-stream'); | ||||
|         if (xhr.overrideMimeType) | ||||
|             xhr.overrideMimeType('Content-Type', 'application/octet-stream'); | ||||
|  | ||||
|         reader.readAsArrayBuffer(bobslice.call(t.fobj, car, cdr)); | ||||
|     } | ||||
|  | ||||
|     ///// | ||||
|     //// | ||||
|     ///   progress bar | ||||
|     // | ||||
|  | ||||
|     function prog(nfile, nchunk, color, percent) { | ||||
|         var n1 = ebi('f{0}p{1}'.format(nfile, nchunk)); | ||||
|         var n2 = n1.getElementsByTagName('div')[0]; | ||||
|         if (percent === undefined) { | ||||
|             n1.style.background = color; | ||||
|             n2.style.display = 'none'; | ||||
|         } | ||||
|         else { | ||||
|             n2.style.width = percent + '%'; | ||||
|             n2.style.display = 'block'; | ||||
|         } | ||||
|         xhr.responseType = 'text'; | ||||
|         xhr.send(bobslice.call(t.fobj, car, cdr)); | ||||
|     } | ||||
|  | ||||
|     ///// | ||||
| @@ -1023,6 +1235,7 @@ function up2k_init(have_crypto) { | ||||
|         if (btn.parentNode !== parent) { | ||||
|             parent.appendChild(btn); | ||||
|             ebi('u2conf').setAttribute('class', wide ? 'has_btn' : ''); | ||||
|             ebi('u2cards').setAttribute('class', wide ? 'w' : ''); | ||||
|         } | ||||
|     } | ||||
|     window.addEventListener('resize', onresize); | ||||
| @@ -1058,14 +1271,14 @@ function up2k_init(have_crypto) { | ||||
|  | ||||
|         var obj = ebi('nthread'); | ||||
|         if (dir.target) { | ||||
|             obj.style.background = '#922'; | ||||
|             clmod(obj, 'err', 1); | ||||
|             var v = Math.floor(parseInt(obj.value)); | ||||
|             if (v < 1 || v > 8 || v !== v) | ||||
|                 return; | ||||
|  | ||||
|             parallel_uploads = v; | ||||
|             swrite('nthread', v); | ||||
|             obj.style.background = '#444'; | ||||
|             clmod(obj, 'err'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -47,6 +47,11 @@ | ||||
| 	margin: -1.5em 0; | ||||
| 	padding: .8em 0; | ||||
| 	width: 100%; | ||||
| 	max-width: 12em; | ||||
| 	display: inline-block; | ||||
| } | ||||
| #u2conf #u2btn_cw { | ||||
| 	text-align: right; | ||||
| } | ||||
| #u2notbtn { | ||||
| 	display: none; | ||||
| @@ -72,6 +77,7 @@ | ||||
| } | ||||
| #u2tab td:nth-child(2) { | ||||
| 	width: 5em; | ||||
| 	white-space: nowrap; | ||||
| } | ||||
| #u2tab td:nth-child(3) { | ||||
| 	width: 40%; | ||||
| @@ -83,6 +89,35 @@ | ||||
| #u2tab tr+tr:hover td { | ||||
| 	background: #222; | ||||
| } | ||||
| #u2cards { | ||||
| 	margin: 2.5em auto -2.5em auto; | ||||
| 	text-align: center; | ||||
| } | ||||
| #u2cards.w { | ||||
| 	width: 45em; | ||||
| 	text-align: left; | ||||
| } | ||||
| #u2cards a { | ||||
| 	padding: .2em 1em; | ||||
| 	border: 1px solid #777; | ||||
| 	border-width: 0 0 1px 0; | ||||
| 	background: linear-gradient(to bottom, #333, #222); | ||||
| } | ||||
| #u2cards a:first-child { | ||||
| 	border-radius: .4em 0 0 0; | ||||
| } | ||||
| #u2cards a:last-child { | ||||
| 	border-radius: 0 .4em 0 0; | ||||
| } | ||||
| #u2cards a.act { | ||||
| 	border-width: 1px 1px 0 1px; | ||||
| 	border-radius: .3em .3em 0 0; | ||||
| 	margin-left: -1px; | ||||
| 	background: transparent; | ||||
| } | ||||
| #u2cards span { | ||||
| 	color: #fff; | ||||
| } | ||||
| #u2conf { | ||||
| 	margin: 1em auto; | ||||
| 	width: 30em; | ||||
| @@ -106,6 +141,9 @@ | ||||
| 	font-size: 1.2em; | ||||
| 	padding: .15em 0; | ||||
| } | ||||
| #u2conf .txtbox.err { | ||||
| 	background: #922; | ||||
| } | ||||
| #u2conf a { | ||||
| 	color: #fff; | ||||
| 	background: #c38; | ||||
| @@ -193,24 +231,6 @@ | ||||
| .prog { | ||||
| 	font-family: monospace; | ||||
| } | ||||
| .prog>div { | ||||
| 	display: inline-block; | ||||
| 	position: relative; | ||||
| 	overflow: hidden; | ||||
| 	margin: 0; | ||||
| 	padding: 0; | ||||
| 	height: 1.1em; | ||||
| 	margin-bottom: -.15em; | ||||
| 	box-shadow: -1px -1px 0 inset rgba(255,255,255,0.1); | ||||
| } | ||||
| .prog>div>div { | ||||
| 	width: 0%; | ||||
| 	position: absolute; | ||||
| 	left: 0; | ||||
| 	top: 0; | ||||
| 	bottom: 0; | ||||
| 	background: #0a0; | ||||
| } | ||||
| #u2tab a>span { | ||||
| 	font-weight: bold; | ||||
| 	font-style: italic; | ||||
| @@ -221,3 +241,35 @@ | ||||
| 	float: right; | ||||
| 	margin-bottom: -.3em; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| html.light #u2btn { | ||||
| 	box-shadow: .4em .4em 0 #ccc; | ||||
| } | ||||
| html.light #u2cards span { | ||||
| 	color: #000; | ||||
| } | ||||
| html.light #u2cards a { | ||||
| 	background: linear-gradient(to bottom, #eee, #fff); | ||||
| } | ||||
| html.light #u2cards a.act { | ||||
| 	background: inherit; | ||||
| } | ||||
| html.light #u2conf .txtbox { | ||||
| 	background: #fff; | ||||
| 	color: #444; | ||||
| } | ||||
| html.light #u2conf .txtbox.err { | ||||
| 	background: #f96; | ||||
| 	color: #300; | ||||
| } | ||||
| html.light #u2cdesc { | ||||
| 	background: #fff; | ||||
| 	border: none; | ||||
| } | ||||
| html.light #op_up2k.srch #u2btn { | ||||
| 	border-color: #a80; | ||||
| } | ||||
| @@ -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> | ||||
| @@ -79,12 +79,23 @@ | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
|             <div id="u2cards"> | ||||
|                 <a href="#" act="ok">ok <span>0</span></a><a | ||||
|                 href="#" act="ng">ng <span>0</span></a><a | ||||
|                 href="#" act="done">done <span>0</span></a><a | ||||
|                 href="#" act="bz" class="act">busy <span>0</span></a><a | ||||
|                 href="#" act="q">que <span>0</span></a> | ||||
|             </div> | ||||
|  | ||||
|             <table id="u2tab"> | ||||
|                 <tr> | ||||
|                     <td>filename</td> | ||||
|                     <td>status</td> | ||||
|                     <td>progress<a href="#" id="u2cleanup">cleanup</a></td> | ||||
|                 </tr> | ||||
|                 <thead> | ||||
|                     <tr> | ||||
|                         <td>filename</td> | ||||
|                         <td>status</td> | ||||
|                         <td>progress<a href="#" id="u2cleanup">cleanup</a></td> | ||||
|                     </tr> | ||||
|                 </thead> | ||||
|                 <tbody></tbody> | ||||
|             </table> | ||||
|  | ||||
|             <p id="u2foot"></p> | ||||
|   | ||||
| @@ -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,85 @@ 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) { | ||||
|     var sopts = jread('fsort', [["lead", -1, ""], ["href", 1, ""]]); | ||||
|  | ||||
|     try { | ||||
|         var is_srch = false; | ||||
|         if (nodes[0]['rp']) { | ||||
|             is_srch = true; | ||||
|             for (var b = 0, bb = nodes.length; b < bb; b++) | ||||
|                 nodes[b].ext = nodes[b].rp.split('.').pop(); | ||||
|             for (var b = 0; b < sopts.length; b++) | ||||
|                 if (sopts[b][0] == 'href') | ||||
|                     sopts[b][0] = 'rp'; | ||||
|         } | ||||
|         for (var a = sopts.length - 1; a >= 0; a--) { | ||||
|             var name = sopts[a][0], rev = sopts[a][1], typ = sopts[a][2]; | ||||
|             if (!name) | ||||
|                 continue; | ||||
|  | ||||
|             if (name.indexOf('tags/') === 0) { | ||||
|                 name = name.slice(5); | ||||
|                 for (var b = 0, bb = nodes.length; b < bb; b++) | ||||
|                     nodes[b]._sv = nodes[b].tags[name]; | ||||
|             } | ||||
|             else { | ||||
|                 for (var b = 0, bb = nodes.length; b < bb; b++) { | ||||
|                     var v = nodes[b][name]; | ||||
|  | ||||
|                     if ((v + '').indexOf('<a ') === 0) | ||||
|                         v = v.split('>')[1]; | ||||
|                     else if (name == "href" && v) | ||||
|                         v = uricom_dec(v)[0] | ||||
|  | ||||
|                     nodes[b]._sv = v; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var onodes = nodes.map(function (x) { return x; }); | ||||
|             nodes.sort(function (n1, n2) { | ||||
|                 var v1 = n1._sv, | ||||
|                     v2 = n2._sv; | ||||
|  | ||||
|                 if (v1 === undefined) { | ||||
|                     if (v2 === undefined) { | ||||
|                         return onodes.indexOf(n1) - onodes.indexOf(n2); | ||||
|                     } | ||||
|                     return -1 * rev; | ||||
|                 } | ||||
|                 if (v2 === undefined) return 1 * rev; | ||||
|  | ||||
|                 var ret = rev * (typ == 'int' ? (v1 - v2) : (v1.localeCompare(v2))); | ||||
|                 if (ret === 0) | ||||
|                     ret = onodes.indexOf(n1) - onodes.indexOf(n2); | ||||
|  | ||||
|                 return ret; | ||||
|             }); | ||||
|         } | ||||
|         for (var b = 0, bb = nodes.length; b < bb; b++) { | ||||
|             delete nodes[b]._sv; | ||||
|             if (is_srch) | ||||
|                 delete nodes[b].ext; | ||||
|         } | ||||
|     } | ||||
|     catch (ex) { | ||||
|         console.log("failed to apply sort config: " + ex); | ||||
|     } | ||||
|     return nodes; | ||||
| } | ||||
|  | ||||
|  | ||||
| function sortTable(table, col, cb) { | ||||
| @@ -186,7 +273,6 @@ function makeSortable(table, cb) { | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| (function () { | ||||
|     var ops = document.querySelectorAll('#ops>a'); | ||||
|     for (var a = 0; a < ops.length; a++) { | ||||
| @@ -212,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) | ||||
| @@ -408,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 | ||||
|  | ||||
| @@ -129,6 +136,16 @@ pip install virtualenv | ||||
| # readme toc | ||||
| cat README.md | awk '!/^#/{next} {lv=length($1);sub(/[^ ]+ /,"");bab=$0;gsub(/ /,"-",bab)} {printf "%" ((lv-1)*4+1) "s [%s](#%s)\n", "*",$0,bab}' | ||||
|  | ||||
| # fix firefox phantom breakpoints, | ||||
| # suggestions from bugtracker, doesnt work (debugger is not attachable) | ||||
| devtools settings >> advanced >> enable browser chrome debugging + enable remote debugging | ||||
| burger > developer >> browser toolbox  (ctrl-alt-shift-i) | ||||
| iframe btn topright >> chrome://devtools/content/debugger/index.html | ||||
| 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 | ||||
| @@ -161,7 +163,7 @@ find .. -type f \( -name .DS_Store -or -name ._.DS_Store \) -delete | ||||
| find .. -type f -name ._\* | while IFS= read -r f; do cmp <(printf '\x00\x05\x16') <(head -c 3 -- "$f") && rm -f -- "$f"; done | ||||
|  | ||||
| echo use smol web deps | ||||
| rm -f copyparty/web/deps/*.full.* | ||||
| rm -f copyparty/web/deps/*.full.* copyparty/web/{Makefile,splash.js} | ||||
|  | ||||
| # it's fine dw | ||||
| grep -lE '\.full\.(js|css)' copyparty/web/* | | ||||
| @@ -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) | ||||
|   | ||||
| @@ -18,7 +18,9 @@ from copyparty import util | ||||
|  | ||||
| class Cfg(Namespace): | ||||
|     def __init__(self, a=[], v=[], c=None): | ||||
|         ex = {k: False for k in "e2d e2ds e2dsa e2t e2ts e2tsr mte".split()} | ||||
|         ex = {k: False for k in "e2d e2ds e2dsa e2t e2ts e2tsr".split()} | ||||
|         ex["mtp"] = [] | ||||
|         ex["mte"] = "a" | ||||
|         super(Cfg, self).__init__(a=a, v=v, c=c, **ex) | ||||
|  | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user