copyparty 1.15.8__py3-none-any.whl → 1.15.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
copyparty/__main__.py CHANGED
@@ -1029,7 +1029,7 @@ def add_network(ap):
1029
1029
  else:
1030
1030
  ap2.add_argument("--freebind", action="store_true", help="allow listening on IPs which do not yet exist, for example if the network interfaces haven't finished going up. Only makes sense for IPs other than '0.0.0.0', '127.0.0.1', '::', and '::1'. May require running as root (unless net.ipv6.ip_nonlocal_bind)")
1031
1031
  ap2.add_argument("--s-thead", metavar="SEC", type=int, default=120, help="socket timeout (read request header)")
1032
- ap2.add_argument("--s-tbody", metavar="SEC", type=float, default=186.0, help="socket timeout (read/write request/response bodies). Use 60 on fast servers (default is extremely safe). Disable with 0 if reverse-proxied for a 2%% speed boost")
1032
+ ap2.add_argument("--s-tbody", metavar="SEC", type=float, default=128.0, help="socket timeout (read/write request/response bodies). Use 60 on fast servers (default is extremely safe). Disable with 0 if reverse-proxied for a 2%% speed boost")
1033
1033
  ap2.add_argument("--s-rd-sz", metavar="B", type=int, default=256*1024, help="socket read size in bytes (indirectly affects filesystem writes; recommendation: keep equal-to or lower-than \033[33m--iobuf\033[0m)")
1034
1034
  ap2.add_argument("--s-wr-sz", metavar="B", type=int, default=256*1024, help="socket write size in bytes")
1035
1035
  ap2.add_argument("--s-wr-slp", metavar="SEC", type=float, default=0.0, help="debug: socket write delay in seconds")
@@ -1349,6 +1349,14 @@ def add_transcoding(ap):
1349
1349
  ap2.add_argument("--ac-maxage", metavar="SEC", type=int, default=86400, help="delete cached transcode output after \033[33mSEC\033[0m seconds")
1350
1350
 
1351
1351
 
1352
+ def add_rss(ap):
1353
+ ap2 = ap.add_argument_group('RSS options')
1354
+ ap2.add_argument("--rss", action="store_true", help="enable RSS output (experimental)")
1355
+ ap2.add_argument("--rss-nf", metavar="HITS", type=int, default=250, help="default number of files to return (url-param 'nf')")
1356
+ ap2.add_argument("--rss-fext", metavar="E,E", type=u, default="", help="default list of file extensions to include (url-param 'fext'); blank=all")
1357
+ ap2.add_argument("--rss-sort", metavar="ORD", type=u, default="m", help="default sort order (url-param 'sort'); [\033[32mm\033[0m]=last-modified [\033[32mu\033[0m]=upload-time [\033[32mn\033[0m]=filename [\033[32ms\033[0m]=filesize; Uppercase=oldest-first. Note that upload-time is 0 for non-uploaded files")
1358
+
1359
+
1352
1360
  def add_db_general(ap, hcores):
1353
1361
  noidx = APPLESAN_TXT if MACOS else ""
1354
1362
  ap2 = ap.add_argument_group('general db options')
@@ -1518,6 +1526,7 @@ def run_argparse(
1518
1526
  add_db_metadata(ap)
1519
1527
  add_thumbnail(ap)
1520
1528
  add_transcoding(ap)
1529
+ add_rss(ap)
1521
1530
  add_ftp(ap)
1522
1531
  add_webdav(ap)
1523
1532
  add_tftp(ap)
copyparty/__version__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 15, 8)
3
+ VERSION = (1, 15, 9)
4
4
  CODENAME = "fill the drives"
5
- BUILD_DT = (2024, 10, 16)
5
+ BUILD_DT = (2024, 10, 18)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
copyparty/authsrv.py CHANGED
@@ -158,8 +158,11 @@ class Lim(object):
158
158
  self.chk_rem(rem)
159
159
  if sz != -1:
160
160
  self.chk_sz(sz)
161
- self.chk_vsz(broker, ptop, sz, volgetter)
162
- self.chk_df(abspath, sz) # side effects; keep last-ish
161
+ else:
162
+ sz = 0
163
+
164
+ self.chk_vsz(broker, ptop, sz, volgetter)
165
+ self.chk_df(abspath, sz) # side effects; keep last-ish
163
166
 
