Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca04a00662 | ||
|
|
8a09601be8 | ||
|
|
1fe0d4693e | ||
|
|
bba8a3c6bc | ||
|
|
e3d7f0c7d5 | ||
|
|
be7bb71bbc | ||
|
|
e0c4829ec6 | ||
|
|
5af1575329 | ||
|
|
884f966b86 | ||
|
|
f6c6fbc223 |
12
README.md
12
README.md
@@ -12,7 +12,7 @@ turn your phone or raspi into a portable file server with resumable uploads/down
|
||||
* browse/upload with [IE4](#browser-support) / netscape4.0 on win3.11 (heh)
|
||||
* protocols: [http](#the-browser) // [ftp](#ftp-server) // [webdav](#webdav-server) // [smb/cifs](#smb-server)
|
||||
|
||||
try the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running from a basement in finland
|
||||
**[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running from a basement in finland
|
||||
|
||||
📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer)
|
||||
|
||||
@@ -108,6 +108,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
|
||||
|
||||
* or install through pypi (python3 only): `python3 -m pip install --user -U copyparty`
|
||||
* or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
|
||||
* or if you are on android, [install copyparty in termux](#install-on-android)
|
||||
* or if you prefer to [use docker](./scripts/docker/) 🐋 you can do that too
|
||||
* docker has all deps built-in, so skip this step:
|
||||
|
||||
@@ -165,11 +166,15 @@ firewall-cmd --reload
|
||||
* ☑ [smb/cifs server](#smb-server)
|
||||
* ☑ [qr-code](#qr-code) for quick access
|
||||
* ☑ [upnp / zeroconf / mdns / ssdp](#zeroconf)
|
||||
* ☑ [event hooks](#event-hooks) / script runner
|
||||
* ☑ [reverse-proxy support](https://github.com/9001/copyparty#reverse-proxy)
|
||||
* upload
|
||||
* ☑ basic: plain multipart, ie6 support
|
||||
* ☑ [up2k](#uploading): js, resumable, multithreaded
|
||||
* unaffected by cloudflare's max-upload-size (100 MiB)
|
||||
* ☑ stash: simple PUT filedropper
|
||||
* ☑ filename randomizer
|
||||
* ☑ write-only folders
|
||||
* ☑ [unpost](#unpost): undo/delete accidental uploads
|
||||
* ☑ [self-destruct](#self-destruct) (specified server-side or client-side)
|
||||
* ☑ symlink/discard existing files (content-matching)
|
||||
@@ -193,10 +198,15 @@ firewall-cmd --reload
|
||||
* ☑ [locate files by contents](#file-search)
|
||||
* ☑ search by name/path/date/size
|
||||
* ☑ [search by ID3-tags etc.](#searching)
|
||||
* client support
|
||||
* ☑ [sync folder to server](https://github.com/9001/copyparty/tree/hovudstraum/bin#up2kpy)
|
||||
* ☑ [curl-friendly](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png)
|
||||
* markdown
|
||||
* ☑ [viewer](#markdown-viewer)
|
||||
* ☑ editor (sure why not)
|
||||
|
||||
PS: something missing? post any crazy ideas you've got as a [feature request](https://github.com/9001/copyparty/issues/new?assignees=9001&labels=enhancement&template=feature_request.md) or [discussion](https://github.com/9001/copyparty/discussions/new?category=ideas) 🤙
|
||||
|
||||
|
||||
## testimonials
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Maintainer: icxes <dev.null@need.moe>
|
||||
pkgname=copyparty
|
||||
pkgver="1.6.7"
|
||||
pkgver="1.6.8"
|
||||
pkgrel=1
|
||||
pkgdesc="Portable file sharing hub"
|
||||
arch=("any")
|
||||
@@ -26,7 +26,7 @@ source=("${url}/releases/download/v${pkgver}/${pkgname}-sfx.py"
|
||||
"https://raw.githubusercontent.com/9001/${pkgname}/v${pkgver}/LICENSE"
|
||||
)
|
||||
backup=("etc/${pkgname}.d/init" )
|
||||
sha256sums=("3fb40a631e9decf0073db06aab6fd8d743de91f4ddb82a65164d39d53e0b413f"
|
||||
sha256sums=("744037ef9ff70e7a6ec066272b4bb24e259df6faa4472ab92a768d2f801ec2aa"
|
||||
"b8565eba5e64dedba1cf6c7aac7e31c5a731ed7153d6810288a28f00a36c28b2"
|
||||
"f65c207e0670f9d78ad2e399bda18d5502ff30d2ac79e0e7fc48e7fbdc39afdc"
|
||||
"c4f396b083c9ec02ad50b52412c84d2a82be7f079b2d016e1c9fad22d68285ff"
|
||||
|
||||
@@ -946,6 +946,7 @@ def add_ui(ap, retry):
|
||||
ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
|
||||
ap2.add_argument("--css-browser", metavar="L", type=u, help="URL to additional CSS to include")
|
||||
ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages")
|
||||
ap2.add_argument("--ih", action="store_true", help="if a folder contains index.html, show that instead of the directory listing by default (can be changed in the client settings UI)")
|
||||
ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
|
||||
ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")
|
||||
ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty", help="title / service-name to show in html documents")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# coding: utf-8
|
||||
|
||||
VERSION = (1, 6, 8)
|
||||
VERSION = (1, 6, 9)
|
||||
CODENAME = "cors k"
|
||||
BUILD_DT = (2023, 3, 12)
|
||||
BUILD_DT = (2023, 3, 16)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
||||
@@ -129,6 +129,7 @@ class FtpFs(AbstractedFS):
|
||||
|
||||
def die(self, msg):
|
||||
self.h.die(msg)
|
||||
raise Exception()
|
||||
|
||||
def v2a(
|
||||
self,
|
||||
|
||||
@@ -778,8 +778,8 @@ class HttpCli(object):
|
||||
if "k304" in self.uparam:
|
||||
return self.set_k304()
|
||||
|
||||
if "am_js" in self.uparam:
|
||||
return self.set_am_js()
|
||||
if "setck" in self.uparam:
|
||||
return self.setck()
|
||||
|
||||
if "reset" in self.uparam:
|
||||
return self.set_cfg_reset()
|
||||
@@ -1757,12 +1757,13 @@ class HttpCli(object):
|
||||
hits = idx.fsearch(vols, body)
|
||||
msg: Any = repr(hits)
|
||||
taglist: list[str] = []
|
||||
trunc = False
|
||||
else:
|
||||
# search by query params
|
||||
q = body["q"]
|
||||
n = body.get("n", self.args.srch_hits)
|
||||
self.log("qj: {} |{}|".format(q, n))
|
||||
hits, taglist = idx.search(vols, q, n)
|
||||
hits, taglist, trunc = idx.search(vols, q, n)
|
||||
msg = len(hits)
|
||||
|
||||
idx.p_end = time.time()
|
||||
@@ -1782,7 +1783,8 @@ class HttpCli(object):
|
||||
for hit in hits:
|
||||
hit["rp"] = self.args.RS + hit["rp"]
|
||||
|
||||
r = json.dumps({"hits": hits, "tag_order": order}).encode("utf-8")
|
||||
rj = {"hits": hits, "tag_order": order, "trunc": trunc}
|
||||
r = json.dumps(rj).encode("utf-8")
|
||||
self.reply(r, mime="application/json")
|
||||
return True
|
||||
|
||||
@@ -2896,15 +2898,16 @@ class HttpCli(object):
|
||||
self.redirect("", "?h#cc")
|
||||
return True
|
||||
|
||||
def set_am_js(self) -> bool:
|
||||
v = "n" if self.uparam["am_js"] == "n" else "y"
|
||||
ck = gencookie("js", v, self.args.R, False, 86400 * 299)
|
||||
def setck(self) -> bool:
|
||||
k, v = self.uparam["setck"].split("=", 1)
|
||||
t = None if v == "" else 86400 * 299
|
||||
ck = gencookie(k, v, self.args.R, False, t)
|
||||
self.out_headerlist.append(("Set-Cookie", ck))
|
||||
self.reply(b"promoted\n")
|
||||
self.reply(b"o7\n")
|
||||
return True
|
||||
|
||||
def set_cfg_reset(self) -> bool:
|
||||
for k in ("k304", "js", "cppwd", "cppws"):
|
||||
for k in ("k304", "js", "idxh", "cppwd", "cppws"):
|
||||
cookie = gencookie(k, "x", self.args.R, False, None)
|
||||
self.out_headerlist.append(("Set-Cookie", cookie))
|
||||
|
||||
@@ -3433,6 +3436,7 @@ class HttpCli(object):
|
||||
"dtheme": self.args.theme,
|
||||
"themes": self.args.themes,
|
||||
"turbolvl": self.args.turbo,
|
||||
"idxh": int(self.args.ih),
|
||||
"u2sort": self.args.u2sort,
|
||||
}
|
||||
|
||||
@@ -3566,6 +3570,20 @@ class HttpCli(object):
|
||||
files.append(item)
|
||||
item["rd"] = rem
|
||||
|
||||
if (
|
||||
self.cookies.get("idxh") == "y"
|
||||
and "ls" not in self.uparam
|
||||
and "v" not in self.uparam
|
||||
):
|
||||
idx_html = set(["index.htm", "index.html"])
|
||||
for item in files:
|
||||
if item["name"] in idx_html:
|
||||
# do full resolve in case of shadowed file
|
||||
vp = vjoin(self.vpath.split("?")[0], item["name"])
|
||||
vn, rem = self.asrv.vfs.get(vp, self.uname, True, False)
|
||||
ap = vn.canonical(rem)
|
||||
return self.tx_file(ap) # is no-cache
|
||||
|
||||
tagset: set[str] = set()
|
||||
for fe in files:
|
||||
fn = fe["name"]
|
||||
|
||||
@@ -120,10 +120,10 @@ class U2idx(object):
|
||||
|
||||
def search(
|
||||
self, vols: list[tuple[str, str, dict[str, Any]]], uq: str, lim: int
|
||||
) -> tuple[list[dict[str, Any]], list[str]]:
|
||||
) -> tuple[list[dict[str, Any]], list[str], bool]:
|
||||
"""search by query params"""
|
||||
if not HAVE_SQLITE3:
|
||||
return [], []
|
||||
return [], [], False
|
||||
|
||||
q = ""
|
||||
v: Union[str, int] = ""
|
||||
@@ -275,7 +275,7 @@ class U2idx(object):
|
||||
have_up: bool,
|
||||
have_mt: bool,
|
||||
lim: int,
|
||||
) -> tuple[list[dict[str, Any]], list[str]]:
|
||||
) -> tuple[list[dict[str, Any]], list[str], bool]:
|
||||
done_flag: list[bool] = []
|
||||
self.active_id = "{:.6f}_{}".format(
|
||||
time.time(), threading.current_thread().ident
|
||||
@@ -316,9 +316,6 @@ class U2idx(object):
|
||||
c = cur.execute(uq, tuple(vuv))
|
||||
for hit in c:
|
||||
w, ts, sz, rd, fn, ip, at = hit[:7]
|
||||
lim -= 1
|
||||
if lim < 0:
|
||||
break
|
||||
|
||||
if rd.startswith("//") or fn.startswith("//"):
|
||||
rd, fn = s3dec(rd, fn)
|
||||
@@ -346,6 +343,10 @@ class U2idx(object):
|
||||
)[:fk]
|
||||
)
|
||||
|
||||
lim -= 1
|
||||
if lim < 0:
|
||||
break
|
||||
|
||||
seen_rps.add(rp)
|
||||
sret.append({"ts": int(ts), "sz": sz, "rp": rp + suf, "w": w[:16]})
|
||||
|
||||
@@ -368,7 +369,7 @@ class U2idx(object):
|
||||
|
||||
ret.sort(key=itemgetter("rp"))
|
||||
|
||||
return ret, list(taglist.keys())
|
||||
return ret, list(taglist.keys()), lim < 0
|
||||
|
||||
def terminator(self, identifier: str, done_flag: list[bool]) -> None:
|
||||
for _ in range(self.timeout):
|
||||
|
||||
@@ -1251,12 +1251,15 @@ class Up2k(object):
|
||||
if n_rm2:
|
||||
self.log("forgetting {} shadowed deleted files".format(n_rm2))
|
||||
|
||||
c2.connection.commit()
|
||||
|
||||
# then covers
|
||||
n_rm3 = 0
|
||||
qu = "select 1 from up where rd=? and +fn=? limit 1"
|
||||
q = "delete from cv where rd=? and dn=? and +fn=?"
|
||||
for crd, cdn, fn in cur.execute("select * from cv"):
|
||||
ap = os.path.join(top, crd, cdn, fn)
|
||||
if not bos.path.exists(ap):
|
||||
urd = vjoin(crd, cdn)
|
||||
if not c2.execute(qu, (urd, fn)).fetchone():
|
||||
c2.execute(q, (crd, cdn, fn))
|
||||
n_rm3 += 1
|
||||
|
||||
|
||||
@@ -796,6 +796,8 @@ html.y #path a:hover {
|
||||
}
|
||||
.logue {
|
||||
padding: .2em 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.logue.hidden,
|
||||
.logue:empty {
|
||||
|
||||
@@ -155,6 +155,7 @@
|
||||
sb_lg = "{{ sb_lg }}",
|
||||
lifetime = {{ lifetime }},
|
||||
turbolvl = {{ turbolvl }},
|
||||
idxh = {{ idxh }},
|
||||
frand = {{ frand|tojson }},
|
||||
u2sort = "{{ u2sort }}",
|
||||
have_emp = {{ have_emp|tojson }},
|
||||
|
||||
@@ -193,6 +193,7 @@ var Ls = {
|
||||
"ct_dots": "show hidden files (if server permits)",
|
||||
"ct_dir1st": "sort folders before files",
|
||||
"ct_readme": "show README.md in folder listings",
|
||||
"ct_idxh": "show index.html instead of folder listing",
|
||||
"ct_sbars": "show scrollbars",
|
||||
|
||||
"cut_turbo": "the yolo button, you probably DO NOT want to enable this:$N$Nuse this if you were uploading a huge amount of files and had to restart for some reason, and want to continue the upload ASAP$N$Nthis replaces the hash-check with a simple <em>"does this have the same filesize on the server?"</em> so if the file contents are different it will NOT be uploaded$N$Nyou should turn this off when the upload is done, and then "upload" the same files again to let the client verify them",
|
||||
@@ -652,6 +653,7 @@ var Ls = {
|
||||
"ct_dots": "vis skjulte filer (gitt at serveren tillater det)",
|
||||
"ct_dir1st": "sorter slik at mapper kommer foran filer",
|
||||
"ct_readme": "vis README.md nedenfor filene",
|
||||
"ct_idxh": "vis index.html istedenfor fil-liste",
|
||||
"ct_sbars": "vis rullgardiner / skrollefelt",
|
||||
|
||||
"cut_turbo": "forenklet befaring ved opplastning; bør sannsynlig <em>ikke</em> skrus på:$N$Nnyttig dersom du var midt i en svær opplastning som måtte restartes av en eller annen grunn, og du vil komme igang igjen så raskt som overhodet mulig.$N$Nnår denne er skrudd på så forenkles befaringen kraftig; istedenfor å utføre en trygg sjekk på om filene finnes på serveren i god stand, så sjekkes kun om <em>filstørrelsen</em> stemmer. Så dersom en korrupt fil skulle befinne seg på serveren allerede, på samme sted med samme størrelse og navn, så blir det <em>ikke oppdaget</em>.$N$Ndet anbefales å kun benytte denne funksjonen for å komme seg raskt igjennom selve opplastningen, for så å skru den av, og til slutt "laste opp" de samme filene én gang til -- slik at integriteten kan verifiseres",
|
||||
@@ -1083,6 +1085,7 @@ ebi('op_cfg').innerHTML = (
|
||||
' <a id="dotfiles" class="tgl btn" href="#" tt="' + L.ct_dots + '">dotfiles</a>\n' +
|
||||
' <a id="dir1st" class="tgl btn" href="#" tt="' + L.ct_dir1st + '">📁 first</a>\n' +
|
||||
' <a id="ireadme" class="tgl btn" href="#" tt="' + L.ct_readme + '">📜 readme</a>\n' +
|
||||
' <a id="idxh" class="tgl btn" href="#" tt="' + L.ct_idxh + '">htm</a>\n' +
|
||||
' <a id="sbars" class="tgl btn" href="#" tt="' + L.ct_sbars + '">⟊</a>\n' +
|
||||
' </div>\n' +
|
||||
'</div>\n' +
|
||||
@@ -1278,6 +1281,7 @@ function set_files_html(html) {
|
||||
|
||||
|
||||
var ACtx = window.AudioContext || window.webkitAudioContext,
|
||||
noih = /[?&]v\b/.exec('' + location),
|
||||
hash0 = location.hash,
|
||||
mp;
|
||||
|
||||
@@ -4872,7 +4876,7 @@ document.onkeydown = function (e) {
|
||||
|
||||
var html = mk_files_header(tagord), seen = {};
|
||||
html.push('<tbody>');
|
||||
html.push('<tr class="srch_hdr"><td>-</td><td><a href="#" id="unsearch"><big style="font-weight:bold">[❌] ' + L.sl_close + '</big></a> -- ' + L.sl_hits.format(res.hits.length) + (res.hits.length == cap ? ' -- <a href="#" id="moar">' + L.sl_moar + '</a>' : '') + '</td></tr>');
|
||||
html.push('<tr class="srch_hdr"><td>-</td><td><a href="#" id="unsearch"><big style="font-weight:bold">[❌] ' + L.sl_close + '</big></a> -- ' + L.sl_hits.format(res.hits.length) + (res.trunc ? ' -- <a href="#" id="moar">' + L.sl_moar + '</a>' : '') + '</td></tr>');
|
||||
|
||||
for (var a = 0; a < res.hits.length; a++) {
|
||||
var r = res.hits[a],
|
||||
@@ -4986,6 +4990,7 @@ var treectl = (function () {
|
||||
treesz = clamp(icfg_get('treesz', 16), 10, 50);
|
||||
|
||||
bcfg_bind(r, 'ireadme', 'ireadme', true);
|
||||
bcfg_bind(r, 'idxh', 'idxh', idxh, setidxh);
|
||||
bcfg_bind(r, 'dyn', 'dyntree', true, onresize);
|
||||
bcfg_bind(r, 'dots', 'dotfiles', false, function (v) {
|
||||
r.goto(get_evpath());
|
||||
@@ -5010,6 +5015,16 @@ var treectl = (function () {
|
||||
}
|
||||
setwrap(r.wtree);
|
||||
|
||||
function setidxh(v) {
|
||||
if (!v == !/\bidxh=y\b/.exec('' + document.cookie))
|
||||
return;
|
||||
|
||||
var xhr = new XHR();
|
||||
xhr.open('GET', SR + '/?setck=idxh=' + (v ? 'y' : 'n'), true);
|
||||
xhr.send();
|
||||
}
|
||||
setidxh(r.idxh);
|
||||
|
||||
r.entree = function (e, nostore) {
|
||||
ev(e);
|
||||
entreed = true;
|
||||
@@ -5438,6 +5453,9 @@ var treectl = (function () {
|
||||
return;
|
||||
}
|
||||
|
||||
if (r.chk_index_html(this.top, res))
|
||||
return;
|
||||
|
||||
for (var a = 0; a < res.files.length; a++)
|
||||
if (res.files[a].tags === undefined)
|
||||
res.files[a].tags = {};
|
||||
@@ -5485,6 +5503,17 @@ var treectl = (function () {
|
||||
}
|
||||
}
|
||||
|
||||
r.chk_index_html = function (top, res) {
|
||||
if (!r.idxh || !res || !res.files || noih)
|
||||
return;
|
||||
|
||||
for (var a = 0; a < res.files.length; a++)
|
||||
if (/^index.html?(\?|$)/i.exec(res.files[a].href)) {
|
||||
window.location = vjoin(top, res.files[a].href);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
r.gentab = function (top, res) {
|
||||
var nodes = res.dirs.concat(res.files),
|
||||
html = mk_files_header(res.taglist),
|
||||
@@ -5605,14 +5634,18 @@ var treectl = (function () {
|
||||
qsr('#bbsw');
|
||||
if (ls0 === null) {
|
||||
var xhr = new XHR();
|
||||
xhr.open('GET', SR + '/?am_js', true);
|
||||
xhr.open('GET', SR + '/?setck=js=y', true);
|
||||
xhr.send();
|
||||
|
||||
r.ls_cb = showfile.addlinks;
|
||||
return r.reqls(get_evpath(), false);
|
||||
}
|
||||
|
||||
r.gentab(get_evpath(), ls0);
|
||||
var top = get_evpath();
|
||||
if (r.chk_index_html(top, ls0))
|
||||
return;
|
||||
|
||||
r.gentab(top, ls0);
|
||||
pbar.onresize();
|
||||
vbar.onresize();
|
||||
showfile.addlinks();
|
||||
|
||||
@@ -632,6 +632,29 @@ function vsplit(vp) {
|
||||
}
|
||||
|
||||
|
||||
function vjoin(p1, p2) {
|
||||
if (!p1)
|
||||
p1 = '';
|
||||
|
||||
if (!p2)
|
||||
p2 = '';
|
||||
|
||||
if (p1.endsWith('/'))
|
||||
p1 = p1.slice(0, -1);
|
||||
|
||||
if (p2.startsWith('/'))
|
||||
p2 = p2.slice(1);
|
||||
|
||||
if (!p1)
|
||||
return p2;
|
||||
|
||||
if (!p2)
|
||||
return p1;
|
||||
|
||||
return p1 + '/' + p2;
|
||||
}
|
||||
|
||||
|
||||
function uricom_enc(txt, do_fb_enc) {
|
||||
try {
|
||||
return encodeURIComponent(txt);
|
||||
@@ -1183,13 +1206,13 @@ var tt = (function () {
|
||||
r.th.style.top = (e.pageY + 12 * sy) + 'px';
|
||||
};
|
||||
|
||||
if (IPHONE) {
|
||||
if (TOUCH) {
|
||||
var f1 = r.show,
|
||||
f2 = r.hide,
|
||||
q = [];
|
||||
|
||||
// if an onclick-handler creates a new timer,
|
||||
// iOS 13.1.2 delays the entire handler by up to 401ms,
|
||||
// webkits delay the entire handler by up to 401ms,
|
||||
// win by using a shared timer instead
|
||||
|
||||
timer.add(function () {
|
||||
|
||||
@@ -1,3 +1,43 @@
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2023-0312-1610 `v1.6.8` folder thumbs
|
||||
|
||||
* read-only demo server at https://a.ocv.me/pub/demo/
|
||||
* [docker image](https://github.com/9001/copyparty/tree/hovudstraum/scripts/docker) ╱ [similar software](https://github.com/9001/copyparty/blob/hovudstraum/docs/versus.md) ╱ [client testbed](https://cd.ocv.me/b/)
|
||||
|
||||
## new features
|
||||
* folder thumbnails are indexed in the db
|
||||
* now supports non-lowercase names (`Cover.jpg`, `Folder.JPG`)
|
||||
* folders without a specific cover/folder image will show the first pic inside
|
||||
* when audio playback continues into an empty folder, keep trying for a bit
|
||||
* add no-index hints (google etc) in basic-browser HTML (`?b`, `?b=u`)
|
||||
* [commandline uploader](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py) supports long filenames on win7
|
||||
|
||||
## bugfixes
|
||||
* rotated logfiles didn't get xz compressed
|
||||
* image-gallery links pointing to a deleted image shows an error instead of a crashpage
|
||||
|
||||
## other changes
|
||||
* folder thumbnails have purple text to differentiate from files
|
||||
* `copyparty32.exe` starts 30% faster (but is 6% larger)
|
||||
|
||||
----
|
||||
|
||||
# what to download?
|
||||
| download link | is it good? | description |
|
||||
| -- | -- | -- |
|
||||
| **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** | ✅ the best 👍 | runs anywhere! only needs python |
|
||||
| [a docker image](https://github.com/9001/copyparty/blob/hovudstraum/scripts/docker/README.md) | it's ok | good if you prefer docker 🐋 |
|
||||
| [copyparty.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty.exe) | ⚠️ [acceptable](https://github.com/9001/copyparty#copypartyexe) | for [win8](https://user-images.githubusercontent.com/241032/221445946-1e328e56-8c5b-44a9-8b9f-dee84d942535.png) or later; built-in thumbnailer |
|
||||
| [up2k.exe](https://github.com/9001/copyparty/releases/latest/download/up2k.exe) | ⚠️ acceptable | [CLI uploader](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py) as a win7+ exe ([video](https://a.ocv.me/pub/demo/pics-vids/u2cli.webm)) |
|
||||
| [copyparty32.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty32.exe) | ⛔️ [dangerous](https://github.com/9001/copyparty#copypartyexe) | for [win7](https://user-images.githubusercontent.com/241032/221445944-ae85d1f4-d351-4837-b130-82cab57d6cca.png) -- never expose to the internet! |
|
||||
| [cpp-winpe64.exe](https://github.com/9001/copyparty/releases/download/v1.6.8/copyparty-winpe64.exe) | ⛔️ dangerous | runs on [64bit WinPE](https://user-images.githubusercontent.com/241032/205454984-e6b550df-3c49-486d-9267-1614078dd0dd.png), otherwise useless |
|
||||
|
||||
* except for [up2k.exe](https://github.com/9001/copyparty/releases/latest/download/up2k.exe), all of the options above are equivalent
|
||||
* the zip and tar.gz files below are just source code
|
||||
* python packages are available at [PyPI](https://pypi.org/project/copyparty/#files)
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2023-0305-2018 `v1.6.7` fix no-dedup + add up2k.exe
|
||||
|
||||
|
||||
@@ -9,11 +9,14 @@ tee build2.sh | cmp build.sh && rm build2.sh || {
|
||||
[[ $r =~ [yY] ]] && mv build{2,}.sh && exec ./build.sh
|
||||
}
|
||||
|
||||
./up2k.sh
|
||||
[ -e up2k.sh ] && ./up2k.sh
|
||||
|
||||
uname -s | grep WOW64 && m= || m=32
|
||||
uname -s | grep WOW64 && m=64 || m=32
|
||||
uname -s | grep NT-10 && w10=1 || w7=1
|
||||
[ $w7 ] && pyv=37 || pyv=311
|
||||
esuf=
|
||||
[ $w7 ] && [ $m = 32 ] && esuf=32
|
||||
[ $w7 ] && [ $m = 64 ] && esuf=-winpe64
|
||||
|
||||
appd=$(cygpath.exe "$APPDATA")
|
||||
spkgs=$appd/Python/Python$pyv/site-packages
|
||||
@@ -59,8 +62,7 @@ read a b c d _ < <(
|
||||
sed -r 's/[^0-9]+//;s/[" )]//g;s/[-,]/ /g;s/$/ 0/'
|
||||
)
|
||||
sed -r 's/1,2,3,0/'$a,$b,$c,$d'/;s/1\.2\.3/'$a.$b.$c/ <loader.rc >loader.rc2
|
||||
[ $m ] &&
|
||||
sed -ri 's/copyparty.exe/copyparty32.exe/' loader.rc2
|
||||
sed -ri s/copyparty.exe/copyparty$esuf.exe/ loader.rc2
|
||||
|
||||
excl=(
|
||||
copyparty.broker_mp
|
||||
@@ -105,4 +107,4 @@ base64 | head -c12 >> dist/copyparty.exe
|
||||
|
||||
dist/copyparty.exe --version
|
||||
|
||||
curl -fkT dist/copyparty.exe -b cppwd=wark https://192.168.123.1:3923/copyparty$m.exe
|
||||
curl -fkT dist/copyparty.exe -b cppwd=wark https://192.168.123.1:3923/copyparty$esuf.exe
|
||||
|
||||
@@ -30,6 +30,9 @@ if possible, for performance and security reasons, please use this instead:
|
||||
https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py
|
||||
"""
|
||||
|
||||
if sys.maxsize > 2 ** 32:
|
||||
v = v.replace("32-bit", "64-bit")
|
||||
|
||||
try:
|
||||
print(v.replace("\n", "\n▒▌ ")[1:] + "\n")
|
||||
except:
|
||||
|
||||
@@ -98,7 +98,7 @@ class Cfg(Namespace):
|
||||
def __init__(self, a=None, v=None, c=None):
|
||||
ka = {}
|
||||
|
||||
ex = "daw dav_inf dav_mac dotsrch e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp ed emp force_js getmod hardlink ihead magic never_symlink nid nih no_acode no_athumb no_dav no_dedup no_del no_dupe no_logues no_mv no_readme no_robots no_sb_md no_sb_lg no_scandir no_thumb no_vthumb no_zip nrand nw rand vc xdev xlink xvol"
|
||||
ex = "daw dav_inf dav_mac dotsrch e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp ed emp force_js getmod hardlink ih ihead magic never_symlink nid nih no_acode no_athumb no_dav no_dedup no_del no_dupe no_logues no_mv no_readme no_robots no_sb_md no_sb_lg no_scandir no_thumb no_vthumb no_zip nrand nw rand vc xdev xlink xvol"
|
||||
ka.update(**{k: False for k in ex.split()})
|
||||
|
||||
ex = "dotpart no_rescan no_sendfile no_voldump plain_ip"
|
||||
|
||||
Reference in New Issue
Block a user