mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 17:12:13 -06:00
u2cli: server is allowed to reject dupes
This commit is contained in:
parent
29c212a60e
commit
3a800585bc
70
bin/up2k.py
70
bin/up2k.py
|
@ -3,7 +3,7 @@ from __future__ import print_function, unicode_literals
|
||||||
|
|
||||||
"""
|
"""
|
||||||
up2k.py: upload to copyparty
|
up2k.py: upload to copyparty
|
||||||
2022-11-04, v0.21, ed <irc.rizon.net>, MIT-Licensed
|
2022-11-29, v0.22, ed <irc.rizon.net>, MIT-Licensed
|
||||||
https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py
|
https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py
|
||||||
|
|
||||||
- dependencies: requests
|
- dependencies: requests
|
||||||
|
@ -94,6 +94,7 @@ class File(object):
|
||||||
self.kchunks = {} # type: dict[str, tuple[int, int]] # hash: [ ofs, sz ]
|
self.kchunks = {} # type: dict[str, tuple[int, int]] # hash: [ ofs, sz ]
|
||||||
|
|
||||||
# set by handshake
|
# set by handshake
|
||||||
|
self.recheck = False # duplicate; redo handshake after all files done
|
||||||
self.ucids = [] # type: list[str] # chunks which need to be uploaded
|
self.ucids = [] # type: list[str] # chunks which need to be uploaded
|
||||||
self.wark = None # type: str
|
self.wark = None # type: str
|
||||||
self.url = None # type: str
|
self.url = None # type: str
|
||||||
|
@ -472,8 +473,8 @@ def get_hashlist(file, pcb, mth):
|
||||||
file.kchunks[k] = [v1, v2]
|
file.kchunks[k] = [v1, v2]
|
||||||
|
|
||||||
|
|
||||||
def handshake(req_ses, url, file, pw, search):
|
def handshake(url, file, pw, search):
|
||||||
# type: (requests.Session, str, File, any, bool) -> list[str]
|
# type: (str, File, Any, bool) -> tuple[list[str], bool]
|
||||||
"""
|
"""
|
||||||
performs a handshake with the server; reply is:
|
performs a handshake with the server; reply is:
|
||||||
if search, a list of search results
|
if search, a list of search results
|
||||||
|
@ -507,6 +508,17 @@ def handshake(req_ses, url, file, pw, search):
|
||||||
eprint("handshake failed, retrying: {0}\n {1}\n\n".format(file.name, em))
|
eprint("handshake failed, retrying: {0}\n {1}\n\n".format(file.name, em))
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
sc = r.status_code
|
||||||
|
if sc >= 400:
|
||||||
|
txt = r.text
|
||||||
|
if sc == 422 or "<pre>partial upload exists at a different" in txt:
|
||||||
|
file.recheck = True
|
||||||
|
return [], False
|
||||||
|
elif sc == 409 or "<pre>upload rejected, file already exists" in txt:
|
||||||
|
return [], False
|
||||||
|
|
||||||
|
raise Exception("http {0}: {1}".format(sc, txt))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = r.json()
|
r = r.json()
|
||||||
except:
|
except:
|
||||||
|
@ -528,8 +540,8 @@ def handshake(req_ses, url, file, pw, search):
|
||||||
return r["hash"], r["sprs"]
|
return r["hash"], r["sprs"]
|
||||||
|
|
||||||
|
|
||||||
def upload(req_ses, file, cid, pw):
|
def upload(file, cid, pw):
|
||||||
# type: (requests.Session, File, str, any) -> None
|
# type: (File, str, Any) -> None
|
||||||
"""upload one specific chunk, `cid` (a chunk-hash)"""
|
"""upload one specific chunk, `cid` (a chunk-hash)"""
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
|
@ -626,8 +638,8 @@ class Ctl(object):
|
||||||
|
|
||||||
self.mutex = threading.Lock()
|
self.mutex = threading.Lock()
|
||||||
self.q_handshake = Queue() # type: Queue[File]
|
self.q_handshake = Queue() # type: Queue[File]
|
||||||
self.q_recheck = Queue() # type: Queue[File] # partial upload exists [...]
|
|
||||||
self.q_upload = Queue() # type: Queue[tuple[File, str]]
|
self.q_upload = Queue() # type: Queue[tuple[File, str]]
|
||||||
|
self.recheck = [] # type: list[File]
|
||||||
|
|
||||||
self.st_hash = [None, "(idle, starting...)"] # type: tuple[File, int]
|
self.st_hash = [None, "(idle, starting...)"] # type: tuple[File, int]
|
||||||
self.st_up = [None, "(idle, starting...)"] # type: tuple[File, int]
|
self.st_up = [None, "(idle, starting...)"] # type: tuple[File, int]
|
||||||
|
@ -649,7 +661,7 @@ class Ctl(object):
|
||||||
burl = self.ar.url[:12] + self.ar.url[8:].split("/")[0] + "/"
|
burl = self.ar.url[:12] + self.ar.url[8:].split("/")[0] + "/"
|
||||||
while True:
|
while True:
|
||||||
print(" hs...")
|
print(" hs...")
|
||||||
hs, _ = handshake(req_ses, self.ar.url, file, self.ar.a, search)
|
hs, _ = handshake(self.ar.url, file, self.ar.a, search)
|
||||||
if search:
|
if search:
|
||||||
if hs:
|
if hs:
|
||||||
for hit in hs:
|
for hit in hs:
|
||||||
|
@ -666,9 +678,18 @@ class Ctl(object):
|
||||||
ncs = len(hs)
|
ncs = len(hs)
|
||||||
for nc, cid in enumerate(hs):
|
for nc, cid in enumerate(hs):
|
||||||
print(" {0} up {1}".format(ncs - nc, cid))
|
print(" {0} up {1}".format(ncs - nc, cid))
|
||||||
upload(req_ses, file, cid, self.ar.a)
|
upload(file, cid, self.ar.a)
|
||||||
|
|
||||||
print(" ok!")
|
print(" ok!")
|
||||||
|
if file.recheck:
|
||||||
|
self.recheck.append(file)
|
||||||
|
|
||||||
|
if not self.recheck:
|
||||||
|
return
|
||||||
|
|
||||||
|
eprint("finalizing {0} duplicate files".format(len(self.recheck)))
|
||||||
|
for file in self.recheck:
|
||||||
|
handshake(self.ar.url, file, self.ar.a, search)
|
||||||
|
|
||||||
def _fancy(self):
|
def _fancy(self):
|
||||||
if VT100:
|
if VT100:
|
||||||
|
@ -740,6 +761,13 @@ class Ctl(object):
|
||||||
t = "{0} eta @ {1}/s, {2}, {3}# left".format(eta, spd, sleft, nleft)
|
t = "{0} eta @ {1}/s, {2}, {3}# left".format(eta, spd, sleft, nleft)
|
||||||
eprint(txt + "\033]0;{0}\033\\\r{0}{1}".format(t, tail))
|
eprint(txt + "\033]0;{0}\033\\\r{0}{1}".format(t, tail))
|
||||||
|
|
||||||
|
if not self.recheck:
|
||||||
|
return
|
||||||
|
|
||||||
|
eprint("finalizing {0} duplicate files".format(len(self.recheck)))
|
||||||
|
for file in self.recheck:
|
||||||
|
handshake(self.ar.url, file, self.ar.a, False)
|
||||||
|
|
||||||
def cleanup_vt100(self):
|
def cleanup_vt100(self):
|
||||||
ss.scroll_region(None)
|
ss.scroll_region(None)
|
||||||
eprint("\033[J\033]0;\033\\")
|
eprint("\033[J\033]0;\033\\")
|
||||||
|
@ -812,16 +840,10 @@ class Ctl(object):
|
||||||
|
|
||||||
def handshaker(self):
|
def handshaker(self):
|
||||||
search = self.ar.s
|
search = self.ar.s
|
||||||
q = self.q_handshake
|
|
||||||
burl = self.ar.url[:8] + self.ar.url[8:].split("/")[0] + "/"
|
burl = self.ar.url[:8] + self.ar.url[8:].split("/")[0] + "/"
|
||||||
while True:
|
while True:
|
||||||
file = q.get()
|
file = self.q_handshake.get()
|
||||||
if not file:
|
if not file:
|
||||||
if q == self.q_handshake:
|
|
||||||
q = self.q_recheck
|
|
||||||
q.put(None)
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.q_upload.put(None)
|
self.q_upload.put(None)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -829,16 +851,7 @@ class Ctl(object):
|
||||||
self.handshaker_busy += 1
|
self.handshaker_busy += 1
|
||||||
|
|
||||||
upath = file.abs.decode("utf-8", "replace")
|
upath = file.abs.decode("utf-8", "replace")
|
||||||
|
hs, sprs = handshake(self.ar.url, file, self.ar.a, search)
|
||||||
try:
|
|
||||||
hs, sprs = handshake(req_ses, self.ar.url, file, self.ar.a, search)
|
|
||||||
except Exception as ex:
|
|
||||||
if q == self.q_handshake and "<pre>partial upload exists" in str(ex):
|
|
||||||
self.q_recheck.put(file)
|
|
||||||
hs = []
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
if search:
|
if search:
|
||||||
if hs:
|
if hs:
|
||||||
for hit in hs:
|
for hit in hs:
|
||||||
|
@ -855,8 +868,11 @@ class Ctl(object):
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if file.recheck:
|
||||||
|
self.recheck.append(file)
|
||||||
|
|
||||||
with self.mutex:
|
with self.mutex:
|
||||||
if not sprs and not self.serialized:
|
if hs and not sprs and not self.serialized:
|
||||||
t = "server filesystem does not support sparse files; serializing uploads\n"
|
t = "server filesystem does not support sparse files; serializing uploads\n"
|
||||||
eprint(t)
|
eprint(t)
|
||||||
self.serialized = True
|
self.serialized = True
|
||||||
|
@ -899,7 +915,7 @@ class Ctl(object):
|
||||||
|
|
||||||
file, cid = task
|
file, cid = task
|
||||||
try:
|
try:
|
||||||
upload(req_ses, file, cid, self.ar.a)
|
upload(file, cid, self.ar.a)
|
||||||
except:
|
except:
|
||||||
eprint("upload failed, retrying: {0} #{1}\n".format(file.name, cid[:8]))
|
eprint("upload failed, retrying: {0} #{1}\n".format(file.name, cid[:8]))
|
||||||
pass # handshake will fix it
|
pass # handshake will fix it
|
||||||
|
|
|
@ -2005,13 +2005,13 @@ class Up2k(object):
|
||||||
except:
|
except:
|
||||||
self.dupesched[src] = [dupe]
|
self.dupesched[src] = [dupe]
|
||||||
|
|
||||||
raise Pebkac(400, err)
|
raise Pebkac(422, err)
|
||||||
|
|
||||||
elif "nodupe" in self.flags[job["ptop"]]:
|
elif "nodupe" in self.flags[job["ptop"]]:
|
||||||
self.log("dupe-reject:\n {0}\n {1}".format(src, dst))
|
self.log("dupe-reject:\n {0}\n {1}".format(src, dst))
|
||||||
err = "upload rejected, file already exists:\n"
|
err = "upload rejected, file already exists:\n"
|
||||||
err += "/" + quotep(vsrc) + " "
|
err += "/" + quotep(vsrc) + " "
|
||||||
raise Pebkac(400, err)
|
raise Pebkac(409, err)
|
||||||
else:
|
else:
|
||||||
# symlink to the client-provided name,
|
# symlink to the client-provided name,
|
||||||
# returning the previous upload info
|
# returning the previous upload info
|
||||||
|
|
|
@ -2297,8 +2297,8 @@ function up2k_init(subtle) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var err_pend = rsp.indexOf('partial upload exists') + 1,
|
var err_pend = rsp.indexOf('partial upload exists at a different') + 1,
|
||||||
err_dupe = rsp.indexOf('file already exists') + 1;
|
err_dupe = rsp.indexOf('upload rejected, file already exists') + 1;
|
||||||
|
|
||||||
if (err_pend || err_dupe) {
|
if (err_pend || err_dupe) {
|
||||||
err = rsp;
|
err = rsp;
|
||||||
|
|
Loading…
Reference in a new issue