164
167
  ap2, vp2 = self.rot(abspath)
165
168
  if abspath == ap2:
@@ -199,7 +202,15 @@ class Lim(object):
199
202
 
200
203
  if self.dft < time.time():
201
204
  self.dft = int(time.time()) + 300
202
- self.dfv = get_df(abspath)[0] or 0
205
+
206
+ df, du, err = get_df(abspath, True)
207
+ if err:
208
+ t = "failed to read disk space usage for [%s]: %s"
209
+ self.log(t % (abspath, err), 3)
210
+ self.dfv = 0xAAAAAAAAA # 42.6 GiB
211
+ else:
212
+ self.dfv = df or 0
213
+
203
214
  for j in list(self.reg.values()) if self.reg else []:
204
215
  self.dfv -= int(j["size"] / (len(j["hash"]) or 999) * len(j["need"]))
205
216
 
copyparty/cfg.py CHANGED
@@ -46,6 +46,7 @@ def vf_bmap() :
46
46
  "og_no_head",
47
47
  "og_s_title",
48
48
  "rand",
49
+ "rss",
49
50
  "xdev",
50
51
  "xlink",
51
52
  "xvol",
copyparty/httpcli.py CHANGED
@@ -127,6 +127,8 @@ LOGUES = [[0, ".prologue.html"], [1, ".epilogue.html"]]
127
127
 
128
128
  READMES = [[0, ["preadme.md", "PREADME.md"]], [1, ["readme.md", "README.md"]]]
129
129
 
130
+ RSS_SORT = {"m": "mt", "u": "at", "n": "fn", "s": "sz"}
131
+
130
132
 
131
133
  class HttpCli(object):
