mirror of
				https://github.com/9001/copyparty.git
				synced 2025-10-31 03:53:31 +00:00 
			
		
		
		
	Compare commits
	
		
			19 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | c56ded828c | ||
|  | 02c7061945 | ||
|  | 9209e44cd3 | ||
|  | ebed37394e | ||
|  | 4c7a2a7ec3 | ||
|  | 0a25a88a34 | ||
|  | 6aa9025347 | ||
|  | a918cc67eb | ||
|  | 08f4695283 | ||
|  | 44e76d5eeb | ||
|  | cfa36fd279 | ||
|  | 3d4166e006 | ||
|  | 07bac1c592 | ||
|  | 755f2ce1ba | ||
|  | cca2844deb | ||
|  | 24a2f760b7 | ||
|  | 79bbd8fe38 | ||
|  | 35dce1e3e4 | ||
|  | f886fdf913 | 
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							| @@ -127,6 +127,7 @@ summary: all planned features work! now please enjoy the bloatening | ||||
|   * ☑ basic: plain multipart, ie6 support | ||||
|   * ☑ up2k: js, resumable, multithreaded | ||||
|   * ☑ stash: simple PUT filedropper | ||||
|   * ☑ unpost: undo/delete accidental uploads | ||||
|   * ☑ symlink/discard existing files (content-matching) | ||||
| * download | ||||
|   * ☑ single files in browser | ||||
| @@ -215,10 +216,11 @@ example: | ||||
| ## tabs | ||||
|  | ||||
| * `[🔎]` search by size, date, path/name, mp3-tags ... see [searching](#searching) | ||||
| * `[🧯]` unpost: undo/delete accidental uploads | ||||
| * `[🚀]` and `[🎈]` are the uploaders, see [uploading](#uploading) | ||||
| * `[📂]` mkdir, create directories | ||||
| * `[📝]` new-md, create a new markdown document | ||||
| * `[📟]` send-msg, either to server-log or into textfiles if `--urlform save` | ||||
| * `[📂]` mkdir: create directories | ||||
| * `[📝]` new-md: create a new markdown document | ||||
| * `[📟]` send-msg: either to server-log or into textfiles if `--urlform save` | ||||
| * `[🎺]` audio-player config options | ||||
| * `[⚙️]` general client config options | ||||
|  | ||||
| @@ -312,8 +314,10 @@ you can also zip a selection of files or folders by clicking them in the browser | ||||
| ## uploading | ||||
|  | ||||
| two upload methods are available in the html client: | ||||
| * `🎈 bup`, the basic uploader, supports almost every browser since netscape 4.0 | ||||
| * `🚀 up2k`, the fancy one | ||||
| * `[🎈] bup`, the basic uploader, supports almost every browser since netscape 4.0 | ||||
| * `[🚀] up2k`, the fancy one | ||||
|  | ||||
| you can undo/delete uploads using `[🧯] unpost` if the server is running with `-e2d` | ||||
|  | ||||
| up2k has several advantages: | ||||
| * you can drop folders into the browser (files are added recursively) | ||||
|   | ||||
| @@ -13,6 +13,10 @@ | ||||
| #   But note that journalctl will get the timestamps wrong due to | ||||
| #   python disabling line-buffering, so messages are out-of-order: | ||||
| #   https://user-images.githubusercontent.com/241032/126040249-cb535cc7-c599-4931-a796-a5d9af691bad.png | ||||
| # | ||||
| # enable line-buffering for realtime logging (slight performance cost): | ||||
| #   modify ExecStart and prefix it with `/bin/stdbuf -oL` like so: | ||||
| #   ExecStart=/bin/stdbuf -oL /usr/bin/python3 [...] | ||||
|  | ||||
| [Unit] | ||||
| Description=copyparty file server | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| # coding: utf-8 | ||||
|  | ||||
| VERSION = (0, 12, 3) | ||||
| VERSION = (0, 12, 9) | ||||
| CODENAME = "fil\033[33med" | ||||
| BUILD_DT = (2021, 7, 30) | ||||
| BUILD_DT = (2021, 8, 1) | ||||
|  | ||||
| S_VERSION = ".".join(map(str, VERSION)) | ||||
| S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT) | ||||
|   | ||||
| @@ -401,17 +401,18 @@ class AuthSrv(object): | ||||
|         if uname == "": | ||||
|             uname = "*" | ||||
|  | ||||
|         if "r" in lvl: | ||||
|             axs.uread[uname] = 1 | ||||
|         for un in uname.split(","): | ||||
|             if "r" in lvl: | ||||
|                 axs.uread[un] = 1 | ||||
|  | ||||
|         if "w" in lvl: | ||||
|             axs.uwrite[uname] = 1 | ||||
|             if "w" in lvl: | ||||
|                 axs.uwrite[un] = 1 | ||||
|  | ||||
|         if "m" in lvl: | ||||
|             axs.umove[uname] = 1 | ||||
|             if "m" in lvl: | ||||
|                 axs.umove[un] = 1 | ||||
|  | ||||
|         if "d" in lvl: | ||||
|             axs.udel[uname] = 1 | ||||
|             if "d" in lvl: | ||||
|                 axs.udel[un] = 1 | ||||
|  | ||||
|     def _read_volflag(self, flags, name, value, is_list): | ||||
|         if name not in ["mtp"]: | ||||
|   | ||||
| @@ -182,7 +182,7 @@ class HttpCli(object): | ||||
|  | ||||
|         self.uparam = uparam | ||||
|         self.cookies = cookies | ||||
|         self.vpath = unquotep(vpath) | ||||
|         self.vpath = unquotep(vpath)  # not query, so + means + | ||||
|  | ||||
|         pwd = uparam.get("pw") | ||||
|         self.uname = self.asrv.iacct.get(pwd, "*") | ||||
| @@ -1310,11 +1310,9 @@ class HttpCli(object): | ||||
|         else: | ||||
|             fn = self.headers.get("host", "hey") | ||||
|  | ||||
|         afn = "".join( | ||||
|             [x if x in (string.ascii_letters + string.digits) else "_" for x in fn] | ||||
|         ) | ||||
|  | ||||
|         bascii = unicode(string.ascii_letters + string.digits).encode("utf-8") | ||||
|         safe = (string.ascii_letters + string.digits).replace("%", "") | ||||
|         afn = "".join([x if x in safe.replace('"', "") else "_" for x in fn]) | ||||
|         bascii = unicode(safe).encode("utf-8") | ||||
|         ufn = fn.encode("utf-8", "xmlcharrefreplace") | ||||
|         if PY2: | ||||
|             ufn = [unicode(x) if x in bascii else "%{:02x}".format(ord(x)) for x in ufn] | ||||
| @@ -1329,6 +1327,7 @@ class HttpCli(object): | ||||
|  | ||||
|         cdis = "attachment; filename=\"{}.{}\"; filename*=UTF-8''{}.{}" | ||||
|         cdis = cdis.format(afn, fmt, ufn, fmt) | ||||
|         self.log(cdis) | ||||
|         self.send_headers(None, mime=mime, headers={"Content-Disposition": cdis}) | ||||
|  | ||||
|         fgen = vn.zipgen(rem, items, self.uname, dots, not self.args.no_scandir) | ||||
| @@ -1621,6 +1620,9 @@ class HttpCli(object): | ||||
|         if not dst: | ||||
|             raise Pebkac(400, "need dst vpath") | ||||
|  | ||||
|         # x-www-form-urlencoded (url query part) uses | ||||
|         # either + or %20 for 0x20 so handle both | ||||
|         dst = unquotep(dst.replace("+", " ")) | ||||
|         x = self.conn.hsrv.broker.put( | ||||
|             True, "up2k.handle_mv", self.uname, self.vpath, dst | ||||
|         ) | ||||
|   | ||||
| @@ -26,6 +26,9 @@ class ThumbCli(object): | ||||
|         if is_vid and self.args.no_vthumb: | ||||
|             return None | ||||
|  | ||||
|         if rem.startswith(".hist/th/") and rem.split(".")[-1] in ["webp", "jpg"]: | ||||
|             return os.path.join(ptop, rem) | ||||
|  | ||||
|         if fmt == "j" and self.args.th_no_jpg: | ||||
|             fmt = "w" | ||||
|  | ||||
|   | ||||
| @@ -1405,7 +1405,7 @@ class Up2k(object): | ||||
|                     try: | ||||
|                         ptop = dbv.realpath | ||||
|                         cur, wark, _, _, _, _ = self._find_from_vpath(ptop, volpath) | ||||
|                         self._forget_file(ptop, volpath, cur, wark) | ||||
|                         self._forget_file(ptop, volpath, cur, wark, True) | ||||
|                     finally: | ||||
|                         cur.connection.commit() | ||||
|  | ||||
| @@ -1491,10 +1491,10 @@ class Up2k(object): | ||||
|             fsize = st.st_size | ||||
|  | ||||
|         if w: | ||||
|             if c2: | ||||
|             if c2 and c2 != c1: | ||||
|                 self._copy_tags(c1, c2, w) | ||||
|  | ||||
|             self._forget_file(svn.realpath, srem, c1, w) | ||||
|             self._forget_file(svn.realpath, srem, c1, w, c1 != c2) | ||||
|             self._relink(w, svn.realpath, srem, dabs) | ||||
|             c1.connection.commit() | ||||
|  | ||||
| @@ -1535,17 +1535,19 @@ class Up2k(object): | ||||
|             return cur, wark, ftime, fsize, ip, at | ||||
|         return cur, None, None, None, None, None | ||||
|  | ||||
|     def _forget_file(self, ptop, vrem, cur, wark): | ||||
|     def _forget_file(self, ptop, vrem, cur, wark, drop_tags): | ||||
|         """forgets file in db, fixes symlinks, does not delete""" | ||||
|         srd, sfn = vsplit(vrem) | ||||
|         self.log("forgetting {}".format(vrem)) | ||||
|         if wark: | ||||
|             self.log("found {} in db".format(wark)) | ||||
|             self._relink(wark, ptop, vrem, None) | ||||
|             if self._relink(wark, ptop, vrem, None): | ||||
|                 drop_tags = False | ||||
|  | ||||
|             q = "delete from mt where w=?" | ||||
|             cur.execute(q, (wark[:16],)) | ||||
|             self.db_rm(cur, srd, sfn) | ||||
|             if drop_tags: | ||||
|                 q = "delete from mt where w=?" | ||||
|                 cur.execute(q, (wark[:16],)) | ||||
|                 self.db_rm(cur, srd, sfn) | ||||
|  | ||||
|         reg = self.registry.get(ptop) | ||||
|         if reg: | ||||
| @@ -1581,7 +1583,7 @@ class Up2k(object): | ||||
|                     self.log("found {} dupe: [{}] {}".format(wark, ptop, dvrem)) | ||||
|  | ||||
|         if not dupes: | ||||
|             return | ||||
|             return 0 | ||||
|  | ||||
|         full = {} | ||||
|         links = {} | ||||
| @@ -1618,6 +1620,8 @@ class Up2k(object): | ||||
|  | ||||
|             self._symlink(dabs, alink, False) | ||||
|  | ||||
|         return len(full) + len(links) | ||||
|  | ||||
|     def _get_wark(self, cj): | ||||
|         if len(cj["name"]) > 1024 or len(cj["hash"]) > 512 * 1024:  # 16TiB | ||||
|             raise Pebkac(400, "name or numchunks not according to spec") | ||||
|   | ||||
| @@ -49,7 +49,7 @@ pre, code, tt { | ||||
| 	transition: opacity 0.14s, height 0.14s, padding 0.14s; | ||||
| } | ||||
| #toast { | ||||
| 	top: 1.4em; | ||||
| 	bottom: 5em; | ||||
| 	right: -1em; | ||||
| 	line-height: 1.5em; | ||||
| 	padding: 1em 1.3em; | ||||
| @@ -1068,6 +1068,46 @@ html.light #ggrid a:hover { | ||||
| 	margin: 0; | ||||
| 	padding: 0; | ||||
| } | ||||
| #rui { | ||||
| 	position: fixed; | ||||
| 	top: 0; | ||||
| 	left: 0; | ||||
| 	width: calc(100% - 2em); | ||||
| 	height: auto; | ||||
| 	overflow: auto; | ||||
| 	max-height: calc(100% - 2em); | ||||
| 	border-bottom: .5em solid #999; | ||||
| 	background: #333; | ||||
| 	padding: 1em; | ||||
| 	z-index: 765; | ||||
| } | ||||
| html.light #rui { | ||||
| 	color: #fff; | ||||
| } | ||||
| #rui div+div { | ||||
| 	margin-top: 1em; | ||||
| } | ||||
| #rui table { | ||||
| 	width: 100%; | ||||
| } | ||||
| #rui td { | ||||
| 	padding: .2em .5em; | ||||
| } | ||||
| #rui td+td, | ||||
| #rui td input { | ||||
| 	width: 100%; | ||||
| } | ||||
| #rui input[readonly] { | ||||
| 	color: #fff; | ||||
| 	background: #444; | ||||
| 	border: 1px solid #777; | ||||
| 	padding: .2em .25em; | ||||
| } | ||||
| #rui h1 { | ||||
| 	margin: 0 0 .3em 0; | ||||
| 	padding: 0; | ||||
| 	font-size: 1.5em; | ||||
| } | ||||
| #pvol, | ||||
| #barbuf, | ||||
| #barpos, | ||||
|   | ||||
| @@ -133,6 +133,7 @@ ebi('op_cfg').innerHTML = ( | ||||
| 	'	<div>\n' + | ||||
| 	'		<a id="tooltips" class="tgl btn" href="#" tt="◔ ◡ ◔">ℹ️ tooltips</a>\n' + | ||||
| 	'		<a id="lightmode" class="tgl btn" href="#">☀️ lightmode</a>\n' + | ||||
| 	'		<a id="dotfiles" class="tgl btn" href="#" tt="show hidden files (if server permits)">dotfiles</a>\n' + | ||||
| 	'		<a id="griden" class="tgl btn" href="#" tt="toggle icons or list-view$NHotkey: G">田 the grid</a>\n' + | ||||
| 	'		<a id="thumbs" class="tgl btn" href="#" tt="in icon view, toggle icons or thumbnails$NHotkey: T">🖼️ thumbs</a>\n' + | ||||
| 	'	</div>\n' + | ||||
| @@ -521,15 +522,14 @@ var mp = new MPlayer(); | ||||
| makeSortable(ebi('files'), mp.read_order.bind(mp)); | ||||
|  | ||||
|  | ||||
| function get_np() { | ||||
| function ft2dict(tr) { | ||||
| 	var th = ebi('files').tHead.rows[0].cells, | ||||
| 		tr = QS('#files tr.play').cells, | ||||
| 		rv = [], | ||||
| 		ra = [], | ||||
| 		rt = {}; | ||||
|  | ||||
| 	for (var a = 1, aa = th.length; a < aa; a++) { | ||||
| 		var tv = tr[a].textContent, | ||||
| 		var tv = tr.cells[a].textContent, | ||||
| 			tk = a == 1 ? 'file' : th[a].getAttribute('name').split('/').slice(-1)[0], | ||||
| 			vis = th[a].className.indexOf('min') === -1; | ||||
|  | ||||
| @@ -540,6 +540,12 @@ function get_np() { | ||||
| 		rt[tk] = tv; | ||||
| 	} | ||||
| 	return [rt, rv, ra]; | ||||
| } | ||||
|  | ||||
|  | ||||
| function get_np() { | ||||
| 	var tr = QS('#files tr.play'); | ||||
| 	return ft2dict(tr); | ||||
| }; | ||||
|  | ||||
|  | ||||
| @@ -1468,10 +1474,10 @@ var fileman = (function () { | ||||
| 		if (r.clip === null) | ||||
| 			r.clip = jread('fman_clip', []); | ||||
|  | ||||
| 		var sel = msel.getsel(); | ||||
| 		clmod(bren, 'en', sel.length == 1); | ||||
| 		clmod(bdel, 'en', sel.length); | ||||
| 		clmod(bcut, 'en', sel.length); | ||||
| 		var nsel = msel.getsel().length; | ||||
| 		clmod(bren, 'en', nsel == 1); | ||||
| 		clmod(bdel, 'en', nsel); | ||||
| 		clmod(bcut, 'en', nsel); | ||||
| 		clmod(bpst, 'en', r.clip && r.clip.length); | ||||
| 		bren.style.display = have_mv && has(perms, 'write') && has(perms, 'move') ? '' : 'none'; | ||||
| 		bdel.style.display = have_del && has(perms, 'delete') ? '' : 'none'; | ||||
| @@ -1496,30 +1502,89 @@ var fileman = (function () { | ||||
|  | ||||
| 		var vsp = vsplit(src), | ||||
| 			base = vsp[0], | ||||
| 			ofn = vsp[1]; | ||||
| 			ofn = uricom_dec(vsp[1])[0]; | ||||
|  | ||||
| 		var fn = prompt('new filename:', ofn); | ||||
| 		if (!fn || fn == ofn) | ||||
| 			return toast.warn(1, 'rename aborted'); | ||||
|  | ||||
| 		var dst = base + fn; | ||||
|  | ||||
| 		function rename_cb() { | ||||
| 			if (this.readyState != XMLHttpRequest.DONE) | ||||
| 				return; | ||||
|  | ||||
| 			if (this.status !== 200) { | ||||
| 				var msg = this.responseText; | ||||
| 				toast.err(9, 'rename failed:\n' + msg); | ||||
| 				return; | ||||
| 			} | ||||
| 			toast.ok(2, 'rename OK'); | ||||
| 			treectl.goto(get_evpath()); | ||||
| 		var rui = ebi('rui'); | ||||
| 		if (!rui) { | ||||
| 			rui = mknod('div'); | ||||
| 			rui.setAttribute('id', 'rui'); | ||||
| 			document.body.appendChild(rui); | ||||
| 		} | ||||
| 		var xhr = new XMLHttpRequest(); | ||||
| 		xhr.open('GET', src + '?move=' + dst, true); | ||||
| 		xhr.onreadystatechange = rename_cb; | ||||
| 		xhr.send(); | ||||
| 		var html = [ | ||||
| 			'<h1>rename file</h1>', | ||||
| 			'<div><table>', | ||||
| 			'<tr><td>old:</td><td><input type="text" id="rn_old" readonly /></td></tr>', | ||||
| 			'<tr><td>new:</td><td><input type="text" id="rn_new" /></td></tr>', | ||||
| 			'</table></div>', | ||||
| 			'<div>', | ||||
| 			'<button id="rn_dec">url-decode</button>', | ||||
| 			'|', | ||||
| 			'<button id="rn_reset">↺ reset</button>', | ||||
| 			'<button id="rn_cancel">❌ cancel</button>', | ||||
| 			'<button id="rn_apply">✅ apply rename</button>', | ||||
| 			'</div>', | ||||
| 			'<div><table>' | ||||
| 		]; | ||||
|  | ||||
| 		var vars = ft2dict(ebi(sel[0].id).closest('tr')), | ||||
| 			keys = vars[1].concat(vars[2]); | ||||
|  | ||||
| 		vars = vars[0]; | ||||
| 		for (var a = 0; a < keys.length; a++) | ||||
| 			html.push('<tr><td>' + esc(keys[a]) + '</td><td><input type="text" readonly value="' + esc(vars[keys[a]]) + '" /></td></tr>'); | ||||
|  | ||||
| 		html.push('</table></div>'); | ||||
| 		rui.innerHTML = html.join('\n'); | ||||
| 		var iold = ebi('rn_old'), | ||||
| 			inew = ebi('rn_new'); | ||||
|  | ||||
| 		function rn_reset() { | ||||
| 			inew.value = iold.value; | ||||
| 			inew.focus(); | ||||
| 			inew.setSelectionRange(0, inew.value.lastIndexOf('.'), "forward"); | ||||
| 		} | ||||
| 		function rn_cancel() { | ||||
| 			rui.parentNode.removeChild(rui); | ||||
| 		} | ||||
|  | ||||
| 		inew.onkeydown = function (e) { | ||||
| 			if (e.key == 'Escape') | ||||
| 				return rn_cancel(); | ||||
|  | ||||
| 			if (e.key == 'Enter') | ||||
| 				return rn_apply(); | ||||
| 		}; | ||||
| 		ebi('rn_cancel').onclick = rn_cancel; | ||||
| 		ebi('rn_reset').onclick = rn_reset; | ||||
| 		ebi('rn_apply').onclick = rn_apply; | ||||
| 		ebi('rn_dec').onclick = function () { | ||||
| 			inew.value = uricom_dec(inew.value)[0]; | ||||
| 		}; | ||||
|  | ||||
| 		iold.value = ofn; | ||||
| 		rn_reset(); | ||||
|  | ||||
| 		function rn_apply() { | ||||
| 			var dst = base + uricom_enc(inew.value, false); | ||||
|  | ||||
| 			function rename_cb() { | ||||
| 				if (this.readyState != XMLHttpRequest.DONE) | ||||
| 					return; | ||||
|  | ||||
| 				if (this.status !== 200) { | ||||
| 					var msg = this.responseText; | ||||
| 					toast.err(9, 'rename failed:\n' + msg); | ||||
| 					return; | ||||
| 				} | ||||
| 				toast.ok(2, 'rename OK'); | ||||
| 				treectl.goto(get_evpath()); | ||||
| 				rn_cancel(); | ||||
| 			} | ||||
| 			var xhr = new XMLHttpRequest(); | ||||
| 			xhr.open('GET', src + '?move=' + dst, true); | ||||
| 			xhr.onreadystatechange = rename_cb; | ||||
| 			xhr.send(); | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	r.delete = function (e) { | ||||
| @@ -1611,7 +1676,7 @@ var fileman = (function () { | ||||
| 			links = QSA('#files tbody td:nth-child(2) a'); | ||||
|  | ||||
| 		for (var a = 0, aa = links.length; a < aa; a++) | ||||
| 			indir.push(links[a].getAttribute('name')); | ||||
| 			indir.push(vsplit(links[a].getAttribute('href'))[1]); | ||||
|  | ||||
| 		for (var a = 0; a < r.clip.length; a++) { | ||||
| 			var found = false; | ||||
| @@ -1626,12 +1691,12 @@ var fileman = (function () { | ||||
| 		} | ||||
|  | ||||
| 		if (exists.length) | ||||
| 			alert('these ' + exists.length + ' items cannot be pasted here (names already exist):\n\n' + exists.join('\n')); | ||||
| 			alert('these ' + exists.length + ' items cannot be pasted here (names already exist):\n\n' + uricom_adec(exists).join('\n')); | ||||
|  | ||||
| 		if (!req.length) | ||||
| 			return; | ||||
|  | ||||
| 		if (!confirm('paste these ' + req.length + ' items here?\n\n' + req.join('\n'))) | ||||
| 		if (!confirm('paste these ' + req.length + ' items here?\n\n' + uricom_adec(req).join('\n'))) | ||||
| 			return; | ||||
|  | ||||
| 		function paster() { | ||||
| @@ -1644,7 +1709,7 @@ var fileman = (function () { | ||||
| 				r.tx(srcdir); | ||||
| 				return; | ||||
| 			} | ||||
| 			toast.inf(0, 'pasting ' + (req.length + 1) + ' items\n\n' + vp); | ||||
| 			toast.inf(0, 'pasting ' + (req.length + 1) + ' items\n\n' + uricom_dec(vp)[0]); | ||||
|  | ||||
| 			var dst = get_evpath() + vp.split('/').slice(-1)[0]; | ||||
|  | ||||
| @@ -2415,6 +2480,7 @@ var treectl = (function () { | ||||
| 		prev_atop = null, | ||||
| 		prev_winh = null, | ||||
| 		dyn = bcfg_get('dyntree', true), | ||||
| 		dots = bcfg_get('dotfiles', false), | ||||
| 		treesz = icfg_get('treesz', 16); | ||||
|  | ||||
| 	treesz = Math.min(Math.max(treesz, 4), 50); | ||||
| @@ -2533,7 +2599,7 @@ var treectl = (function () { | ||||
| 		xhr.dst = dst; | ||||
| 		xhr.rst = rst; | ||||
| 		xhr.ts = Date.now(); | ||||
| 		xhr.open('GET', dst + '?tree=' + top, true); | ||||
| 		xhr.open('GET', dst + '?tree=' + top + (dots ? '&dots' : ''), true); | ||||
| 		xhr.onreadystatechange = recvtree; | ||||
| 		xhr.send(); | ||||
| 		enspin('#tree'); | ||||
| @@ -2637,7 +2703,7 @@ var treectl = (function () { | ||||
| 		xhr.top = url; | ||||
| 		xhr.hpush = hpush; | ||||
| 		xhr.ts = Date.now(); | ||||
| 		xhr.open('GET', xhr.top + '?ls', true); | ||||
| 		xhr.open('GET', xhr.top + '?ls' + (dots ? '&dots' : ''), true); | ||||
| 		xhr.onreadystatechange = recvls; | ||||
| 		xhr.send(); | ||||
| 		if (hpush) | ||||
| @@ -2774,6 +2840,13 @@ var treectl = (function () { | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	function tdots(e) { | ||||
| 		ev(e); | ||||
| 		dots = !dots; | ||||
| 		bcfg_set('dotfiles', dots); | ||||
| 		treectl.goto(get_evpath()); | ||||
| 	} | ||||
|  | ||||
| 	function dyntree(e) { | ||||
| 		ev(e); | ||||
| 		dyn = !dyn; | ||||
| @@ -2793,6 +2866,7 @@ var treectl = (function () { | ||||
|  | ||||
| 	ebi('entree').onclick = treectl.entree; | ||||
| 	ebi('detree').onclick = treectl.detree; | ||||
| 	ebi('dotfiles').onclick = tdots; | ||||
| 	ebi('dyntree').onclick = dyntree; | ||||
| 	ebi('twig').onclick = scaletree; | ||||
| 	ebi('twobytwo').onclick = scaletree; | ||||
| @@ -2839,7 +2913,7 @@ function apply_perms(newperms) { | ||||
|  | ||||
| 	var axs = [], | ||||
| 		aclass = '>', | ||||
| 		chk = ['read', 'write', 'rename', 'delete']; | ||||
| 		chk = ['read', 'write', 'move', 'delete']; | ||||
|  | ||||
| 	for (var a = 0; a < chk.length; a++) | ||||
| 		if (has(perms, chk[a])) | ||||
| @@ -3319,13 +3393,11 @@ var msel = (function () { | ||||
| 			item.id = links[a].getAttribute('id'); | ||||
| 			item.sel = links[a].closest('tr').classList.contains('sel'); | ||||
| 			item.vp = href.indexOf('/') !== -1 ? href : vbase + href; | ||||
| 			item.name = href.split('/').slice(-1); | ||||
|  | ||||
| 			r.all.push(item); | ||||
| 			if (item.sel) | ||||
| 				r.sel.push(item); | ||||
|  | ||||
| 			links[a].setAttribute('name', item.name); | ||||
| 			links[a].closest('tr').setAttribute('tabindex', '0'); | ||||
| 		} | ||||
| 	}; | ||||
| @@ -3365,10 +3437,15 @@ var msel = (function () { | ||||
| 	}; | ||||
| 	ebi('selzip').onclick = function (e) { | ||||
| 		ev(e); | ||||
| 		var names = r.getsel(), | ||||
| 		var sel = r.getsel(), | ||||
| 			arg = ebi('selzip').getAttribute('fmt'), | ||||
| 			txt = names.join('\n'), | ||||
| 			frm = mknod('form'); | ||||
| 			frm = mknod('form'), | ||||
| 			txt = []; | ||||
|  | ||||
| 		for (var a = 0; a < sel.length; a++) | ||||
| 			txt.push(vsplit(sel[a].vp)[1]); | ||||
|  | ||||
| 		txt = txt.join('\n'); | ||||
|  | ||||
| 		frm.setAttribute('action', '?' + arg); | ||||
| 		frm.setAttribute('method', 'post'); | ||||
|   | ||||
| @@ -398,6 +398,15 @@ function uricom_dec(txt) { | ||||
| } | ||||
|  | ||||
|  | ||||
| function uricom_adec(arr) { | ||||
|     var ret = []; | ||||
|     for (var a = 0; a < arr.length; a++) | ||||
|         ret.push(uricom_dec(arr[a])[0]); | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| function get_evpath() { | ||||
|     var ret = document.location.pathname; | ||||
|  | ||||
|   | ||||
| @@ -44,7 +44,7 @@ avg() { awk 'function pr(ncsz) {if (nsmp>0) {printf "%3s %s\n", csz, sum/nsmp} c | ||||
| dirs=("$HOME/vfs/ほげ" "$HOME/vfs/ほげ/ぴよ" "$HOME/vfs/$(printf \\xed\\x91)" "$HOME/vfs/$(printf \\xed\\x91/\\xed\\x92)") | ||||
| mkdir -p "${dirs[@]}" | ||||
| for dir in "${dirs[@]}"; do for fn in ふが "$(printf \\xed\\x93)" 'qwe,rty;asd fgh+jkl%zxc&vbn <qwe>"rty'"'"'uio&asd fgh'; do echo "$dir" > "$dir/$fn.html"; done; done | ||||
|  | ||||
| # qw er+ty%20ui%%20op<as>df&gh&jk#zx'cv"bn`m=qw*er^ty?ui@op,as.df-gh_jk | ||||
|  | ||||
| ## | ||||
| ## upload mojibake | ||||
| @@ -79,6 +79,10 @@ command -v gdate && date() { gdate "$@"; }; while true; do t=$(date +%s.%N); (ti | ||||
| # 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")); | ||||
|  | ||||
| # rename all selected songs to <leading-track-number> + <Title> + <extension> | ||||
| var sel=msel.getsel(), ci=find_file_col('Title')[0], re=[]; for (var a=0; a<sel.length; a++) { var url=sel[a].vp, tag=ebi(sel[a].id).closest('tr').querySelectorAll('td')[ci].textContent, name=uricom_dec(vsplit(url)[1])[0], m=/^([0-9]+[\. -]+)?.*(\.[^\.]+$)/.exec(name), name2=(m[1]||'')+tag+m[2], url2=vsplit(url)[0]+uricom_enc(name2,false); if (url!=url2) re.push([url, url2]); } | ||||
| console.log(JSON.stringify(re, null, '  ')); | ||||
| function f() { if (!re.length) return treectl.goto(get_evpath()); var [u1,u2] = re.shift(); fetch(u1+'?move='+u2).then((rsp) => {if (rsp.ok) f(); }); }; f(); | ||||
|  | ||||
| ## | ||||
| ## bash oneliners | ||||
|   | ||||
| @@ -34,6 +34,7 @@ gtar=$(command -v gtar || command -v gnutar) || true | ||||
| 	sed()  { gsed  "$@"; } | ||||
| 	find() { gfind "$@"; } | ||||
| 	sort() { gsort "$@"; } | ||||
| 	sha1sum() { shasum "$@"; } | ||||
| 	unexpand() { gunexpand "$@"; } | ||||
| 	command -v grealpath >/dev/null && | ||||
| 		realpath() { grealpath "$@"; } | ||||
| @@ -81,16 +82,23 @@ tmv() { | ||||
| 	mv t "$1" | ||||
| } | ||||
|  | ||||
| stamp=$( | ||||
| 	for d in copyparty scripts; do | ||||
| 		find $d -type f -printf '%TY-%Tm-%Td %TH:%TM:%TS %p\n' | ||||
| 	done | sort | tail -n 1 | sha1sum | cut -c-16 | ||||
| ) | ||||
|  | ||||
| rm -rf sfx/* | ||||
| mkdir -p sfx build | ||||
| cd sfx | ||||
|  | ||||
| [ $repack ] && { | ||||
| 	old="$( | ||||
| 		printf '%s\n' "$TMPDIR" /tmp | | ||||
| 		awk '/./ {print; exit}' | ||||
| 	)/pe-copyparty" | ||||
| tmpdir="$( | ||||
| 	printf '%s\n' "$TMPDIR" /tmp | | ||||
| 	awk '/./ {print; exit}' | ||||
| )" | ||||
|  | ||||
| [ $repack ] && { | ||||
| 	old="$tmpdir/pe-copyparty" | ||||
| 	echo "repack of files in $old" | ||||
| 	cp -pR "$old/"*{dep-j2,copyparty} . | ||||
| } | ||||
| @@ -172,12 +180,12 @@ mkdir -p ../dist | ||||
| sfx_out=../dist/copyparty-sfx | ||||
|  | ||||
| echo cleanup | ||||
| find .. -name '*.pyc' -delete | ||||
| find .. -name __pycache__ -delete | ||||
| find -name '*.pyc' -delete | ||||
| find -name __pycache__ -delete | ||||
|  | ||||
| # especially prevent osx from leaking your lan ip (wtf apple) | ||||
| 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 | ||||
| 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.* copyparty/web/dbg-* copyparty/web/Makefile | ||||
| @@ -241,20 +249,42 @@ find | grep -E '\.(js|html)$' | while IFS= read -r f; do | ||||
| 	tmv "$f" | ||||
| done | ||||
|  | ||||
|  | ||||
| gzres() { | ||||
| command -v pigz && | ||||
| 	pk='pigz -11 -J 34 -I 100' || | ||||
| 	pk='gzip' | ||||
| 	command -v pigz && | ||||
| 		pk='pigz -11 -J 34 -I 256' || | ||||
| 		pk='gzip' | ||||
|  | ||||
| echo "$pk" | ||||
| find | grep -E '\.(js|css)$' | grep -vF /deps/ | while IFS= read -r f; do | ||||
| 	echo -n . | ||||
| 	$pk "$f" | ||||
| done | ||||
| echo | ||||
| 	echo "$pk" | ||||
| 	find | grep -E '\.(js|css)$' | grep -vF /deps/ | while IFS= read -r f; do | ||||
| 		echo -n . | ||||
| 		$pk "$f" | ||||
| 	done | ||||
| 	echo | ||||
| } | ||||
|  | ||||
|  | ||||
| zdir="$tmpdir/cpp-mksfx" | ||||
| [ -e "$zdir/$stamp" ] || rm -rf "$zdir" | ||||
| mkdir -p "$zdir" | ||||
| echo a > "$zdir/$stamp" | ||||
| nf=$(ls -1 "$zdir"/arc.* | wc -l) | ||||
| [ $nf -ge 2 ] && [ ! $repack ] && use_zdir=1 || use_zdir= | ||||
|  | ||||
| [ $use_zdir ] || { | ||||
| 	echo "$nf alts += 1" | ||||
| 	gzres | ||||
| 	[ $repack ] || | ||||
| 		tar -cf "$zdir/arc.$(date +%s)" copyparty/web/*.gz | ||||
| } | ||||
| [ $use_zdir ] && { | ||||
| 	arcs=("$zdir"/arc.*) | ||||
| 	arc="${arcs[$RANDOM % ${#arcs[@]} ] }" | ||||
| 	echo "using $arc" | ||||
| 	tar -xf "$arc" | ||||
| 	for f in copyparty/web/*.gz; do | ||||
| 		rm "${f%.*}" | ||||
| 	done | ||||
| } | ||||
| gzres | ||||
|  | ||||
|  | ||||
| echo gen tarlist | ||||
|   | ||||
| @@ -65,9 +65,9 @@ def uncomment(fpath): | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|     print("uncommenting", end="") | ||||
|     print("uncommenting", end="", flush=True) | ||||
|     for f in sys.argv[1:]: | ||||
|         print(".", end="") | ||||
|         print(".", end="", flush=True) | ||||
|         uncomment(f) | ||||
|  | ||||
|     print("k") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user