list incoming files + ETA in controlpanel
This commit is contained in:
		
							parent
							
								
									c79eaa089a
								
							
						
					
					
						commit
						609c5921d4
					
				| @ -43,6 +43,7 @@ turn almost any device into a file server with resumable uploads/downloads using | ||||
|         * [unpost](#unpost) - undo/delete accidental uploads | ||||
|         * [self-destruct](#self-destruct) - uploads can be given a lifetime | ||||
|         * [race the beam](#race-the-beam) - download files while they're still uploading ([demo video](http://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm)) | ||||
|         * [incoming files](#incoming-files) - the control-panel shows the ETA for all incoming files | ||||
|     * [file manager](#file-manager) - cut/paste, rename, and delete files/folders (if you have permission) | ||||
|     * [shares](#shares) - share a file or folder by creating a temporary link | ||||
|     * [batch rename](#batch-rename) - select some files and press `F2` to bring up the rename UI | ||||
| @ -731,6 +732,13 @@ download files while they're still uploading ([demo video](http://a.ocv.me/pub/g | ||||
| requires the file to be uploaded using up2k (which is the default drag-and-drop uploader), alternatively the command-line program | ||||
| 
 | ||||
| 
 | ||||
| ### incoming files | ||||
| 
 | ||||
| the control-panel shows the ETA for all incoming files  , but only for files being uploaded into volumes where you have read-access | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| 
 | ||||
| ## file manager | ||||
| 
 | ||||
| cut/paste, rename, and delete files/folders (if you have permission) | ||||
|  | ||||
| @ -1230,6 +1230,7 @@ def add_optouts(ap): | ||||
|     ap2.add_argument("--no-zip", action="store_true", help="disable download as zip/tar") | ||||
|     ap2.add_argument("--no-tarcmp", action="store_true", help="disable download as compressed tar (?tar=gz, ?tar=bz2, ?tar=xz, ?tar=gz:9, ...)") | ||||
|     ap2.add_argument("--no-lifetime", action="store_true", help="do not allow clients (or server config) to schedule an upload to be deleted after a given time") | ||||
|     ap2.add_argument("--no-up-list", action="store_true", help="don't show list of incoming files in controlpanel") | ||||
|     ap2.add_argument("--no-pipe", action="store_true", help="disable race-the-beam (lockstep download of files which are currently being uploaded) (volflag=nopipe)") | ||||
|     ap2.add_argument("--no-db-ip", action="store_true", help="do not write uploader IPs into the database") | ||||
| 
 | ||||
|  | ||||
| @ -87,6 +87,7 @@ from .util import ( | ||||
|     relchk, | ||||
|     ren_open, | ||||
|     runhook, | ||||
|     s2hms, | ||||
|     s3enc, | ||||
|     sanitize_fn, | ||||
|     sanitize_vpath, | ||||
| @ -3939,11 +3940,30 @@ class HttpCli(object): | ||||
|             for y in [self.rvol, self.wvol, self.avol] | ||||
|         ] | ||||
| 
 | ||||
|         if self.avol and not self.args.no_rescan: | ||||
|             x = self.conn.hsrv.broker.ask("up2k.get_state") | ||||
|         ups = [] | ||||
|         now = time.time() | ||||
|         get_vst = self.avol and not self.args.no_rescan | ||||
|         get_ups = self.rvol and not self.args.no_up_list and self.uname or "" | ||||
|         if get_vst or get_ups: | ||||
|             x = self.conn.hsrv.broker.ask("up2k.get_state", get_vst, get_ups) | ||||
|             vs = json.loads(x.get()) | ||||
|             vstate = {("/" + k).rstrip("/") + "/": v for k, v in vs["volstate"].items()} | ||||
|         else: | ||||
|             try: | ||||
|                 for rem, sz, t0, poke, vp in vs["ups"]: | ||||
|                     fdone = max(0.001, 1 - rem) | ||||
|                     td = max(0.1, now - t0) | ||||
|                     rd, fn = vsplit(vp.replace(os.sep, "/")) | ||||
|                     if not rd: | ||||
|                         rd = "/" | ||||
|                     erd = quotep(rd) | ||||
|                     rds = rd.replace("/", " / ") | ||||
|                     spd = humansize(sz * fdone / td, True) + "/s" | ||||
|                     eta = s2hms((td / fdone) - td, True) | ||||
|                     idle = s2hms(now - poke, True) | ||||
|                     ups.append((int(100 * fdone), spd, eta, idle, erd, rds, fn)) | ||||
|             except Exception as ex: | ||||
|                 self.log("failed to list upload progress: %r" % (ex,), 1) | ||||
|         if not get_vst: | ||||
|             vstate = {} | ||||
|             vs = { | ||||
|                 "scanning": None, | ||||
| @ -3968,6 +3988,12 @@ class HttpCli(object): | ||||
|                 for k in ["scanning", "hashq", "tagq", "mtpq", "dbwt"]: | ||||
|                     txt += " {}({})".format(k, vs[k]) | ||||
| 
 | ||||
|             if ups: | ||||
|                 txt += "\n\nincoming files:" | ||||
|                 for zt in ups: | ||||
|                     txt += "\n%s" % (", ".join((str(x) for x in zt)),) | ||||
|                 txt += "\n" | ||||
| 
 | ||||
|             if rvol: | ||||
|                 txt += "\nyou can browse:" | ||||
|                 for v in rvol: | ||||
| @ -3991,6 +4017,7 @@ class HttpCli(object): | ||||
|             avol=avol, | ||||
|             in_shr=self.args.shr and self.vpath.startswith(self.args.shr[1:]), | ||||
|             vstate=vstate, | ||||
|             ups=ups, | ||||
|             scanning=vs["scanning"], | ||||
|             hashq=vs["hashq"], | ||||
|             tagq=vs["tagq"], | ||||
|  | ||||
| @ -268,19 +268,29 @@ class Up2k(object): | ||||
|             if not self.stop: | ||||
|                 self.log("uploads are now possible", 2) | ||||
| 
 | ||||
|     def get_state(self) -> str: | ||||
|     def get_state(self, get_q: bool, uname: str) -> str: | ||||
|         mtpq: Union[int, str] = 0 | ||||
|         ups = [] | ||||
|         up_en = not self.args.no_up_list | ||||
|         q = "select count(w) from mt where k = 't:mtp'" | ||||
|         got_lock = False if PY2 else self.mutex.acquire(timeout=0.5) | ||||
|         if got_lock: | ||||
|             for cur in self.cur.values(): | ||||
|                 try: | ||||
|                     mtpq += cur.execute(q).fetchone()[0] | ||||
|                 except: | ||||
|                     pass | ||||
|             self.mutex.release() | ||||
|             try: | ||||
|                 for cur in self.cur.values() if get_q else []: | ||||
|                     try: | ||||
|                         mtpq += cur.execute(q).fetchone()[0] | ||||
|                     except: | ||||
|                         pass | ||||
|                 if uname and up_en: | ||||
|                     ups = self._active_uploads(uname) | ||||
|             finally: | ||||
|                 self.mutex.release() | ||||
|         else: | ||||
|             mtpq = "(?)" | ||||
|             if up_en: | ||||
|                 ups = [(0, 0, time.time(), "cannot show list (server too busy)")] | ||||
| 
 | ||||
|         ups.sort(reverse=True) | ||||
| 
 | ||||
|         ret = { | ||||
|             "volstate": self.volstate, | ||||
| @ -288,6 +298,7 @@ class Up2k(object): | ||||
|             "hashq": self.n_hashq, | ||||
|             "tagq": self.n_tagq, | ||||
|             "mtpq": mtpq, | ||||
|             "ups": ups, | ||||
|             "dbwu": "{:.2f}".format(self.db_act), | ||||
|             "dbwt": "{:.2f}".format( | ||||
|                 min(1000 * 24 * 60 * 60 - 1, time.time() - self.db_act) | ||||
| @ -295,6 +306,32 @@ class Up2k(object): | ||||
|         } | ||||
|         return json.dumps(ret, separators=(",\n", ": ")) | ||||
| 
 | ||||
|     def _active_uploads(self, uname: str) -> list[tuple[float, int, int, str]]: | ||||
|         ret = [] | ||||
|         for vtop in self.asrv.vfs.aread[uname]: | ||||
|             vfs = self.asrv.vfs.all_vols.get(vtop) | ||||
|             if not vfs:  # dbv only | ||||
|                 continue | ||||
|             ptop = vfs.realpath | ||||
|             tab = self.registry.get(ptop) | ||||
|             if not tab: | ||||
|                 continue | ||||
|             for job in tab.values(): | ||||
|                 ineed = len(job["need"]) | ||||
|                 ihash = len(job["hash"]) | ||||
|                 if ineed == ihash or not ineed: | ||||
|                     continue | ||||
| 
 | ||||
|                 zt = ( | ||||
|                     ineed / ihash, | ||||
|                     job["size"], | ||||
|                     int(job["t0"]), | ||||
|                     int(job["poke"]), | ||||
|                     djoin(vtop, job["prel"], job["name"]), | ||||
|                 ) | ||||
|                 ret.append(zt) | ||||
|         return ret | ||||
| 
 | ||||
|     def find_job_by_ap(self, ptop: str, ap: str) -> str: | ||||
|         try: | ||||
|             if ANYWIN: | ||||
| @ -2910,9 +2947,12 @@ class Up2k(object): | ||||
|                         job = deepcopy(job) | ||||
|                         job["wark"] = wark | ||||
|                         job["at"] = cj.get("at") or time.time() | ||||
|                         zs = "lmod ptop vtop prel name host user addr poke" | ||||
|                         zs = "vtop ptop prel name lmod host user addr poke" | ||||
|                         for k in zs.split(): | ||||
|                             job[k] = cj.get(k) or "" | ||||
|                         for k in ("life", "replace"): | ||||
|                             if k in cj: | ||||
|                                 job[k] = cj[k] | ||||
| 
 | ||||
|                         pdir = djoin(cj["ptop"], cj["prel"]) | ||||
|                         if rand: | ||||
| @ -3013,18 +3053,8 @@ class Up2k(object): | ||||
|                     "busy": {}, | ||||
|                 } | ||||
|                 # client-provided, sanitized by _get_wark: name, size, lmod | ||||
|                 for k in [ | ||||
|                     "host", | ||||
|                     "user", | ||||
|                     "addr", | ||||
|                     "vtop", | ||||
|                     "ptop", | ||||
|                     "prel", | ||||
|                     "name", | ||||
|                     "size", | ||||
|                     "lmod", | ||||
|                     "poke", | ||||
|                 ]: | ||||
|                 zs = "vtop ptop prel name size lmod host user addr poke" | ||||
|                 for k in zs.split(): | ||||
|                     job[k] = cj[k] | ||||
| 
 | ||||
|                 for k in ["life", "replace"]: | ||||
|  | ||||
| @ -60,6 +60,18 @@ | ||||
| 		</div> | ||||
| 		{%- endif %} | ||||
| 
 | ||||
| 		{%- if ups %} | ||||
| 		<h1 id="aa">incoming files:</h1> | ||||
| 			<table class="vols"> | ||||
| 				<thead><tr><th>%</th><th>speed</th><th>eta</th><th>idle</th><th>dir</th><th>file</th></tr></thead> | ||||
| 				<tbody> | ||||
| 					{% for u in ups %} | ||||
| 					<tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td><a href="{{ u[4] }}">{{ u[5]|e }}</a></td><td>{{ u[6]|e }}</td></tr> | ||||
| 					{% endfor %} | ||||
| 				</tbody> | ||||
| 			</table> | ||||
| 		{%- endif %} | ||||
| 
 | ||||
| 		{%- if rvol %} | ||||
| 		<h1 id="f">you can browse:</h1> | ||||
| 		<ul> | ||||
|  | ||||
| @ -33,6 +33,7 @@ var Ls = { | ||||
| 		"ta1": "du må skrive et nytt passord først", | ||||
| 		"ta2": "gjenta for å bekrefte nytt passord:", | ||||
| 		"ta3": "fant en skrivefeil; vennligst prøv igjen", | ||||
| 		"aa1": "innkommende:", | ||||
| 	}, | ||||
| 	"eng": { | ||||
| 		"d2": "shows the state of all active threads", | ||||
| @ -78,6 +79,7 @@ var Ls = { | ||||
| 		"ta1": "请先输入新密码", | ||||
| 		"ta2": "重复以确认新密码:", | ||||
| 		"ta3": "发现拼写错误;请重试", | ||||
| 		"aa1": "正在接收的文件:", //m
 | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -120,7 +120,7 @@ class Cfg(Namespace): | ||||
|         ex = "chpw daw dav_auth dav_inf dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink ih ihead magic hardlink_only nid nih no_acode no_athumb no_dav no_db_ip no_del no_dupe no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tarcmp no_thumb no_vthumb no_zip nrand nw og og_no_head og_s_title q rand smb srch_dbg stats uqe vague_403 vc ver write_uplog xdev xlink xvol zs" | ||||
|         ka.update(**{k: False for k in ex.split()}) | ||||
| 
 | ||||
|         ex = "dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_voldump re_dhash plain_ip" | ||||
|         ex = "dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump re_dhash plain_ip" | ||||
|         ka.update(**{k: True for k in ex.split()}) | ||||
| 
 | ||||
|         ex = "ah_cli ah_gen css_browser hist js_browser js_other mime mimes no_forget no_hash no_idx nonsus_urls og_tpl og_ua" | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 ed
						ed