132
134
  """
@@ -1196,8 +1198,146 @@ class HttpCli(object):
1196
1198
  if "h" in self.uparam:
1197
1199
  return self.tx_mounts()
1198
1200
 
1201
+ if "rss" in self.uparam:
1202
+ return self.tx_rss()
1203
+
1199
1204
  return self.tx_browser()
1200
1205
 
1206
+ def tx_rss(self) :
1207
+ if self.do_log:
1208
+ self.log("RSS %s @%s" % (self.req, self.uname))
1209
+
1210
+ if not self.can_read:
1211
+ return self.tx_404()
1212
+
1213
+ vn = self.vn
1214
+ if not vn.flags.get("rss"):
1215
+ raise Pebkac(405, "RSS is disabled in server config")
1216
+
1217
+ rem = self.rem
1218
+ idx = self.conn.get_u2idx()
1219
+ if not idx or not hasattr(idx, "p_end"):
1220
+ if not HAVE_SQLITE3:
1221
+ raise Pebkac(500, "sqlite3 not found on server; rss is disabled")
1222
+ raise Pebkac(500, "server busy, cannot generate rss; please retry in a bit")
1223
+
1224
+ uv = [rem]
1225
+ if "recursive" in self.uparam:
1226
+ uq = "up.rd like ?||'%'"
1227
+ else:
1228
+ uq = "up.rd == ?"
1229
+
1230
+ zs = str(self.uparam.get("fext", self.args.rss_fext))
1231
+ if zs in ("True", "False"):
1232
+ zs = ""
1233
+ if zs:
1234
+ zsl = []
1235
+ for ext in zs.split(","):
1236
+ zsl.append("+up.fn like '%.'||?")
1237
+ uv.append(ext)
1238
+ uq += " and ( %s )" % (" or ".join(zsl),)
1239
+
1240
+ zs1 = self.uparam.get("sort", self.args.rss_sort)
1241
+ zs2 = zs1.lower()
1242
+ zs = RSS_SORT.get(zs2)
1243
+ if not zs:
1244
+ raise Pebkac(400, "invalid sort key; must be m/u/n/s")
1245
+
1246
+ uq += " order by up." + zs
1247
+ if zs1 == zs2:
1248
+ uq += " desc"
1249
+
1250
+ nmax = int(self.uparam.get("nf") or self.args.rss_nf)
1251
+
1252
+ hits = idx.run_query(self.uname, [self.vn], uq, uv, False, False, nmax)[0]
1253
+
1254
+ pw = self.ouparam.get("pw")
1255
+ if pw:
1256
+ q_pw = "?pw=%s" % (pw,)
1257
+ a_pw = "&pw=%s" % (pw,)
1258
+ for i in hits:
1259
+ i["rp"] += a_pw if "?" in i["rp"] else q_pw
1260
+ else:
1261
+ q_pw = a_pw = ""
1262
+
1263
+ title = self.uparam.get("title") or self.vpath.split("/")[-1]
1264
+ etitle = html_escape(title, True, True)
1265
+
1266
+ baseurl = "%s://%s%s" % (
1267
+ "https" if self.is_https else "http",
1268
+ self.host,
1269
+ self.args.SRS,
1270
+ )
1271
+ feed = "%s%s" % (baseurl, self.req[1:])
1272
+ efeed = html_escape(feed, True, True)
1273
+ edirlink = efeed.split("?")[0] + q_pw
1274
+
1275
+ ret = [
1276
+ """\
1277
+ <?xml version="1.0" encoding="UTF-8"?>
1278
+ <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:content="http://purl.org/rss/1.0/modules/content/">
1279
+ \t<channel>
1280
+ \t\t<atom:link href="%s" rel="self" type="application/rss+xml" />
1281
+ \t\t<title>%s</title>
1282
+ \t\t<description></description>
1283
+ \t\t<link>%s</link>
1284
+ \t\t<generator>copyparty-1</generator>
1285
+ """
1286
+ % (efeed, etitle, edirlink)
1287
+ ]
1288
+
1289
+ q = "select fn from cv where rd=? and dn=?"
1290
+ crd, cdn = rem.rsplit("/", 1) if "/" in rem else ("", rem)
1291
+ try:
1292
+ cfn = idx.cur[self.vn.realpath].execute(q, (crd, cdn)).fetchone()[0]
1293
+ bos.stat(os.path.join(vn.canonical(rem), cfn))
1294
+ cv_url = "%s%s?th=jf%s" % (baseurl, vjoin(self.vpath, cfn), a_pw)
1295
+ cv_url = html_escape(cv_url, True, True)
1296
+ zs = """\
1297
+ \t\t<image>
1298
+ \t\t\t<url>%s</url>
1299
+ \t\t\t<title>%s</title>
1300
+ \t\t\t<link>%s</link>
1301
+ \t\t</image>
1302
+ """
1303
+ ret.append(zs % (cv_url, etitle, edirlink))
1304
+ except:
1305
+ pass
1306
+
1307
+ for i in hits:
1308
+ iurl = html_escape("%s%s" % (baseurl, i["rp"]), True, True)
1309
+ title = unquotep(i["rp"].split("?")[0].split("/")[-1])
1310
+ title = html_escape(title, True, True)
1311
+ tag_t = str(i["tags"].get("title") or "")
1312
+ tag_a = str(i["tags"].get("artist") or "")
1313
+ desc = "%s - %s" % (tag_a, tag_t) if tag_t and tag_a else (tag_t or tag_a)
1314
+ desc = html_escape(desc, True, True) if desc else title
1315
+ mime = html_escape(guess_mime(title))
1316
+ lmod = formatdate(i["ts"])
1317
+ zsa = (iurl, iurl, title, desc, lmod, iurl, mime, i["sz"])
1318
+ zs = (
1319
+ """\
1320
+ \t\t<item>
1321
+ \t\t\t<guid>%s</guid>
1322
+ \t\t\t<link>%s</link>
1323
+ \t\t\t<title>%s</title>
1324
+ \t\t\t<description>%s</description>
1325
+ \t\t\t<pubDate>%s</pubDate>
1326
+ \t\t\t<enclosure url="%s" type="%s" length="%d"/>
1327
+ """
1328
+ % zsa
1329
+ )
1330
+ dur = i["tags"].get(".dur")
1331
+ if dur:
1332
+ zs += "\t\t\t<itunes:duration>%d</itunes:duration>\n" % (dur,)
1333
+ ret.append(zs + "\t\t</item>\n")
1334
+
1335
+ ret.append("\t</channel>\n</rss>\n")
1336
+ bret = "".join(ret).encode("utf-8", "replace")
1337
+ self.reply(bret, 200, "text/xml; charset=utf-8")
1338
+ self.log("rss: %d hits, %d bytes" % (len(hits), len(bret)))
1339
+ return True
1340
+
1201
1341
  def handle_propfind(self) :
1202
1342
  if self.do_log:
1203
1343
  self.log("PFIND %s @%s" % (self.req, self.uname))
@@ -2481,6 +2621,7 @@ class HttpCli(object):
2481
2621
  except:
2482
2622
  # maybe busted handle (eg. disk went full)
2483
2623
  f.close()
2624
+ chashes = [] # exception flag
2484
2625
  raise
2485
2626
  finally:
2486
2627
  if locked:
@@ -2489,9 +2630,11 @@ class HttpCli(object):
2489
2630
  num_left, t = x.get()
2490
2631
  if num_left < 0:
2491
2632
  self.loud_reply(t, status=500)
2492
- return False
2493
- t = "got %d more chunks, %d left"
2494
- self.log(t % (len(written), num_left), 6)
2633
+ if chashes: # kills exception bubbling otherwise
2634
+ return False
2635
+ else:
2636
+ t = "got %d more chunks, %d left"
2637
+ self.log(t % (len(written), num_left), 6)
2495
2638
 
2496
2639
  if num_left < 0:
2497
2640
  raise Pebkac(500, "unconfirmed; see serverlog")
@@ -5082,7 +5225,7 @@ class HttpCli(object):
5082
5225
  self.log("#wow #whoa")
5083
5226
 
5084
5227
  if not self.args.nid:
5085
- free, total = get_df(abspath)
5228
+ free, total, _ = get_df(abspath, False)
5086
5229
  if total is not None:
5087
5230
  h1 = humansize(free or 0)
5088
5231
  h2 = humansize(total)
copyparty/metrics.py CHANGED
@@ -128,7 +128,7 @@ class Metrics(object):
128
128
  addbh("cpp_disk_size_bytes", "total HDD size of volume")
129
129
  addbh("cpp_disk_free_bytes", "free HDD space in volume")
130
130
  for vpath, vol in allvols:
131
- free, total = get_df(vol.realpath)
131
+ free, total, _ = get_df(vol.realpath, False)
132
132
  if free is None or total is None:
133
133
  continue
134
134
 
copyparty/u2idx.py CHANGED
@@ -91,7 +91,7 @@ class U2idx(object):
91
91
  uv = [wark[:16], wark]
92
92
 
93
93
  try:
94
- return self.run_query(uname, vols, uq, uv, False, 99999)[0]
94
+ return self.run_query(uname, vols, uq, uv, False, True, 99999)[0]
95
95
  except:
96
96
  raise Pebkac(500, min_ex())
97
97
 
@@ -295,7 +295,7 @@ class U2idx(object):
295
295
  q += " lower({}) {} ? ) ".format(field, oper)
296
296
 
297
297
  try:
298
- return self.run_query(uname, vols, q, va, have_mt, lim)
298
+ return self.run_query(uname, vols, q, va, have_mt, True, lim)
299
299
  except Exception as ex:
300
300
  raise Pebkac(500, repr(ex))
301
301
 
@@ -306,6 +306,7 @@ class U2idx(object):
306
306
  uq ,
307
307
  uv ,
308
308
  have_mt ,
309
+ sort ,
309
310
  lim ,
310
311
  ) :
311
312
  if self.args.srch_dbg:
@@ -452,7 +453,8 @@ class U2idx(object):
452
453
  done_flag.append(True)
453
454
  self.active_id = ""
454
455
 
455
- ret.sort(key=itemgetter("rp"))
456
+ if sort:
457
+ ret.sort(key=itemgetter("rp"))
456
458
 
457
459
  return ret, list(taglist.keys()), lim < 0 and not clamped
458
460
 
copyparty/up2k.py CHANGED
@@ -3481,6 +3481,7 @@ class Up2k(object):
3481
3481
  for chash in written:
3482
3482
  job["need"].remove(chash)
3483
3483
  except Exception as ex:
3484
+ # dead tcp connections can get here by timeout (OK)
3484
3485
  return -2, "confirm_chunk, chash(%s) %r" % (chash, ex) # type: ignore
3485
3486
 
3486
3487
  ret = len(job["need"])
copyparty/util.py CHANGED
@@ -192,6 +192,9 @@ except:
192
192
  ansi_re = re.compile("\033\\[[^mK]*[mK]")
193
193
 
194
194
 
195
+ BOS_SEP = ("%s" % (os.sep,)).encode("ascii")
196
+
197
+
195
198
  surrogateescape.register_surrogateescape()
196
199
  if WINDOWS and PY2:
197
200
  FS_ENCODING = "utf-8"
@@ -2407,22 +2410,27 @@ def wunlink(log , abspath , flags ) :
2407
2410
  return _fs_mvrm(log, abspath, "", False, flags)
2408
2411
 
2409
2412
 
2410
- def get_df(abspath ) :
2413
+ def get_df(abspath , prune ) :
2411
2414
  try:
2412
- # some fuses misbehave
2415
+ ap = fsenc(abspath)
2416
+ while prune and not os.path.isdir(ap) and BOS_SEP in ap:
2417
+ # strip leafs until it hits an existing folder
2418
+ ap = ap.rsplit(BOS_SEP, 1)[0]
2419
+
2413
2420
  if ANYWIN:
2421
+ abspath = fsdec(ap)
2414
2422
  bfree = ctypes.c_ulonglong(0)
2415
2423
  ctypes.windll.kernel32.GetDiskFreeSpaceExW( # type: ignore
2416
2424
  ctypes.c_wchar_p(abspath), None, None, ctypes.pointer(bfree)
2417
2425
  )
2418
- return (bfree.value, None)
2426
+ return (bfree.value, None, "")
2419
2427
  else:
2420
- sv = os.statvfs(fsenc(abspath))
2428
+ sv = os.statvfs(ap)
2421
2429
  free = sv.f_frsize * sv.f_bfree
2422
2430
  total = sv.f_frsize * sv.f_blocks
2423
- return (free, total)
2424
- except:
2425
- return (None, None)
2431
+ return (free, total, "")
2432
+ except Exception as ex:
2433
+ return (None, None, repr(ex))
2426
2434
 
2427
2435
 
2428
2436
  if not ANYWIN and not MACOS:
copyparty/web/a/u2c.py CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env python3
2
2
  from __future__ import print_function, unicode_literals
3
3
 
4
- S_VERSION = "2.4"
5
- S_BUILD_DT = "2024-10-16"
4
+ S_VERSION = "2.5"
5
+ S_BUILD_DT = "2024-10-18"
6
6
 
7
7
  """
