mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
list recent uploads
also makes the unpost lister 5x faster
This commit is contained in:
parent
3051b13108
commit
eaa4b04a22
14
README.md
14
README.md
|
@ -48,6 +48,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|||
* [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
|
||||
* [rss feeds](#rss-feeds) - monitor a folder with your RSS reader
|
||||
* [recent uploads](#recent-uploads) - list all recent uploads
|
||||
* [media player](#media-player) - plays almost every audio format there is
|
||||
* [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
|
||||
* [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
|
||||
|
@ -717,7 +718,7 @@ files go into `[ok]` if they exist (and you get a link to where it is), otherwis
|
|||
|
||||
### unpost
|
||||
|
||||
undo/delete accidental uploads
|
||||
undo/delete accidental uploads using the `[🧯]` tab in the UI
|
||||
|
||||

|
||||
|
||||
|
@ -876,6 +877,17 @@ url parameters:
|
|||
* uppercase = reverse-sort; `M` = oldest file first
|
||||
|
||||
|
||||
## recent uploads
|
||||
|
||||
list all recent uploads by clicking "show recent uploads" in the controlpanel
|
||||
|
||||
will show uploader IP and upload-time if the visitor has the admin permission
|
||||
|
||||
* global-option `--ups-when` makes upload-time visible to all users, and not just admins
|
||||
|
||||
note that the [🧯 unpost](#unpost) feature is better suited for viewing *your own* recent uploads, as it includes the option to undo/delete them
|
||||
|
||||
|
||||
## media player
|
||||
|
||||
plays almost every audio format there is (if the server has FFmpeg installed for on-demand transcoding)
|
||||
|
|
|
@ -91,6 +91,9 @@ web/mde.html
|
|||
web/mde.js
|
||||
web/msg.css
|
||||
web/msg.html
|
||||
web/rups.css
|
||||
web/rups.html
|
||||
web/rups.js
|
||||
web/shares.css
|
||||
web/shares.html
|
||||
web/shares.js
|
||||
|
|
|
@ -1250,7 +1250,6 @@ 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")
|
||||
|
||||
|
@ -1326,7 +1325,10 @@ def add_admin(ap):
|
|||
ap2.add_argument("--no-reload", action="store_true", help="disable ?reload=cfg (reload users/volumes/volflags from config file)")
|
||||
ap2.add_argument("--no-rescan", action="store_true", help="disable ?scan (volume reindexing)")
|
||||
ap2.add_argument("--no-stack", action="store_true", help="disable ?stack (list all stacks)")
|
||||
ap2.add_argument("--no-ups-page", action="store_true", help="disable ?ru (list of recent uploads)")
|
||||
ap2.add_argument("--no-up-list", action="store_true", help="don't show list of incoming files in controlpanel")
|
||||
ap2.add_argument("--dl-list", metavar="LVL", type=int, default=2, help="who can see active downloads in the controlpanel? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=everyone")
|
||||
ap2.add_argument("--ups-when", action="store_true", help="let everyone see upload timestamps on the ?ru page, not just admins")
|
||||
|
||||
|
||||
def add_thumbnail(ap):
|
||||
|
|
|
@ -1233,6 +1233,9 @@ class HttpCli(object):
|
|||
if "dls" in self.uparam:
|
||||
return self.tx_dls()
|
||||
|
||||
if "ru" in self.uparam:
|
||||
return self.tx_rups()
|
||||
|
||||
if "h" in self.uparam:
|
||||
return self.tx_mounts()
|
||||
|
||||
|
@ -4919,9 +4922,9 @@ class HttpCli(object):
|
|||
raise Pebkac(500, "sqlite3 not found on server; unpost is disabled")
|
||||
raise Pebkac(500, "server busy, cannot unpost; please retry in a bit")
|
||||
|
||||
filt = self.uparam.get("filter") or ""
|
||||
lm = "ups %r" % (filt,)
|
||||
self.log(lm)
|
||||
zs = self.uparam.get("filter") or ""
|
||||
filt = re.compile(zs, re.I) if zs else None
|
||||
lm = "ups %r" % (zs,)
|
||||
|
||||
if self.args.shr and self.vpath.startswith(self.args.shr1):
|
||||
shr_dbv, shr_vrem = self.vn.get_dbv(self.rem)
|
||||
|
@ -4962,13 +4965,18 @@ class HttpCli(object):
|
|||
|
||||
nfk, fk_alg = fk_vols.get(vol) or (0, 0)
|
||||
|
||||
q = "select sz, rd, fn, at from up where ip=? and at>?"
|
||||
n = 2000
|
||||
q = "select sz, rd, fn, at from up where ip=? and at>? order by at desc"
|
||||
for sz, rd, fn, at in cur.execute(q, (self.ip, lim)):
|
||||
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
||||
if filt and filt not in vp:
|
||||
if filt and not filt.search(vp):
|
||||
continue
|
||||
|
||||
rv = {"vp": quotep(vp), "sz": sz, "at": at, "nfk": nfk}
|
||||
n -= 1
|
||||
if not n:
|
||||
break
|
||||
|
||||
rv = {"vp": vp, "sz": sz, "at": at, "nfk": nfk}
|
||||
if nfk:
|
||||
rv["ap"] = vol.canonical(vjoin(rd, fn))
|
||||
rv["fk_alg"] = fk_alg
|
||||
|
@ -4978,9 +4986,13 @@ class HttpCli(object):
|
|||
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
||||
ret = ret[:2000]
|
||||
|
||||
if len(ret) > 2000:
|
||||
ret = ret[:2000]
|
||||
|
||||
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
||||
n = 0
|
||||
for rv in ret[:11000]:
|
||||
|
||||
for rv in ret:
|
||||
rv["vp"] = quotep(rv["vp"])
|
||||
nfk = rv.pop("nfk")
|
||||
if not nfk:
|
||||
continue
|
||||
|
@ -4997,12 +5009,6 @@ class HttpCli(object):
|
|||
)
|
||||
rv["vp"] += "?k=" + fk[:nfk]
|
||||
|
||||
n += 1
|
||||
if n > 2000:
|
||||
break
|
||||
|
||||
ret = ret[:2000]
|
||||
|
||||
if shr_dbv:
|
||||
# translate vpaths from share-target to share-url
|
||||
# to satisfy access checks
|
||||
|
@ -5026,6 +5032,125 @@ class HttpCli(object):
|
|||
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
||||
return True
|
||||
|
||||
def tx_rups(self) -> bool:
|
||||
if self.args.no_ups_page:
|
||||
raise Pebkac(500, "listing of recent uploads is disabled in server config")
|
||||
|
||||
idx = self.conn.get_u2idx()
|
||||
if not idx or not hasattr(idx, "p_end"):
|
||||
if not HAVE_SQLITE3:
|
||||
raise Pebkac(500, "sqlite3 not found on server; recent-uploads n/a")
|
||||
raise Pebkac(500, "server busy, cannot list recent uploads; please retry")
|
||||
|
||||
sfilt = self.uparam.get("filter") or ""
|
||||
filt = re.compile(sfilt, re.I) if sfilt else None
|
||||
lm = "ru %r" % (sfilt,)
|
||||
self.log(lm)
|
||||
|
||||
ret: list[dict[str, Any]] = []
|
||||
t0 = time.time()
|
||||
allvols = [
|
||||
x
|
||||
for x in self.asrv.vfs.all_vols.values()
|
||||
if "e2d" in x.flags and ("*" in x.axs.uread or self.uname in x.axs.uread)
|
||||
]
|
||||
fk_vols = {
|
||||
vol: (vol.flags["fk"], 2 if "fka" in vol.flags else 1)
|
||||
for vol in allvols
|
||||
if "fk" in vol.flags and "*" not in vol.axs.uread
|
||||
}
|
||||
|
||||
for vol in allvols:
|
||||
cur = idx.get_cur(vol)
|
||||
if not cur:
|
||||
continue
|
||||
|
||||
nfk, fk_alg = fk_vols.get(vol) or (0, 0)
|
||||
adm = "*" in vol.axs.uadmin or self.uname in vol.axs.uadmin
|
||||
dots = "*" in vol.axs.udot or self.uname in vol.axs.udot
|
||||
|
||||
n = 2000
|
||||
q = "select sz, rd, fn, ip, at from up where at>0 order by at desc"
|
||||
for sz, rd, fn, ip, at in cur.execute(q):
|
||||
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
||||
if filt and not filt.search(vp):
|
||||
continue
|
||||
|
||||
if not dots and "/." in vp:
|
||||
continue
|
||||
|
||||
n -= 1
|
||||
if not n:
|
||||
break
|
||||
|
||||
rv = {
|
||||
"vp": vp,
|
||||
"sz": sz,
|
||||
"ip": ip,
|
||||
"at": at,
|
||||
"nfk": nfk,
|
||||
"adm": adm,
|
||||
}
|
||||
if nfk:
|
||||
rv["ap"] = vol.canonical(vjoin(rd, fn))
|
||||
rv["fk_alg"] = fk_alg
|
||||
|
||||
ret.append(rv)
|
||||
if len(ret) > 3000:
|
||||
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
||||
ret = ret[:2000]
|
||||
|
||||
if len(ret) > 2000:
|
||||
ret = ret[:2000]
|
||||
|
||||
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
||||
|
||||
for rv in ret:
|
||||
rv["evp"] = quotep(rv["vp"])
|
||||
nfk = rv.pop("nfk")
|
||||
if not nfk:
|
||||
continue
|
||||
|
||||
alg = rv.pop("fk_alg")
|
||||
ap = rv.pop("ap")
|
||||
try:
|
||||
st = bos.stat(ap)
|
||||
except:
|
||||
continue
|
||||
|
||||
fk = self.gen_fk(
|
||||
alg, self.args.fk_salt, ap, st.st_size, 0 if ANYWIN else st.st_ino
|
||||
)
|
||||
rv["vp"] += "?k=" + fk[:nfk]
|
||||
|
||||
if self.args.ups_when:
|
||||
for rv in ret:
|
||||
adm = rv.pop("adm")
|
||||
if not adm:
|
||||
rv["ip"] = "(You)" if rv["ip"] == self.ip else "(?)"
|
||||
else:
|
||||
for rv in ret:
|
||||
adm = rv.pop("adm")
|
||||
if not adm:
|
||||
rv["ip"] = "(You)" if rv["ip"] == self.ip else "(?)"
|
||||
rv["at"] = 0
|
||||
|
||||
if self.is_vproxied:
|
||||
for v in ret:
|
||||
v["vp"] = self.args.SR + v["vp"]
|
||||
|
||||
self.log("%s #%d %.2fsec" % (lm, len(ret), time.time() - t0))
|
||||
|
||||
if "j" in self.ouparam:
|
||||
jtxt = json.dumps(ret, separators=(",\n", ": "))
|
||||
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
||||
return True
|
||||
|
||||
rows = [[x["vp"], x["evp"], x["sz"], x["ip"], x["at"]] for x in ret]
|
||||
html = self.j2s("rups", this=self, rows=rows, filt=sfilt, now=int(time.time()))
|
||||
self.reply(html.encode("utf-8"), status=200)
|
||||
return True
|
||||
|
||||
def tx_shares(self) -> bool:
|
||||
if self.uname == "*":
|
||||
self.loud_reply("you're not logged in")
|
||||
|
|
|
@ -172,15 +172,16 @@ class HttpSrv(object):
|
|||
env = jinja2.Environment()
|
||||
env.loader = jinja2.FunctionLoader(lambda f: load_jinja2_resource(self.E, f))
|
||||
jn = [
|
||||
"splash",
|
||||
"shares",
|
||||
"svcs",
|
||||
"browser",
|
||||
"browser2",
|
||||
"msg",
|
||||
"cf",
|
||||
"md",
|
||||
"mde",
|
||||
"cf",
|
||||
"msg",
|
||||
"rups",
|
||||
"shares",
|
||||
"splash",
|
||||
"svcs",
|
||||
]
|
||||
self.j2 = {x: env.get_template(x + ".html") for x in jn}
|
||||
self.prism = has_resource(self.E, "web/deps/prism.js.gz")
|
||||
|
|
113
copyparty/web/rups.css
Normal file
113
copyparty/web/rups.css
Normal file
|
@ -0,0 +1,113 @@
|
|||
html {
|
||||
color: #333;
|
||||
background: #f7f7f7;
|
||||
font-family: sans-serif;
|
||||
font-family: var(--font-main), sans-serif;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
#wrap {
|
||||
margin: 2em auto;
|
||||
padding: 0 1em 3em 1em;
|
||||
line-height: 2.3em;
|
||||
}
|
||||
form {
|
||||
display: inline;
|
||||
padding-left: 1em;
|
||||
}
|
||||
input[type=submit],
|
||||
a {
|
||||
color: #047;
|
||||
background: #fff;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
border-bottom: 1px solid #8ab;
|
||||
border-radius: .2em;
|
||||
padding: .2em .6em;
|
||||
margin: 0 .3em;
|
||||
}
|
||||
#wrap td a {
|
||||
margin: 0;
|
||||
line-height: 1em;
|
||||
display: inline-block;
|
||||
white-space: initial;
|
||||
font-family: var(--font-main), sans-serif;
|
||||
}
|
||||
#repl {
|
||||
border: none;
|
||||
background: none;
|
||||
color: inherit;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
bottom: .25em;
|
||||
left: .2em;
|
||||
}
|
||||
#wrap table {
|
||||
border-collapse: collapse;
|
||||
position: relative;
|
||||
margin-top: 2em;
|
||||
}
|
||||
#wrap th {
|
||||
top: -1px;
|
||||
position: sticky;
|
||||
background: #f7f7f7;
|
||||
}
|
||||
#wrap td {
|
||||
font-family: var(--font-mono), monospace, monospace;
|
||||
white-space: pre;
|
||||
}
|
||||
#wrap th:first-child,
|
||||
#wrap td:first-child {
|
||||
text-align: right;
|
||||
}
|
||||
#wrap td,
|
||||
#wrap th {
|
||||
text-align: left;
|
||||
padding: .3em .6em;
|
||||
max-width: 30vw;
|
||||
}
|
||||
#wrap tr:hover td {
|
||||
background: #ddd;
|
||||
box-shadow: 0 -1px 0 rgba(128, 128, 128, 0.5) inset;
|
||||
}
|
||||
#wrap th:first-child,
|
||||
#wrap td:first-child {
|
||||
border-radius: .5em 0 0 .5em;
|
||||
}
|
||||
#wrap th:last-child,
|
||||
#wrap td:last-child {
|
||||
border-radius: 0 .5em .5em 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
html.z {
|
||||
background: #222;
|
||||
color: #ccc;
|
||||
}
|
||||
html.bz {
|
||||
background: #11121d;
|
||||
color: #bbd;
|
||||
}
|
||||
html.z input[type=submit],
|
||||
html.z a {
|
||||
color: #fff;
|
||||
background: #057;
|
||||
border-color: #37a;
|
||||
}
|
||||
html.z input[type=text] {
|
||||
color: #ddd;
|
||||
background: #223;
|
||||
border: none;
|
||||
border-bottom: 1px solid #fc5;
|
||||
border-radius: .2em;
|
||||
padding: .2em .3em;
|
||||
}
|
||||
html.z #wrap th {
|
||||
background: #222;
|
||||
}
|
||||
html.bz #wrap th {
|
||||
background: #223;
|
||||
}
|
||||
html.z #wrap tr:hover td {
|
||||
background: #000;
|
||||
}
|
67
copyparty/web/rups.html
Normal file
67
copyparty/web/rups.html
Normal file
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ s_doctitle }}</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
||||
<meta name="theme-color" content="#{{ tcolor }}">
|
||||
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/rups.css?_={{ ts }}">
|
||||
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
||||
{{ html_head }}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="wrap">
|
||||
<a id="a" href="{{ r }}/?ru" class="af">refresh</a>
|
||||
<a id="a" href="{{ r }}/?h" class="af">control-panel</a>
|
||||
<form method="get" enctype="application/x-www-form-urlencoded" accept-charset="utf-8" action="{{ r }}">
|
||||
<input type="hidden" name="ru" value="a" />
|
||||
Filter: <input type="text" name="filter" size="20" placeholder="documents/passwords" value="{{ filt }}" />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
<span id="hits"></span>
|
||||
<table id="tab"><thead><tr>
|
||||
<th>size</th>
|
||||
<th>who</th>
|
||||
<th>when</th>
|
||||
<th>age</th>
|
||||
<th>dir</th>
|
||||
<th>file</th>
|
||||
</tr></thead><tbody>
|
||||
{% for vp, evp, sz, ip, at in rows %}
|
||||
<tr>
|
||||
<td>{{ sz }}</td>
|
||||
<td>{{ ip }}</td>
|
||||
<td>{{ at }}</td>
|
||||
<td>{{ (now-at) }}</td>
|
||||
<td></td>
|
||||
<td><a href="{{ r }}{{ evp }}">{{ vp|e }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody></table>
|
||||
{% if not rows %}
|
||||
(the database is not aware of any uploads)
|
||||
{% endif %}
|
||||
</div>
|
||||
<a href="#" id="repl">π</a>
|
||||
<script>
|
||||
|
||||
var SR = {{ r|tojson }},
|
||||
NOW = {{ now }},
|
||||
lang="{{ lang }}",
|
||||
dfavico="{{ favico }}";
|
||||
|
||||
var STG = window.localStorage;
|
||||
document.documentElement.className = (STG && STG.cpp_thm) || "{{ this.args.theme }}";
|
||||
|
||||
</script>
|
||||
<script src="{{ r }}/.cpr/util.js?_={{ ts }}"></script>
|
||||
<script src="{{ r }}/.cpr/rups.js?_={{ ts }}"></script>
|
||||
{%- if js %}
|
||||
<script src="{{ js }}_={{ ts }}"></script>
|
||||
{%- endif %}
|
||||
</body>
|
||||
</html>
|
||||
|
34
copyparty/web/rups.js
Normal file
34
copyparty/web/rups.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
(function() {
|
||||
var tab = ebi('tab').tBodies[0],
|
||||
tr = Array.prototype.slice.call(tab.rows, 0),
|
||||
rows = [];
|
||||
|
||||
for (var a = 0; a < tr.length; a++) {
|
||||
var td = tr[a].cells,
|
||||
an = td[5].children[0];
|
||||
|
||||
rows.push([
|
||||
td[0].textContent,
|
||||
td[2].textContent,
|
||||
td[3].textContent,
|
||||
an.textContent,
|
||||
an.getAttribute('href'),
|
||||
]);
|
||||
}
|
||||
|
||||
for (var a = 0; a < rows.length; a++) {
|
||||
var t = rows[a],
|
||||
sz = t[0],
|
||||
at = parseInt(t[1]),
|
||||
nam = vsplit(t[3]),
|
||||
dh = vsplit(t[4])[0];
|
||||
|
||||
tr[a].cells[0].innerHTML = sz.replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
||||
tr[a].cells[2].innerHTML = at ? unix2iso(at) : '(?)';
|
||||
tr[a].cells[3].innerHTML = at ? shumantime(t[2]) : '(?)';
|
||||
tr[a].cells[4].innerHTML = '<a href="' + dh + '">' + nam[0] + '</a>';
|
||||
tr[a].cells[5].children[0].innerHTML = nam[1].split('?')[0];
|
||||
}
|
||||
|
||||
ebi('hits').innerHTML = '-- showing ' + rows.length + ' files';
|
||||
})();
|
|
@ -44,9 +44,10 @@ a {
|
|||
bottom: .25em;
|
||||
left: .2em;
|
||||
}
|
||||
table {
|
||||
#wrap table {
|
||||
border-collapse: collapse;
|
||||
position: relative;
|
||||
position: relative;
|
||||
margin-top: 2em;
|
||||
}
|
||||
th {
|
||||
top: -1px;
|
||||
|
@ -62,6 +63,14 @@ th {
|
|||
#wrap td+td+td+td+td+td+td+td {
|
||||
font-family: var(--font-mono), monospace, monospace;
|
||||
}
|
||||
#wrap th:first-child,
|
||||
#wrap td:first-child {
|
||||
border-radius: .5em 0 0 .5em;
|
||||
}
|
||||
#wrap th:last-child,
|
||||
#wrap td:last-child {
|
||||
border-radius: 0 .5em .5em 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -81,3 +90,6 @@ html.bz {
|
|||
color: #bbd;
|
||||
background: #11121d;
|
||||
}
|
||||
html.bz th {
|
||||
background: #223;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@
|
|||
{% if not rows %}
|
||||
(you don't have any active shares btw)
|
||||
{% endif %}
|
||||
</div>
|
||||
<a href="#" id="repl">π</a>
|
||||
<script>
|
||||
|
||||
var SR = {{ r|tojson }},
|
||||
|
|
|
@ -157,6 +157,7 @@
|
|||
<blockquote id="ad">enabling no304 will disable all caching; try this if k304 wasn't enough. This will waste a huge amount of network traffic!</blockquote></li>
|
||||
{% endif %}
|
||||
|
||||
<li><a id="af" href="{{ r }}/?ru">show recent uploads</a></li>
|
||||
<li><a id="k" href="{{ r }}/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li>
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ var Ls = {
|
|||
"ac1": "skru på no304",
|
||||
"ad1": "no304 stopper all bruk av cache. Hvis ikke k304 var nok, prøv denne. Vil mangedoble dataforbruk!",
|
||||
"ae1": "utgående:",
|
||||
"af1": "vis nylig opplastede filer",
|
||||
},
|
||||
"eng": {
|
||||
"d2": "shows the state of all active threads",
|
||||
|
@ -88,6 +89,7 @@ var Ls = {
|
|||
"ac1": "开启 k304",
|
||||
"ad1": "启用 no304 将禁用所有缓存;如果 k304 不够,可以尝试此选项。这将消耗大量的网络流量!", //m
|
||||
"ae1": "正在下载:", //m
|
||||
"af1": "显示最近上传的文件", //m
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -143,6 +143,9 @@ authenticate using header `Cookie: cppwd=foo` or url param `&pw=foo`
|
|||
| GET | `?dls` | show active downloads (do this as admin) |
|
||||
| GET | `?ups` | show recent uploads from your IP |
|
||||
| GET | `?ups&filter=f` | ...where URL contains `f` |
|
||||
| GET | `?ru` | show all recent uploads |
|
||||
| GET | `?ru&filter=f` | ...where URL contains `f` |
|
||||
| GET | `?ru&j` | ...as json |
|
||||
| GET | `?mime=foo` | specify return mimetype `foo` |
|
||||
| GET | `?v` | render markdown file at URL |
|
||||
| GET | `?v` | open image/video/audio in mediaplayer |
|
||||
|
|
|
@ -105,6 +105,9 @@ copyparty/web/mde.html,
|
|||
copyparty/web/mde.js,
|
||||
copyparty/web/msg.css,
|
||||
copyparty/web/msg.html,
|
||||
copyparty/web/rups.css,
|
||||
copyparty/web/rups.html,
|
||||
copyparty/web/rups.js,
|
||||
copyparty/web/shares.css,
|
||||
copyparty/web/shares.html,
|
||||
copyparty/web/shares.js,
|
||||
|
|
|
@ -80,6 +80,7 @@ var tl_cpanel = {
|
|||
"ac1": "enable no304",
|
||||
"ad1": "enabling no304 will disable all caching; try this if k304 wasn't enough. This will waste a huge amount of network traffic!",
|
||||
"ae1": "active downloads:",
|
||||
"af1": "show recent uploads",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ var tl_cpanel = {{
|
|||
"ac1": "enable no304",
|
||||
"ad1": "enabling no304 will disable all caching; try this if k304 wasn't enough. This will waste a huge amount of network traffic!",
|
||||
"ae1": "active downloads:",
|
||||
"af1": "show recent uploads",
|
||||
}},
|
||||
}};
|
||||
|
||||
|
|
Loading…
Reference in a new issue