8
8
  u2c.py: upload to copyparty
@@ -154,6 +154,7 @@ class HCli(object):
154
154
  self.tls = tls
155
155
  self.verify = ar.te or not ar.td
156
156
  self.conns = []
157
+ self.hconns = []
157
158
  if tls:
158
159
  import ssl
159
160
 
@@ -173,7 +174,7 @@ class HCli(object):
173
174
  "User-Agent": "u2c/%s" % (S_VERSION,),
174
175
  }
175
176
 
176
- def _connect(self):
177
+ def _connect(self, timeout):
177
178
  args = {}
178
179
  if PY37:
179
180
  args["blocksize"] = 1048576
@@ -185,7 +186,7 @@ class HCli(object):
185
186
  if self.ctx:
186
187
  args = {"context": self.ctx}
187
188
 
188
- return C(self.addr, self.port, timeout=999, **args)
189
+ return C(self.addr, self.port, timeout=timeout, **args)
189
190
 
190
191
  def req(self, meth, vpath, hdrs, body=None, ctype=None):
191
192
  hdrs.update(self.base_hdrs)
@@ -198,7 +199,9 @@ class HCli(object):
198
199
  0 if not body else body.len if hasattr(body, "len") else len(body)
199
200
  )
200
201
 
201
- c = self.conns.pop() if self.conns else self._connect()
202
+ # large timeout for handshakes (safededup)
203
+ conns = self.hconns if ctype == MJ else self.conns
204
+ c = conns.pop() if conns else self._connect(999 if ctype == MJ else 128)
202
205
  try:
203
206
  c.request(meth, vpath, body, hdrs)
204
207
  if PY27:
@@ -207,7 +210,7 @@ class HCli(object):
207
210
  rsp = c.getresponse()
208
211
 
209
212
  data = rsp.read()
210
- self.conns.append(c)
213
+ conns.append(c)
211
214
  return rsp.status, data.decode("utf-8")
212
215
  except:
213
216
  c.close()
@@ -868,9 +871,10 @@ def upload(fsl, stats, maxsz):
868
871
  if sc >= 400:
869
872
  raise Exception("http %s: %s" % (sc, txt))
870
873
  finally:
871
- fsl.f.close()
872
- if nsub != -1:
873
- fsl.unsub()
874
+ if fsl.f:
875
+ fsl.f.close()
876
+ if nsub != -1:
877
+ fsl.unsub()
874
878
 
875
879
 
876
880
  class Ctl(object):
copyparty/web/up2k.js.gz CHANGED
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.15.8
3
+ Version: 1.15.9
4
4
  Summary: Portable file server with accelerated resumable uploads, deduplication, WebDAV, FTP, zeroconf, media indexer, video thumbnails, audio transcoding, and write-only folders
5
5
  Author-email: ed <copyparty@ocv.me>
6
6
  License: MIT
@@ -101,6 +101,7 @@ turn almost any device into a file server with resumable uploads/downloads using
101
101
  * [file manager](#file-manager) - cut/paste, rename, and delete files/folders (if you have permission)
102
102
  * [shares](#shares) - share a file or folder by creating a temporary link
103
103
  * [batch rename](#batch-rename) - select some files and press `F2` to bring up the rename UI
104
+ * [rss feeds](#rss-feeds) - monitor a folder with your RSS reader
104
105
  * [media player](#media-player) - plays almost every audio format there is
105
106
  * [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
106
107
  * [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
@@ -899,6 +900,30 @@ or a mix of both:
899
900
  the metadata keys you can use in the format field are the ones in the file-browser table header (whatever is collected with `-mte` and `-mtp`)
900
901
 
901
902
 
903
+ ## rss feeds
904
+
905
+ monitor a folder with your RSS reader , optionally recursive
906
+
907
+ must be enabled per-volume with volflag `rss` or globally with `--rss`
908
+
909
+ the feed includes itunes metadata for use with podcast readers such as [AntennaPod](https://antennapod.org/)
910
+
911
+ a feed example: https://cd.ocv.me/a/d2/d22/?rss&fext=mp3
912
+
913
+ url parameters:
914
+
915
+ * `pw=hunter2` for password auth
916
+ * `recursive` to also include subfolders
917
+ * `title=foo` changes the feed title (default: folder name)
918
+ * `fext=mp3,opus` only include mp3 and opus files (default: all)
919
+ * `nf=30` only show the first 30 results (default: 250)
920
+ * `sort=m` sort by mtime (file last-modified), newest first (default)
921
+ * `u` = upload-time; NOTE: non-uploaded files have upload-time `0`
922
+ * `n` = filename
923
+ * `a` = filesize
924
+ * uppercase = reverse-sort; `M` = oldest file first
925
+
926
+
902
927
  ## media player
903
928
 
904
929
  plays almost every audio format there is (if the server has FFmpeg installed for on-demand transcoding)
@@ -1,22 +1,22 @@
1
1
  copyparty/__init__.py,sha256=Chqw7uXX4r_-a2p6-xthrrqVHFI4aZdW45sWU7UvqeE,2597
2
- copyparty/__main__.py,sha256=oTzWyuZotTwi-dHpD41mVp_EW2mPSWDMKP8o_nxp9Yk,110180
3
- copyparty/__version__.py,sha256=V6jLFRwMXDp4buHI89fYcQoINvqpP0XYLPVMn_QHEDg,258
4
- copyparty/authsrv.py,sha256=-lImrFH6pm3gcI76vZiFfEgrQx3_STYTSS2soYX5y1Y,98711
2
+ copyparty/__main__.py,sha256=rx8OlvcX3M-SQOvkrYT9C-HN7GmlPUx5p_2Vqv_LEo4,110949
3
+ copyparty/__version__.py,sha256=PMcIJPKN9PvxPb2mIFEiFAsp9HLavA8qaM4ckTc7cNE,258
4
+ copyparty/authsrv.py,sha256=Iw_4lJUhRU9q3qCQucGWRtJOFKYrYd5omL5nmwGvw-k,98979
5
5
  copyparty/broker_mp.py,sha256=jsHUM2BSfRVRyZT869iPCqYEHSqedk6VkwvygZwbEZE,4017
6
6
  copyparty/broker_mpw.py,sha256=PYFgQfssOCfdI6qayW1ZjO1j1-7oez094muhYMbPOz0,3339
7
7
  copyparty/broker_thr.py,sha256=MXrwjusP0z1LPURUhi5jx_TL3jrXhYcDrJPDSKu6EEU,1705
8
8
  copyparty/broker_util.py,sha256=76mfnFOpX1gUUvtjm8UQI7jpTIaVINX10QonM-B7ggc,1680
9
9
  copyparty/cert.py,sha256=0ZAPeXeMR164vWn9GQU3JDKooYXEq_NOQkDeg543ivg,8009
10
- copyparty/cfg.py,sha256=33nLatBUmzRFKQ4KpoQei3ZY6EqRrlaHpQnvCNFXcHI,10112
10
+ copyparty/cfg.py,sha256=E9iBGNjIUrDAPLFRgKsVOmAknP9bDE27xh0gkmNdH1s,10127
11
11
  copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
12
12
  copyparty/fsutil.py,sha256=5CshJWO7CflfaRRNOb3JxghUH7W5rmS_HWNmKfx42MM,4538
13
13
  copyparty/ftpd.py,sha256=G_h1urfIikzfCWGXnW9p-rioWdNM_Je6vWYq0-QSbC8,17580
14
- copyparty/httpcli.py,sha256=xhM8unCDyo7Gj3hmLdhncXUCgKHgb6a300bolO1WF4U,194152
14
+ copyparty/httpcli.py,sha256=irIxsAI0KpNvxoenK16zkezttl4sUSNUB59yBr8L6VA,199053
15
15
  copyparty/httpconn.py,sha256=mQSgljh0Q-jyWjF4tQLrHbRKRe9WKl19kGqsGMsJpWo,6880
16
16
  copyparty/httpsrv.py,sha256=d_UiGnQKniBoEV68lNFgnYm-byda7uj56mFf-YC7piI,17223
17
17
  copyparty/ico.py,sha256=eWSxEae4wOCfheHl-m-wchYvFRAR_97kJDb4NGaB-Z8,3561
18
18
  copyparty/mdns.py,sha256=vC078llnL1v0pvL3mnwacuStFHPJUQuxo9Opj-IbHL4,18155
19
- copyparty/metrics.py,sha256=aV09nntEmKMIyde8xoPtj1ehDOQVQOHchRF4uMMNzqM,8855
19
+ copyparty/metrics.py,sha256=-1Rkk44gBh_1YJbdzGZHaqR4pEwkbno6fSdsRb5wDIk,8865
20
20
  copyparty/mtag.py,sha256=8WGjEn0T0Ri9ww1yBpLUnFHZiTQMye1BMXL6SkK3MRo,18893
21
21
  copyparty/multicast.py,sha256=Ha27l2oATEa-Qo2WOzkeRgjAm6G_YDCfbVJWR-ao2UE,12319
22
22
  copyparty/pwhash.py,sha256=AdLMLyIi2IDhGtbKIQOswKUxWvO7ARYYRF_ThsryOoc,4124
@@ -30,9 +30,9 @@ copyparty/tcpsrv.py,sha256=l_vb9FoF0AJur0IoqHNUSBDqMgBO_MRUZeDszi1UNfY,19881
30
30
  copyparty/tftpd.py,sha256=jZbf2JpeJmkuQWJErmAPG-dKhtYNvIUHbkAgodSXw9Y,13582
31
31
  copyparty/th_cli.py,sha256=o6FMkerYvAXS455z3DUossVztu_nzFlYSQhs6qN6Jt8,4636
32
32
  copyparty/th_srv.py,sha256=hI9wY1E_9N9Cgqvtr8zADeVqqiLGTiTdAnYAA7WFvJw,29346
33
- copyparty/u2idx.py,sha256=JjgqwgJBNj6sTn4PJfuqM3VEHqlmoyGC5bk4_92K2h0,13414
34
- copyparty/up2k.py,sha256=Zn33f8EkFpAnK_RTDyVNA97CKlS9MS3k5mnnvYh1w1k,165351
35
- copyparty/util.py,sha256=7_-17F94TsLw644xLT8FfO_fH0DwViD54-grej3f8sY,92379
33
+ copyparty/u2idx.py,sha256=HLO49L1zmpJtBcJiXgD12a6pAlQdnf2pFelHMA7habw,13462
34
+ copyparty/up2k.py,sha256=2K21-Scz4c3_Y9MzAPmvq3_LEOIkFnx7KLR6hqJkP18,165419
35
+ copyparty/util.py,sha256=eJU7a5O-bZf_MEplCVmLoS3r5mjPC3-2khqZVTYGw-c,92653
36
36
  copyparty/bos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
37
  copyparty/bos/bos.py,sha256=Wb7eWsXJgR5AFlBR9ZOyKrLTwy-Kct9RrGiOu4Jo37Y,1622
38
38
  copyparty/bos/path.py,sha256=yEjCq2ki9CvxA5sCT8pS0keEXwugs0ZeUyUhdBziOCI,777
@@ -80,12 +80,12 @@ copyparty/web/splash.js.gz,sha256=Xoccku-2vE3tABo-88q3Cl4koHs_AE76T8QvMy4u6T8,25
80
80
  copyparty/web/svcs.html,sha256=P5YZimYLeQMT0uz6u3clQSNZRc5Zs0Ok-ffcbcGSYuc,11762
81
81
  copyparty/web/svcs.js.gz,sha256=k81ZvZ3I-f4fMHKrNGGOgOlvXnCBz0mVjD-8mieoWCA,520
82
82
  copyparty/web/ui.css.gz,sha256=wloSacrHgP722hy4XiOvVY2GI9-V4zvfvzu84LLWS_o,2779
83
- copyparty/web/up2k.js.gz,sha256=iMaZ2joij8ndkA2iFCTri6MnYRxXzQbRu9uwAnxZBj8,23096
83
+ copyparty/web/up2k.js.gz,sha256=lGR1Xb0RkIZ1eHmncsSwWRuFc6FC2rZalvjo3oNTV1s,23291
84
84
  copyparty/web/util.js.gz,sha256=NvjPYhIa0-C_NhUyW-Ra-XinUCRjj8G3pYq1zJHYWEk,14805
85
- copyparty/web/w.hash.js.gz,sha256=5weFAxkcfHhu90tUKnVnTu04U_PQjhzUVRfvir199I0,1093
85
+ copyparty/web/w.hash.js.gz,sha256=l3GpSJD6mcU-1CRWkIj7PybgbjlfSr8oeO3vortIrQk,1105
86
86
  copyparty/web/a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
87
  copyparty/web/a/partyfuse.py,sha256=fa9bBYNJHvtWpNVjQyyFzx6JOK7MJL1u0zj80PBYQKs,27960
88
- copyparty/web/a/u2c.py,sha256=QA0t_k422uLRh8Kt2eQIpcGYaaUZA1CGz_S-z23Yk-4,49343
88
+ copyparty/web/a/u2c.py,sha256=ZmLcGuOWB66ZkAMpJBiR1Xpa75PgpzdFRTt3GGVCorc,49533
89
89
  copyparty/web/a/webdav-cfg.bat,sha256=Y4NoGZlksAIg4cBMb7KdJrpKC6Nx97onaTl6yMjaimk,1449
90
90
  copyparty/web/dd/2.png,sha256=gJ14XFPzaw95L6z92fSq9eMPikSQyu-03P1lgiGe0_I,258
91
91
  copyparty/web/dd/3.png,sha256=4lho8Koz5tV7jJ4ODo6GMTScZfkqsT05yp48EDFIlyg,252
@@ -106,9 +106,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
106
106
  copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
107
107
  copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
108
108
  copyparty/web/deps/sha512.hw.js.gz,sha256=vqoXeracj-99Z5MfY3jK2N4WiSzYQdfjy0RnUlQDhSU,8110
109
- copyparty-1.15.8.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
110
- copyparty-1.15.8.dist-info/METADATA,sha256=R5tW0crtzFZ4vKzGny4gQ6-IOERgBgj7LZtP14Huung,138915
111
- copyparty-1.15.8.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
112
- copyparty-1.15.8.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
113
- copyparty-1.15.8.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
114
- copyparty-1.15.8.dist-info/RECORD,,
109
+ copyparty-1.15.9.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
110
+ copyparty-1.15.9.dist-info/METADATA,sha256=1GMM2x8ZO0z3TgiQgCB6xEiiahXsn3PdeiA7Uni_D_4,139807
111
+ copyparty-1.15.9.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
112
+ copyparty-1.15.9.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
113
+ copyparty-1.15.9.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
114
+ copyparty-1.15.9.dist-info/RECORD,,