Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
deca082623 | ||
|
|
0ea8bb7c83 | ||
|
|
1fb251a4c2 | ||
|
|
4295923b76 | ||
|
|
572aa4b26c | ||
|
|
b1359f039f | ||
|
|
867d8ee49e | ||
|
|
04c86e8a89 | ||
|
|
bc0cb43ef9 | ||
|
|
769454fdce | ||
|
|
4ee81af8f6 | ||
|
|
8b0e66122f | ||
|
|
8a98efb929 | ||
|
|
b6fd555038 | ||
|
|
7eb413ad51 | ||
|
|
4421d509eb | ||
|
|
793ffd7b01 |
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@@ -1,2 +1,2 @@
|
||||
Please include the following text somewhere in this PR description:
|
||||
To show that your contribution is compatible with the MIT License, please include the following text somewhere in this PR description:
|
||||
This PR complies with the DCO; https://developercertificate.org/
|
||||
|
||||
32
.vscode/settings.json
vendored
32
.vscode/settings.json
vendored
@@ -35,34 +35,18 @@
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.linting.banditEnabled": true,
|
||||
"python.linting.mypyEnabled": true,
|
||||
"python.linting.mypyArgs": [
|
||||
"--ignore-missing-imports",
|
||||
"--follow-imports=silent",
|
||||
"--show-column-numbers",
|
||||
"--strict"
|
||||
],
|
||||
"python.linting.flake8Args": [
|
||||
"--max-line-length=120",
|
||||
"--ignore=E722,F405,E203,W503,W293,E402,E501,E128",
|
||||
"--ignore=E722,F405,E203,W503,W293,E402,E501,E128,E226",
|
||||
],
|
||||
"python.linting.banditArgs": [
|
||||
"--ignore=B104"
|
||||
],
|
||||
"python.linting.pylintArgs": [
|
||||
"--disable=missing-module-docstring",
|
||||
"--disable=missing-class-docstring",
|
||||
"--disable=missing-function-docstring",
|
||||
"--disable=import-outside-toplevel",
|
||||
"--disable=wrong-import-position",
|
||||
"--disable=raise-missing-from",
|
||||
"--disable=bare-except",
|
||||
"--disable=broad-except",
|
||||
"--disable=invalid-name",
|
||||
"--disable=line-too-long",
|
||||
"--disable=consider-using-f-string"
|
||||
"--ignore=B104,B110,B112"
|
||||
],
|
||||
// python3 -m isort --py=27 --profile=black copyparty/
|
||||
"python.formatting.provider": "black",
|
||||
"python.formatting.provider": "none",
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "ms-python.black-formatter"
|
||||
},
|
||||
"editor.formatOnSave": true,
|
||||
"[html]": {
|
||||
"editor.formatOnSave": false,
|
||||
@@ -74,10 +58,6 @@
|
||||
"files.associations": {
|
||||
"*.makefile": "makefile"
|
||||
},
|
||||
"python.formatting.blackArgs": [
|
||||
"-t",
|
||||
"py27"
|
||||
],
|
||||
"python.linting.enabled": true,
|
||||
"python.pythonPath": "/usr/bin/python3"
|
||||
}
|
||||
20
README.md
20
README.md
@@ -104,9 +104,9 @@ turn almost any device into a file server with resumable uploads/downloads using
|
||||
|
||||
just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** -- that's it! 🎉
|
||||
|
||||
* or install through pypi (python3 only): `python3 -m pip install --user -U copyparty`
|
||||
* or install through pypi: `python3 -m pip install --user -U copyparty`
|
||||
* or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
|
||||
* or [install through nix](#nix-package), or [on NixOS](#nixos-module), or [on arch](#arch-package)
|
||||
* or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
|
||||
* 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:
|
||||
@@ -305,7 +305,7 @@ upgrade notes
|
||||
* http-api: delete/move is now `POST` instead of `GET`
|
||||
* everything other than `GET` and `HEAD` must pass [cors validation](#cors)
|
||||
* `1.5.0` (2022-12-03): [new chunksize formula](https://github.com/9001/copyparty/commit/54e1c8d261df) for files larger than 128 GiB
|
||||
* **users:** upgrade to the latest [cli uploader](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py) if you use that
|
||||
* **users:** upgrade to the latest [cli uploader](https://github.com/9001/copyparty/blob/hovudstraum/bin/u2c.py) if you use that
|
||||
* **devs:** update third-party up2k clients (if those even exist)
|
||||
|
||||
|
||||
@@ -512,7 +512,7 @@ you can also zip a selection of files or folders by clicking them in the browser
|
||||
|
||||
## uploading
|
||||
|
||||
drag files/folders into the web-browser to upload (or use the [command-line uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#up2kpy))
|
||||
drag files/folders into the web-browser to upload (or use the [command-line uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy))
|
||||
|
||||
this initiates an upload using `up2k`; there are two uploaders available:
|
||||
* `[🎈] bup`, the basic uploader, supports almost every browser since netscape 4.0
|
||||
@@ -1374,10 +1374,10 @@ interact with copyparty using non-browser clients
|
||||
* `(printf 'PUT /junk?pw=wark HTTP/1.1\r\n\r\n'; cat movie.mkv) | nc 127.0.0.1 3923`
|
||||
* `(printf 'PUT / HTTP/1.1\r\n\r\n'; cat movie.mkv) >/dev/tcp/127.0.0.1/3923`
|
||||
|
||||
* python: [up2k.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py) is a command-line up2k client [(webm)](https://ocv.me/stuff/u2cli.webm)
|
||||
* python: [u2c.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/u2c.py) is a command-line up2k client [(webm)](https://ocv.me/stuff/u2cli.webm)
|
||||
* file uploads, file-search, [folder sync](#folder-sync), autoresume of aborted/broken uploads
|
||||
* can be downloaded from copyparty: controlpanel -> connect -> [up2k.py](http://127.0.0.1:3923/.cpr/a/up2k.py)
|
||||
* see [./bin/README.md#up2kpy](bin/README.md#up2kpy)
|
||||
* can be downloaded from copyparty: controlpanel -> connect -> [u2c.py](http://127.0.0.1:3923/.cpr/a/u2c.py)
|
||||
* see [./bin/README.md#u2cpy](bin/README.md#u2cpy)
|
||||
|
||||
* FUSE: mount a copyparty server as a local filesystem
|
||||
* cross-platform python client available in [./bin/](bin/)
|
||||
@@ -1400,11 +1400,11 @@ NOTE: curl will not send the original filename if you use `-T` combined with url
|
||||
|
||||
sync folders to/from copyparty
|
||||
|
||||
the commandline uploader [up2k.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#up2kpy) with `--dr` is the best way to sync a folder to copyparty; verifies checksums and does files in parallel, and deletes unexpected files on the server after upload has finished which makes file-renames really cheap (it'll rename serverside and skip uploading)
|
||||
the commandline uploader [u2c.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy) with `--dr` is the best way to sync a folder to copyparty; verifies checksums and does files in parallel, and deletes unexpected files on the server after upload has finished which makes file-renames really cheap (it'll rename serverside and skip uploading)
|
||||
|
||||
alternatively there is [rclone](./docs/rclone.md) which allows for bidirectional sync and is *way* more flexible (stream files straight from sftp/s3/gcs to copyparty, ...), although there is no integrity check and it won't work with files over 100 MiB if copyparty is behind cloudflare
|
||||
|
||||
* starting from rclone v1.63 (currently [in beta](https://beta.rclone.org/?filter=latest)), rclone will also be faster than up2k.py
|
||||
* starting from rclone v1.63 (currently [in beta](https://beta.rclone.org/?filter=latest)), rclone will also be faster than u2c.py
|
||||
|
||||
|
||||
## mount as drive
|
||||
@@ -1471,7 +1471,7 @@ when uploading files,
|
||||
* chrome is recommended, at least compared to firefox:
|
||||
* up to 90% faster when hashing, especially on SSDs
|
||||
* up to 40% faster when uploading over extremely fast internets
|
||||
* but [up2k.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py) can be 40% faster than chrome again
|
||||
* but [u2c.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/u2c.py) can be 40% faster than chrome again
|
||||
|
||||
* if you're cpu-bottlenecked, or the browser is maxing a cpu core:
|
||||
* up to 30% faster uploads if you hide the upload status list by switching away from the `[🚀]` up2k ui-tab (or closing it)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# [`up2k.py`](up2k.py)
|
||||
# [`u2c.py`](u2c.py)
|
||||
* command-line up2k client [(webm)](https://ocv.me/stuff/u2cli.webm)
|
||||
* file uploads, file-search, autoresume of aborted/broken uploads
|
||||
* sync local folder to server
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
S_VERSION = "1.8"
|
||||
S_BUILD_DT = "2023-04-27"
|
||||
S_VERSION = "1.9"
|
||||
S_BUILD_DT = "2023-05-07"
|
||||
|
||||
"""
|
||||
up2k.py: upload to copyparty
|
||||
u2c.py: upload to copyparty
|
||||
2021, ed <irc.rizon.net>, MIT-Licensed
|
||||
https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py
|
||||
https://github.com/9001/copyparty/blob/hovudstraum/bin/u2c.py
|
||||
|
||||
- dependencies: requests
|
||||
- supports python 2.6, 2.7, and 3.3 through 3.12
|
||||
@@ -1,14 +1,14 @@
|
||||
# Maintainer: icxes <dev.null@need.moe>
|
||||
pkgname=copyparty
|
||||
pkgver="1.6.15"
|
||||
pkgver="1.7.0"
|
||||
pkgrel=1
|
||||
pkgdesc="Portable file sharing hub"
|
||||
arch=("any")
|
||||
url="https://github.com/9001/${pkgname}"
|
||||
license=('MIT')
|
||||
depends=("python" "lsof")
|
||||
optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tags"
|
||||
"python-jinja: faster html generator"
|
||||
depends=("python" "lsof" "python-jinja")
|
||||
makedepends=("python-wheel" "python-build" "python-installer" "make" "pigz")
|
||||
optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tags"
|
||||
"python-mutagen: music tags (alternative)"
|
||||
"python-pillow: thumbnails for images"
|
||||
"python-pyvips: thumbnails for images (higher quality, faster, uses more ram)"
|
||||
@@ -17,34 +17,31 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
|
||||
"python-pyopenssl: ftps functionality"
|
||||
"python-impacket-git: smb support (bad idea)"
|
||||
)
|
||||
source=("${url}/releases/download/v${pkgver}/${pkgname}-sfx.py"
|
||||
"${pkgname}.conf"
|
||||
"${pkgname}.service"
|
||||
"prisonparty.service"
|
||||
"index.md"
|
||||
"https://raw.githubusercontent.com/9001/${pkgname}/v${pkgver}/bin/prisonparty.sh"
|
||||
"https://raw.githubusercontent.com/9001/${pkgname}/v${pkgver}/LICENSE"
|
||||
)
|
||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
backup=("etc/${pkgname}.d/init" )
|
||||
sha256sums=("efe3fe8710ef8f3b50b1eb0f935f8620e378bdee48f592d77a1c5cf3ed7ce244"
|
||||
"b8565eba5e64dedba1cf6c7aac7e31c5a731ed7153d6810288a28f00a36c28b2"
|
||||
"f65c207e0670f9d78ad2e399bda18d5502ff30d2ac79e0e7fc48e7fbdc39afdc"
|
||||
"c4f396b083c9ec02ad50b52412c84d2a82be7f079b2d016e1c9fad22d68285ff"
|
||||
"dba701de9fd584405917e923ea1e59dbb249b96ef23bad479cf4e42740b774c8"
|
||||
"8e89d281483e22d11d111bed540652af35b66af6f14f49faae7b959f6cdc6475"
|
||||
"cb2ce3d6277bf2f5a82ecf336cc44963bc6490bcf496ffbd75fc9e21abaa75f3"
|
||||
)
|
||||
sha256sums=("782e62eb1378e8d9d50af3fa1c18b95d11bb4721df93b4525beba80f14d55661")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||
|
||||
pushd copyparty/web
|
||||
make -j$(nproc)
|
||||
rm Makefile
|
||||
popd
|
||||
|
||||
python3 -m build -wn
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "${srcdir}/"
|
||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||
python3 -m installer -d "$pkgdir" dist/*.whl
|
||||
|
||||
install -dm755 "${pkgdir}/etc/${pkgname}.d"
|
||||
install -Dm755 "${pkgname}-sfx.py" "${pkgdir}/usr/bin/${pkgname}"
|
||||
install -Dm755 "prisonparty.sh" "${pkgdir}/usr/bin/prisonparty"
|
||||
install -Dm644 "${pkgname}.conf" "${pkgdir}/etc/${pkgname}.d/init"
|
||||
install -Dm644 "${pkgname}.service" "${pkgdir}/usr/lib/systemd/system/${pkgname}.service"
|
||||
install -Dm644 "prisonparty.service" "${pkgdir}/usr/lib/systemd/system/prisonparty.service"
|
||||
install -Dm644 "index.md" "${pkgdir}/var/lib/${pkgname}-jail/README.md"
|
||||
install -Dm755 "bin/prisonparty.sh" "${pkgdir}/usr/bin/prisonparty"
|
||||
install -Dm644 "contrib/package/arch/${pkgname}.conf" "${pkgdir}/etc/${pkgname}.d/init"
|
||||
install -Dm644 "contrib/package/arch/${pkgname}.service" "${pkgdir}/usr/lib/systemd/system/${pkgname}.service"
|
||||
install -Dm644 "contrib/package/arch/prisonparty.service" "${pkgdir}/usr/lib/systemd/system/prisonparty.service"
|
||||
install -Dm644 "contrib/package/arch/index.md" "${pkgdir}/var/lib/${pkgname}-jail/README.md"
|
||||
install -Dm644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
||||
|
||||
find /etc/${pkgname}.d -iname '*.conf' 2>/dev/null | grep -qE . && return
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.6.15/copyparty-sfx.py",
|
||||
"version": "1.6.15",
|
||||
"hash": "sha256-7+P+hxDvjztQsesPk1+GION4ve5I9ZLXehxc8+184kQ="
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.7.0/copyparty-sfx.py",
|
||||
"version": "1.7.0",
|
||||
"hash": "sha256-CW2+Bzt5bJyilCUCY0W0htV7J1LejDttMv2Mk5bFtgI="
|
||||
}
|
||||
@@ -186,7 +186,7 @@ def init_E(E: EnvParams) -> None:
|
||||
|
||||
with open_binary("copyparty", "z.tar") as tgz:
|
||||
with tarfile.open(fileobj=tgz) as tf:
|
||||
tf.extractall(tdn)
|
||||
tf.extractall(tdn) # nosec (archive is safe)
|
||||
|
||||
return tdn
|
||||
|
||||
@@ -201,7 +201,7 @@ def init_E(E: EnvParams) -> None:
|
||||
E.mod = _unpack()
|
||||
|
||||
if sys.platform == "win32":
|
||||
bdir = os.environ.get("APPDATA") or os.environ.get("TEMP")
|
||||
bdir = os.environ.get("APPDATA") or os.environ.get("TEMP") or "."
|
||||
E.cfg = os.path.normpath(bdir + "/copyparty")
|
||||
elif sys.platform == "darwin":
|
||||
E.cfg = os.path.expanduser("~/Library/Preferences/copyparty")
|
||||
@@ -785,6 +785,7 @@ def add_webdav(ap):
|
||||
ap2.add_argument("--dav-inf", action="store_true", help="allow depth:infinite requests (recursive file listing); extremely server-heavy but required for spec compliance -- luckily few clients rely on this")
|
||||
ap2.add_argument("--dav-mac", action="store_true", help="disable apple-garbage filter -- allow macos to create junk files (._* and .DS_Store, .Spotlight-*, .fseventsd, .Trashes, .AppleDouble, __MACOS)")
|
||||
ap2.add_argument("--dav-rt", action="store_true", help="show symlink-destination's lastmodified instead of the link itself; always enabled for recursive listings (volflag=davrt)")
|
||||
ap2.add_argument("--dav-auth", action="store_true", help="force auth for all folders (required by davfs2 when only some folders are world-readable) (volflag=davauth)")
|
||||
|
||||
|
||||
def add_smb(ap):
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# coding: utf-8
|
||||
|
||||
VERSION = (1, 7, 0)
|
||||
VERSION = (1, 7, 1)
|
||||
CODENAME = "unlinked"
|
||||
BUILD_DT = (2023, 4, 29)
|
||||
BUILD_DT = (2023, 5, 7)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
||||
@@ -1178,6 +1178,14 @@ class AuthSrv(object):
|
||||
if LEELOO_DALLAS in all_users:
|
||||
raise Exception("sorry, reserved username: " + LEELOO_DALLAS)
|
||||
|
||||
seenpwds = {}
|
||||
for usr, pwd in acct.items():
|
||||
if pwd in seenpwds:
|
||||
t = "accounts [{}] and [{}] have the same password; this is not supported"
|
||||
self.log(t.format(seenpwds[pwd], usr), 1)
|
||||
raise Exception("invalid config")
|
||||
seenpwds[pwd] = usr
|
||||
|
||||
promote = []
|
||||
demote = []
|
||||
for vol in vfs.all_vols.values():
|
||||
|
||||
@@ -13,6 +13,7 @@ def vf_bmap() -> dict[str, str]:
|
||||
"no_dedup": "copydupes",
|
||||
"no_dupe": "nodupe",
|
||||
"no_forget": "noforget",
|
||||
"dav_auth": "davauth",
|
||||
"dav_rt": "davrt",
|
||||
}
|
||||
for k in (
|
||||
@@ -144,6 +145,7 @@ flagcats = {
|
||||
},
|
||||
"others": {
|
||||
"fk=8": 'generates per-file accesskeys,\nwhich will then be required at the "g" permission',
|
||||
"davauth": "ask webdav clients to login for all folders",
|
||||
"davrt": "show lastmod time of symlink destination, not the link itself\n(note: this option is always enabled for recursive listings)",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ class FtpAuth(DummyAuthorizer):
|
||||
|
||||
raise AuthenticationFailed("Authentication failed.")
|
||||
|
||||
handler.uname = uname
|
||||
handler.uname = handler.username = uname
|
||||
|
||||
def get_home_dir(self, username: str) -> str:
|
||||
return "/"
|
||||
|
||||
@@ -331,7 +331,7 @@ class HttpCli(object):
|
||||
for k in arglist.split("&"):
|
||||
if "=" in k:
|
||||
k, zs = k.split("=", 1)
|
||||
uparam[k.lower()] = zs.strip()
|
||||
uparam[k.lower()] = unquotep(zs.strip().replace("+", " "))
|
||||
else:
|
||||
uparam[k.lower()] = ""
|
||||
|
||||
@@ -726,7 +726,7 @@ class HttpCli(object):
|
||||
|
||||
def handle_get(self) -> bool:
|
||||
if self.do_log:
|
||||
logmsg = "{:4} {}".format(self.mode, self.req)
|
||||
logmsg = "%4s %s @%s" % (self.mode, self.req, self.uname)
|
||||
|
||||
if "range" in self.headers:
|
||||
try:
|
||||
@@ -825,17 +825,20 @@ class HttpCli(object):
|
||||
|
||||
def handle_propfind(self) -> bool:
|
||||
if self.do_log:
|
||||
self.log("PFIND " + self.req)
|
||||
self.log("PFIND %s @%s" % (self.req, self.uname))
|
||||
|
||||
if self.args.no_dav:
|
||||
raise Pebkac(405, "WebDAV is disabled in server config")
|
||||
|
||||
if not self.can_read and not self.can_write and not self.can_get:
|
||||
if self.vpath:
|
||||
self.log("inaccessible: [{}]".format(self.vpath))
|
||||
raise Pebkac(401, "authenticate")
|
||||
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False, err=401)
|
||||
tap = vn.canonical(rem)
|
||||
|
||||
self.uparam["h"] = ""
|
||||
if "davauth" in vn.flags and self.uname == "*":
|
||||
self.can_read = self.can_write = self.can_get = False
|
||||
|
||||
if not self.can_read and not self.can_write and not self.can_get:
|
||||
self.log("inaccessible: [{}]".format(self.vpath))
|
||||
raise Pebkac(401, "authenticate")
|
||||
|
||||
from .dxml import parse_xml
|
||||
|
||||
@@ -879,8 +882,6 @@ class HttpCli(object):
|
||||
]
|
||||
|
||||
props = set(props_lst)
|
||||
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, True, False, err=401)
|
||||
tap = vn.canonical(rem)
|
||||
depth = self.headers.get("depth", "infinity").lower()
|
||||
|
||||
try:
|
||||
@@ -890,7 +891,7 @@ class HttpCli(object):
|
||||
raise
|
||||
raise Pebkac(404)
|
||||
|
||||
if not stat.S_ISDIR(topdir["st"].st_mode):
|
||||
if depth == "0" or not self.can_read or not stat.S_ISDIR(topdir["st"].st_mode):
|
||||
fgen = []
|
||||
|
||||
elif depth == "infinity":
|
||||
@@ -932,9 +933,6 @@ class HttpCli(object):
|
||||
ls += [{"vp": v, "st": zsr} for v in vfs_virt]
|
||||
fgen = ls # type: ignore
|
||||
|
||||
elif depth == "0":
|
||||
fgen = [] # type: ignore
|
||||
|
||||
else:
|
||||
t = "invalid depth value '{}' (must be either '0' or '1'{})"
|
||||
t2 = " or 'infinity'" if self.args.dav_inf else ""
|
||||
@@ -1007,7 +1005,7 @@ class HttpCli(object):
|
||||
|
||||
def handle_proppatch(self) -> bool:
|
||||
if self.do_log:
|
||||
self.log("PPATCH " + self.req)
|
||||
self.log("PPATCH %s @%s" % (self.req, self.uname))
|
||||
|
||||
if self.args.no_dav:
|
||||
raise Pebkac(405, "WebDAV is disabled in server config")
|
||||
@@ -1065,7 +1063,7 @@ class HttpCli(object):
|
||||
|
||||
def handle_lock(self) -> bool:
|
||||
if self.do_log:
|
||||
self.log("LOCK " + self.req)
|
||||
self.log("LOCK %s @%s" % (self.req, self.uname))
|
||||
|
||||
if self.args.no_dav:
|
||||
raise Pebkac(405, "WebDAV is disabled in server config")
|
||||
@@ -1131,7 +1129,7 @@ class HttpCli(object):
|
||||
|
||||
def handle_unlock(self) -> bool:
|
||||
if self.do_log:
|
||||
self.log("UNLOCK " + self.req)
|
||||
self.log("UNLOCK %s @%s" % (self.req, self.uname))
|
||||
|
||||
if self.args.no_dav:
|
||||
raise Pebkac(405, "WebDAV is disabled in server config")
|
||||
@@ -1148,7 +1146,7 @@ class HttpCli(object):
|
||||
return True
|
||||
|
||||
if self.do_log:
|
||||
self.log("MKCOL " + self.req)
|
||||
self.log("MKCOL %s @%s" % (self.req, self.uname))
|
||||
|
||||
try:
|
||||
return self._mkdir(self.vpath, True)
|
||||
@@ -1200,7 +1198,7 @@ class HttpCli(object):
|
||||
|
||||
def handle_options(self) -> bool:
|
||||
if self.do_log:
|
||||
self.log("OPTIONS " + self.req)
|
||||
self.log("OPTIONS %s @%s" % (self.req, self.uname))
|
||||
|
||||
oh = self.out_headers
|
||||
oh["Allow"] = ", ".join(self.conn.hsrv.mallow)
|
||||
@@ -1215,11 +1213,11 @@ class HttpCli(object):
|
||||
return True
|
||||
|
||||
def handle_delete(self) -> bool:
|
||||
self.log("DELETE " + self.req)
|
||||
self.log("DELETE %s @%s" % (self.req, self.uname))
|
||||
return self.handle_rm([])
|
||||
|
||||
def handle_put(self) -> bool:
|
||||
self.log("PUT " + self.req)
|
||||
self.log("PUT %s @%s" % (self.req, self.uname))
|
||||
|
||||
if not self.can_write:
|
||||
t = "user {} does not have write-access here"
|
||||
@@ -1237,7 +1235,7 @@ class HttpCli(object):
|
||||
return self.handle_stash(True)
|
||||
|
||||
def handle_post(self) -> bool:
|
||||
self.log("POST " + self.req)
|
||||
self.log("POST %s @%s" % (self.req, self.uname))
|
||||
|
||||
if self.headers.get("expect", "").lower() == "100-continue":
|
||||
try:
|
||||
@@ -2780,7 +2778,7 @@ class HttpCli(object):
|
||||
return True
|
||||
|
||||
def tx_md(self, fs_path: str) -> bool:
|
||||
logmsg = "{:4} {} ".format("", self.req)
|
||||
logmsg = " %s @%s " % (self.req, self.uname)
|
||||
|
||||
if not self.can_write:
|
||||
if "edit" in self.uparam or "edit2" in self.uparam:
|
||||
|
||||
0
copyparty/res/__init__.py
Normal file
0
copyparty/res/__init__.py
Normal file
@@ -73,8 +73,8 @@ if True: # pylint: disable=using-constant-test
|
||||
if TYPE_CHECKING:
|
||||
from .svchub import SvcHub
|
||||
|
||||
zs = "avif,avifs,bmp,gif,heic,heics,heif,heifs,ico,j2p,j2k,jp2,jpeg,jpg,jpx,png,tga,tif,tiff,webp"
|
||||
CV_EXTS = set(zs.split(","))
|
||||
zsg = "avif,avifs,bmp,gif,heic,heics,heif,heifs,ico,j2p,j2k,jp2,jpeg,jpg,jpx,png,tga,tif,tiff,webp"
|
||||
CV_EXTS = set(zsg.split(","))
|
||||
|
||||
|
||||
class Dbw(object):
|
||||
|
||||
@@ -296,11 +296,11 @@ REKOBO_LKEY = {k.lower(): v for k, v in REKOBO_KEY.items()}
|
||||
pybin = sys.executable or ""
|
||||
if EXE:
|
||||
pybin = ""
|
||||
for p in "python3 python".split():
|
||||
for zsg in "python3 python".split():
|
||||
try:
|
||||
p = shutil.which(p)
|
||||
if p:
|
||||
pybin = p
|
||||
zsg = shutil.which(zsg)
|
||||
if zsg:
|
||||
pybin = zsg
|
||||
break
|
||||
except:
|
||||
pass
|
||||
@@ -1593,7 +1593,7 @@ def gen_filekey_dbg(
|
||||
|
||||
|
||||
def gencookie(k: str, v: str, r: str, tls: bool, dur: Optional[int]) -> str:
|
||||
v = v.replace(";", "")
|
||||
v = v.replace("%", "%25").replace(";", "%3B")
|
||||
if dur:
|
||||
exp = formatdate(time.time() + dur, usegmt=True)
|
||||
else:
|
||||
@@ -2320,7 +2320,7 @@ def unescape_cookie(orig: str) -> str:
|
||||
ret += chr(int(esc[1:], 16))
|
||||
except:
|
||||
ret += esc
|
||||
esc = ""
|
||||
esc = ""
|
||||
|
||||
else:
|
||||
ret += ch
|
||||
|
||||
@@ -6,7 +6,7 @@ pk: $(addsuffix .gz, $(wildcard *.js *.css))
|
||||
un: $(addsuffix .un, $(wildcard *.gz))
|
||||
|
||||
%.gz: %
|
||||
pigz -11 -J 34 -I 5730 $<
|
||||
pigz -11 -J 34 -I 573 $<
|
||||
|
||||
%.un: %
|
||||
pigz -d $<
|
||||
|
||||
1
copyparty/web/a/u2c.py
Symbolic link
1
copyparty/web/a/u2c.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../bin/u2c.py
|
||||
@@ -1 +0,0 @@
|
||||
../../../bin/up2k.py
|
||||
@@ -1840,6 +1840,7 @@ var pbar = (function () {
|
||||
html_txt = 'a',
|
||||
lastmove = 0,
|
||||
mousepos = 0,
|
||||
t_redraw = 0,
|
||||
gradh = -1,
|
||||
grad;
|
||||
|
||||
@@ -1908,6 +1909,9 @@ var pbar = (function () {
|
||||
bctx = bc.ctx,
|
||||
apos, adur;
|
||||
|
||||
if (!widget.is_open)
|
||||
return;
|
||||
|
||||
bctx.clearRect(0, 0, bc.w, bc.h);
|
||||
|
||||
if (!mp || !mp.au || !isNum(adur = mp.au.duration) || !isNum(apos = mp.au.currentTime) || apos < 0 || adur < apos)
|
||||
@@ -1970,6 +1974,7 @@ var pbar = (function () {
|
||||
w = 8,
|
||||
apos, adur;
|
||||
|
||||
clearTimeout(t_redraw);
|
||||
pctx.clearRect(0, 0, pc.w, pc.h);
|
||||
|
||||
if (!mp || !mp.au || !isNum(adur = mp.au.duration) || !isNum(apos = mp.au.currentTime) || apos < 0 || adur < apos)
|
||||
@@ -1984,17 +1989,30 @@ var pbar = (function () {
|
||||
}
|
||||
|
||||
var sm = bc.w * 1.0 / adur,
|
||||
t1 = s2ms(adur),
|
||||
t2 = s2ms(apos),
|
||||
x = sm * apos;
|
||||
|
||||
if (w && html_txt != t2) {
|
||||
ebi('np_pos').textContent = html_txt = t2;
|
||||
if (mpl.os_ctl)
|
||||
navigator.mediaSession.setPositionState({
|
||||
'duration': adur,
|
||||
'position': apos,
|
||||
'playbackRate': 1
|
||||
});
|
||||
}
|
||||
|
||||
if (!widget.is_open)
|
||||
return;
|
||||
|
||||
pctx.fillStyle = '#573'; pctx.fillRect((x - w / 2) - 1, 0, w + 2, pc.h);
|
||||
pctx.fillStyle = '#dfc'; pctx.fillRect((x - w / 2), 0, w, pc.h);
|
||||
|
||||
pctx.lineWidth = 2.5;
|
||||
pctx.fillStyle = '#fff';
|
||||
|
||||
var t1 = s2ms(adur),
|
||||
t2 = s2ms(apos),
|
||||
m1 = pctx.measureText(t1),
|
||||
var m1 = pctx.measureText(t1),
|
||||
m1b = pctx.measureText(t1 + ":88"),
|
||||
m2 = pctx.measureText(t2),
|
||||
yt = pc.h / 3 * 2.1,
|
||||
@@ -2008,15 +2026,8 @@ var pbar = (function () {
|
||||
pctx.fillText(t1, xt1, yt);
|
||||
pctx.fillText(t2, xt2, yt);
|
||||
|
||||
if (w && html_txt != t2) {
|
||||
ebi('np_pos').textContent = html_txt = t2;
|
||||
if (mpl.os_ctl)
|
||||
navigator.mediaSession.setPositionState({
|
||||
'duration': adur,
|
||||
'position': apos,
|
||||
'playbackRate': 1
|
||||
});
|
||||
}
|
||||
if (sm > 10)
|
||||
t_redraw = setTimeout(r.drawpos, sm > 50 ? 20 : 50);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', r.onresize);
|
||||
@@ -2189,6 +2200,7 @@ function next_song_cmn(e) {
|
||||
return tree_neigh(1);
|
||||
}
|
||||
toast.inf(10, L.mm_nof);
|
||||
mpl.traversals = 0;
|
||||
t_fchg = 0;
|
||||
}
|
||||
function prev_song(e) {
|
||||
@@ -2305,6 +2317,8 @@ var mpui = (function () {
|
||||
return;
|
||||
}
|
||||
|
||||
var paint = !MOBILE || document.hasFocus();
|
||||
|
||||
var pos = mp.au.currentTime;
|
||||
if (!isNum(pos))
|
||||
pos = 0;
|
||||
@@ -2312,7 +2326,7 @@ var mpui = (function () {
|
||||
// indicate playback state in ui
|
||||
widget.paused(mp.au.paused);
|
||||
|
||||
if (++nth > 69) {
|
||||
if (paint && ++nth > 69) {
|
||||
// android-chrome breaks aspect ratio with unannounced viewport changes
|
||||
nth = 0;
|
||||
if (MOBILE) {
|
||||
@@ -2321,13 +2335,13 @@ var mpui = (function () {
|
||||
vbar.onresize();
|
||||
}
|
||||
}
|
||||
else {
|
||||
else if (paint) {
|
||||
// draw current position in song
|
||||
if (!mp.au.paused)
|
||||
pbar.drawpos();
|
||||
|
||||
// occasionally draw buffered regions
|
||||
if (++nth % 5 == 0)
|
||||
if (nth % 5 == 0)
|
||||
pbar.drawbuf();
|
||||
}
|
||||
|
||||
@@ -7054,6 +7068,7 @@ function sandbox(tgt, rules, cls, html) {
|
||||
'},1)</script></body></html>';
|
||||
|
||||
var fr = mknod('iframe');
|
||||
fr.setAttribute('title', 'folder ' + tid + 'logue');
|
||||
fr.setAttribute('sandbox', rules ? 'allow-' + rules.replace(/ /g, ' allow-') : '');
|
||||
fr.setAttribute('srcdoc', html);
|
||||
tgt.innerHTML = '';
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
<p><em>note: if you are on LAN (or just dont have valid certificates), add <code>-td</code></em></p>
|
||||
{% endif %}
|
||||
<p>
|
||||
you can use <a href="{{ r }}/.cpr/a/up2k.py">up2k.py</a> to upload (sometimes faster than web-browsers)
|
||||
you can use <a href="{{ r }}/.cpr/a/u2c.py">u2c.py</a> to upload (sometimes faster than web-browsers)
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
@@ -742,7 +742,7 @@ function get_pwd() {
|
||||
if (pwd.length < 2)
|
||||
return null;
|
||||
|
||||
return pwd[1].split(';')[0];
|
||||
return decodeURIComponent(pwd[1].split(';')[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1769,7 +1769,6 @@ function cprop(name) {
|
||||
|
||||
|
||||
function bchrome() {
|
||||
console.log(document.documentElement.className);
|
||||
var v, o = QS('meta[name=theme-color]');
|
||||
if (!o)
|
||||
return;
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2023-0429-2114 `v1.7.0` unlinked
|
||||
|
||||
don't get excited! nothing new and revolutionary, but `xvol` and `xdev` changed behavior so there's an above-average chance of fresh bugs
|
||||
|
||||
## new features
|
||||
* (#24): `xvol` and `xdev`, previously just hints to the filesystem indexer, now actively block access as well:
|
||||
* `xvol` stops users following symlinks leaving the volumes they have access to
|
||||
* so if you symlink `/home/ed/music` into `/srv/www/music` it'll get blocked
|
||||
* ...unless both folders are accessible through volumes, and the user has read-access to both
|
||||
* `xdev` stops users crossing the filesystem boundary of the volumes they have access to
|
||||
* so if you symlink another HDD into a volume it'll get blocked, but you can still symlink from other places on the same FS
|
||||
* enabling these will add a slight performance hit; the unlikely worst-case is `14%` slower directory listings, `35%` slower download-as-tar
|
||||
* file selection summary (num files, size, audio duration) in the bottom right
|
||||
* [u2cli](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py): more aggressive resolving with `--rh`
|
||||
* [add a warning](https://github.com/9001/copyparty#fix-unreliable-playback-on-android) that the default powersave settings in android may stop playing music during album changes
|
||||
* also appears [in the media player](https://user-images.githubusercontent.com/241032/235327191-7aaefff9-5d41-4e42-b71f-042a8247f29d.png) if the issue is detected at runtime (playback halts for 30sec while screen is off)
|
||||
|
||||
## bugfixes
|
||||
* (#23): stop autodeleting empty folders when moving or deleting files
|
||||
* but files which expire / [self-destruct](https://github.com/9001/copyparty#self-destruct) still clean up parent directories like before
|
||||
* ftp-server: some clients could fail to `mkdir` at first attempt (and also complain during rmdir)
|
||||
|
||||
## other changes
|
||||
* new version of [cpp-winpe64.exe](https://github.com/9001/copyparty/releases/download/v1.7.0/copyparty-winpe64.exe) since the ftp-server fix might be relevant
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2023-0426-2300 `v1.6.15` unexpected boost
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ as a result, the hashes are much less useful than they could have been (search t
|
||||
|
||||
however it allows for hashing multiple chunks in parallel, greatly increasing upload speed from fast storage (NVMe, raid-0 and such)
|
||||
|
||||
* both the [browser uploader](https://github.com/9001/copyparty#uploading) and the [commandline one](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py) does this now, allowing for fast uploading even from plaintext http
|
||||
* both the [browser uploader](https://github.com/9001/copyparty#uploading) and the [commandline one](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy) does this now, allowing for fast uploading even from plaintext http
|
||||
|
||||
hashwasm would solve the streaming issue but reduces hashing speed for sha512 (xxh128 does 6 GiB/s), and it would make old browsers and [iphones](https://bugs.webkit.org/show_bug.cgi?id=228552) unsupported
|
||||
|
||||
@@ -228,6 +228,8 @@ pip install mutagen # audio metadata
|
||||
pip install pyftpdlib # ftp server
|
||||
pip install impacket # smb server -- disable Windows Defender if you REALLY need this on windows
|
||||
pip install Pillow pyheif-pillow-opener pillow-avif-plugin # thumbnails
|
||||
pip install pyvips # faster thumbnails
|
||||
pip install psutil # better cleanup of stuck metadata parsers on windows
|
||||
pip install black==21.12b0 click==8.0.2 bandit pylint flake8 isort mypy # vscode tooling
|
||||
```
|
||||
|
||||
@@ -264,14 +266,17 @@ uses the included prebuilt webdeps
|
||||
if you downloaded a [release](https://github.com/9001/copyparty/releases) source tarball from github (for example [copyparty-1.6.15.tar.gz](https://github.com/9001/copyparty/releases/download/v1.6.15/copyparty-1.6.15.tar.gz) so not the autogenerated one) you can build it like so,
|
||||
|
||||
```bash
|
||||
python3 setup.py install --user
|
||||
python3 -m pip install --user -U build setuptools wheel jinja2 strip_hints
|
||||
bash scripts/run-tests.sh python3 # optional
|
||||
python3 -m build
|
||||
```
|
||||
|
||||
or if you're packaging it for a linux distro (nice), maybe something like
|
||||
if you are unable to use `build`, you can use the old setuptools approach instead,
|
||||
|
||||
```bash
|
||||
bash scripts/run-tests.sh python3 # optional
|
||||
python3 setup.py install --user setuptools wheel jinja2
|
||||
python3 setup.py build
|
||||
# you now have a wheel which you can install. or extract and repackage:
|
||||
python3 setup.py install --skip-build --prefix=/usr --root=$HOME/pe/copyparty
|
||||
```
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ rclone.exe mount --vfs-cache-mode writes --vfs-cache-max-age 5s --attr-timeout 5
|
||||
|
||||
# sync folders to/from copyparty
|
||||
|
||||
note that the up2k client [up2k.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#up2kpy) (available on the "connect" page of your copyparty server) does uploads much faster and safer, but rclone is bidirectional and more ubiquitous
|
||||
note that the up2k client [u2c.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy) (available on the "connect" page of your copyparty server) does uploads much faster and safer, but rclone is bidirectional and more ubiquitous
|
||||
|
||||
```
|
||||
rclone sync /usr/share/icons/ cpp-rw:fds/
|
||||
|
||||
@@ -287,7 +287,7 @@ symbol legend,
|
||||
* `curl-friendly ls` = returns a [sortable plaintext folder listing](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png) when curled
|
||||
* `curl-friendly upload` = uploading with curl is just `curl -T some.bin http://.../`
|
||||
* `a`/copyparty remarks:
|
||||
* one-way folder sync from local to server can be done efficiently with [up2k.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py), or with webdav and conventional rsync
|
||||
* one-way folder sync from local to server can be done efficiently with [u2c.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy), or with webdav and conventional rsync
|
||||
* can hot-reload config files (with just a few exceptions)
|
||||
* can set per-folder permissions if that folder is made into a separate volume, so there is configuration overhead
|
||||
* [event hooks](https://github.com/9001/copyparty/tree/hovudstraum/bin/hooks) ([discord](https://user-images.githubusercontent.com/241032/215304439-1c1cb3c8-ec6f-4c17-9f27-81f969b1811a.png), [desktop](https://user-images.githubusercontent.com/241032/215335767-9c91ed24-d36e-4b6b-9766-fb95d12d163f.png)) inspired by filebrowser, as well as the more complex [media parser](https://github.com/9001/copyparty/tree/hovudstraum/bin/mtag) alternative
|
||||
|
||||
146
pyproject.toml
Normal file
146
pyproject.toml
Normal file
@@ -0,0 +1,146 @@
|
||||
[project]
|
||||
name = "copyparty"
|
||||
description = """
|
||||
Portable file server with accelerated resumable uploads, \
|
||||
deduplication, WebDAV, FTP, zeroconf, media indexer, \
|
||||
video thumbnails, audio transcoding, and write-only folders"""
|
||||
readme = "README.md"
|
||||
authors = [{ name = "ed", email = "copyparty@ocv.me" }]
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.3"
|
||||
dependencies = ["Jinja2"]
|
||||
dynamic = ["version"]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.3",
|
||||
"Programming Language :: Python :: 3.4",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: Jython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Environment :: Console",
|
||||
"Environment :: No Input/Output (Daemon)",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"Intended Audience :: System Administrators",
|
||||
"Topic :: Communications :: File Sharing",
|
||||
"Topic :: Internet :: File Transfer Protocol (FTP)",
|
||||
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
"Source Code" = "https://github.com/9001/copyparty"
|
||||
"Bug Tracker" = "https://github.com/9001/copyparty/issues"
|
||||
"Demo Server" = "https://a.ocv.me/pub/demo/"
|
||||
|
||||
[project.optional-dependencies]
|
||||
thumbnails = ["Pillow"]
|
||||
thumbnails2 = ["pyvips"]
|
||||
audiotags = ["mutagen"]
|
||||
ftpd = ["pyftpdlib"]
|
||||
ftps = ["pyftpdlib", "pyopenssl"]
|
||||
|
||||
[project.scripts]
|
||||
copyparty = "copyparty.__main__:main"
|
||||
"u2c" = "copyparty.web.a.u2c:main"
|
||||
"partyfuse" = "copyparty.web.a.partyfuse:main"
|
||||
|
||||
# =====================================================================
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
# requires = ["hatchling"]
|
||||
# build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.version]
|
||||
source = "code"
|
||||
path = "copyparty/__version__.py"
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
version = { attr = "copyparty.__version__.__version__" }
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["copyparty*"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
copyparty = [
|
||||
"res/COPYING.txt",
|
||||
"res/insecure.pem",
|
||||
"web/*.gz",
|
||||
"web/*.js",
|
||||
"web/*.css",
|
||||
"web/*.html",
|
||||
"web/a/*.bat",
|
||||
"web/dd/*.png",
|
||||
"web/deps/*.gz",
|
||||
"web/deps/*.woff*",
|
||||
]
|
||||
|
||||
# =====================================================================
|
||||
|
||||
[tool.black]
|
||||
required-version = '21.12b0'
|
||||
target-version = ['py27']
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
include_trailing_comma = true
|
||||
force_sort_within_sections = true
|
||||
|
||||
[tool.bandit]
|
||||
skips = ["B104", "B110", "B112"]
|
||||
|
||||
# =====================================================================
|
||||
|
||||
[tool.pylint.MAIN]
|
||||
py-version = "3.11"
|
||||
jobs = 2
|
||||
|
||||
[tool.pylint."MESSAGES CONTROL"]
|
||||
disable = [
|
||||
"missing-module-docstring",
|
||||
"missing-class-docstring",
|
||||
"missing-function-docstring",
|
||||
"import-outside-toplevel",
|
||||
"wrong-import-position",
|
||||
"raise-missing-from",
|
||||
"bare-except",
|
||||
"broad-exception-raised",
|
||||
"broad-exception-caught",
|
||||
"invalid-name",
|
||||
"line-too-long",
|
||||
"too-many-lines",
|
||||
"consider-using-f-string",
|
||||
"pointless-string-statement",
|
||||
]
|
||||
|
||||
[tool.pylint.FORMAT]
|
||||
expected-line-ending-format = "LF"
|
||||
|
||||
# =====================================================================
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.11"
|
||||
files = ["copyparty"]
|
||||
show_error_codes = true
|
||||
show_column_numbers = true
|
||||
pretty = true
|
||||
strict = true
|
||||
local_partial_types = true
|
||||
strict_equality = true
|
||||
warn_unreachable = true
|
||||
ignore_missing_imports = true
|
||||
follow_imports = "silent"
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
no_implicit_reexport = false
|
||||
@@ -34,7 +34,7 @@ set -e
|
||||
# 4823 copyparty-extras/copyparty-repack.sh
|
||||
# `- source files from github
|
||||
#
|
||||
# 23663 copyparty-extras/up2k.py
|
||||
# 23663 copyparty-extras/u2c.py
|
||||
# `- standalone utility to upload or search for files
|
||||
#
|
||||
# 32280 copyparty-extras/partyfuse.py
|
||||
@@ -147,7 +147,7 @@ repack sfx-lite "re no-dd no-cm no-hl gz"
|
||||
# copy lite-sfx.py to ./copyparty,
|
||||
# delete extracted source code
|
||||
( cd copyparty-extras/
|
||||
mv copyparty-*/bin/up2k.py .
|
||||
mv copyparty-*/bin/u2c.py .
|
||||
mv copyparty-*/bin/partyfuse.py .
|
||||
cp -pv sfx-lite/copyparty-sfx.py ../copyparty
|
||||
rm -rf copyparty-{0..9}*.*.*{0..9}
|
||||
|
||||
@@ -73,14 +73,17 @@ pydir="$(
|
||||
}
|
||||
|
||||
function have() {
|
||||
python -c "import $1; $1; $1.__version__"
|
||||
python -c "import $1; $1; getattr($1,'__version__',0)"
|
||||
}
|
||||
|
||||
function load_env() {
|
||||
. buildenv/bin/activate
|
||||
have setuptools
|
||||
have wheel
|
||||
have build
|
||||
have twine
|
||||
have jinja2
|
||||
have strip_hints
|
||||
}
|
||||
|
||||
load_env || {
|
||||
@@ -88,19 +91,32 @@ load_env || {
|
||||
deactivate || true
|
||||
rm -rf buildenv
|
||||
python3 -m venv buildenv
|
||||
(. buildenv/bin/activate && pip install twine wheel)
|
||||
(. buildenv/bin/activate && pip install \
|
||||
setuptools wheel build twine jinja2 strip_hints )
|
||||
load_env
|
||||
}
|
||||
|
||||
# cleanup
|
||||
rm -rf unt build/pypi
|
||||
|
||||
# grab licenses
|
||||
scripts/genlic.sh copyparty/res/COPYING.txt
|
||||
|
||||
# remove type hints to support python < 3.9
|
||||
# clean-ish packaging env
|
||||
rm -rf build/pypi
|
||||
mkdir -p build/pypi
|
||||
cp -pR setup.py README.md LICENSE copyparty contrib bin scripts/strip_hints build/pypi/
|
||||
cp -pR pyproject.toml README.md LICENSE copyparty contrib bin scripts/strip_hints build/pypi/
|
||||
tar -c docs/lics.txt scripts/genlic.sh build/*.txt | tar -xC build/pypi/
|
||||
cd build/pypi
|
||||
|
||||
# delete junk
|
||||
find -name '*.pyc' -delete
|
||||
find -name __pycache__ -delete
|
||||
find -name py.typed -delete
|
||||
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
|
||||
|
||||
# remove type hints to support python < 3.9
|
||||
f=../strip-hints-0.1.10.tar.gz
|
||||
[ -e $f ] ||
|
||||
(url=https://files.pythonhosted.org/packages/9c/d4/312ddce71ee10f7e0ab762afc027e07a918f1c0e1be5b0069db5b0e7542d/strip-hints-0.1.10.tar.gz;
|
||||
@@ -132,24 +148,13 @@ while IFS= read -r x; do
|
||||
done
|
||||
|
||||
rm -rf contrib
|
||||
[ $fast ] && sed -ri s/5730/10/ copyparty/web/Makefile
|
||||
[ $fast ] && sed -ri s/573/10/ copyparty/web/Makefile
|
||||
(cd copyparty/web && make -j$(nproc) && rm Makefile)
|
||||
|
||||
# build
|
||||
./setup.py clean2
|
||||
./setup.py sdist bdist_wheel --universal
|
||||
python3 -m build
|
||||
|
||||
[ "$mode" == t ] && twine upload -r pypitest dist/*
|
||||
[ "$mode" == u ] && twine upload -r pypi dist/*
|
||||
|
||||
cat <<EOF
|
||||
|
||||
|
||||
all done!
|
||||
|
||||
to clean up the source tree:
|
||||
|
||||
cd ~/dev/copyparty
|
||||
./setup.py clean2
|
||||
|
||||
EOF
|
||||
true
|
||||
|
||||
@@ -96,6 +96,7 @@ rm \
|
||||
.gitignore
|
||||
|
||||
cp -pv LICENSE LICENSE.txt
|
||||
mv setup.py{,.disabled}
|
||||
|
||||
# the regular cleanup memes
|
||||
find -name '*.pyc' -delete
|
||||
|
||||
@@ -11,30 +11,12 @@ update_arch_pkgbuild() {
|
||||
rm -rf x
|
||||
mkdir x
|
||||
|
||||
(echo "$self/../dist/copyparty-sfx.py"
|
||||
awk -v self="$self" '
|
||||
/^\)/{o=0}
|
||||
/^source=/{o=1;next}
|
||||
{
|
||||
sub(/..pkgname./,"copyparty");
|
||||
sub(/.*pkgver./,self "/..");
|
||||
sub(/^ +"/,"");sub(/"/,"")
|
||||
}
|
||||
o&&!/https/' PKGBUILD
|
||||
) |
|
||||
xargs sha256sum > x/sums
|
||||
sha=$(sha256sum "$self/../dist/copyparty-$ver.tar.gz" | awk '{print$1}')
|
||||
|
||||
(awk -v ver=$ver '
|
||||
awk -v ver=$ver -v sha=$sha '
|
||||
/^pkgver=/{sub(/[0-9\.]+/,ver)};
|
||||
/^sha256sums=/{exit};
|
||||
1' PKGBUILD
|
||||
echo -n 'sha256sums=('
|
||||
p=; cat x/sums | while read s _; do
|
||||
echo "$p\"$s\""
|
||||
p=' '
|
||||
done
|
||||
awk '/^sha256sums=/{o=1} o&&/^\)/{o=2} o==2' PKGBUILD
|
||||
) >a
|
||||
/^sha256sums=/{sub(/[0-9a-f]{64}/,sha)};
|
||||
1' PKGBUILD >a
|
||||
mv a PKGBUILD
|
||||
|
||||
rm -rf x
|
||||
|
||||
@@ -7,7 +7,7 @@ fe62705893c86eeb2d5b841da8debe05dedda98364dec190b487e718caad8a8735503bf93739a7a2
|
||||
132a5380f33a245f2e744413a0e1090bc42b7356376de5121397cec5976b04b79f7c9ebe28af222c9c7b01461f7d7920810d220e337694727e0d7cd9e91fa667 pywin32_ctypes-0.2.0-py2.py3-none-any.whl
|
||||
3c5adf0a36516d284a2ede363051edc1bcc9df925c5a8a9fa2e03cab579dd8d847fdad42f7fd5ba35992e08234c97d2dbfec40a9d12eec61c8dc03758f2bd88e typing_extensions-4.4.0-py3-none-any.whl
|
||||
4b6e9ae967a769fe32be8cf0bc0d5a213b138d1e0344e97656d08a3d15578d81c06c45b334c872009db2db8f39db0c77c94ff6c35168d5e13801917667c08678 upx-4.0.2-win32.zip
|
||||
# up2k (win7)
|
||||
# u2c (win7)
|
||||
a7d259277af4948bf960682bc9fb45a44b9ae9a19763c8a7c313cef4aa9ec2d447d843e4a7c409e9312c8c8f863a24487a8ee4ffa6891e9b1c4e111bb4723861 certifi-2022.12.7-py3-none-any.whl
|
||||
2822c0dae180b1c8cfb7a70c8c00bad62af9afdbb18b656236680def9d3f1fcdcb8ef5eb64fc3b4c934385cd175ad5992a2284bcba78a243130de75b2d1650db charset_normalizer-3.1.0-cp37-cp37m-win32.whl
|
||||
ffdd45326f4e91c02714f7a944cbcc2fdd09299f709cfa8aec0892053eef0134fb80d9ba3790afd319538a86feb619037cbf533e2f5939cb56b35bb17f56c858 idna-3.4-py3-none-any.whl
|
||||
|
||||
@@ -13,7 +13,7 @@ https://pypi.org/project/MarkupSafe/#files
|
||||
https://pypi.org/project/mutagen/#files
|
||||
https://pypi.org/project/Pillow/#files
|
||||
|
||||
# up2k (win7) additionals
|
||||
# u2c (win7) additionals
|
||||
https://pypi.org/project/certifi/#files
|
||||
https://pypi.org/project/charset-normalizer/#files # cp37-cp37m-win32.whl
|
||||
https://pypi.org/project/idna/#files
|
||||
|
||||
@@ -18,9 +18,9 @@ VSVersionInfo(
|
||||
[StringStruct('CompanyName', 'ocv.me'),
|
||||
StringStruct('FileDescription', 'copyparty uploader / filesearch command'),
|
||||
StringStruct('FileVersion', '1.2.3'),
|
||||
StringStruct('InternalName', 'up2k'),
|
||||
StringStruct('InternalName', 'u2c'),
|
||||
StringStruct('LegalCopyright', '2019, ed'),
|
||||
StringStruct('OriginalFilename', 'up2k.exe'),
|
||||
StringStruct('OriginalFilename', 'u2c.exe'),
|
||||
StringStruct('ProductName', 'copyparty up2k client'),
|
||||
StringStruct('ProductVersion', '1.2.3')])
|
||||
]),
|
||||
|
||||
@@ -14,7 +14,7 @@ uname -s | grep -E 'WOW64|NT-10' && echo need win7-32 && exit 1
|
||||
dl() { curl -fkLO "$1"; }
|
||||
cd ~/Downloads
|
||||
|
||||
dl https://192.168.123.1:3923/cpp/bin/up2k.py
|
||||
dl https://192.168.123.1:3923/cpp/bin/u2c.py
|
||||
dl https://192.168.123.1:3923/cpp/scripts/pyinstaller/up2k.ico
|
||||
dl https://192.168.123.1:3923/cpp/scripts/pyinstaller/up2k.rc
|
||||
dl https://192.168.123.1:3923/cpp/scripts/pyinstaller/up2k.spec
|
||||
@@ -37,12 +37,12 @@ grep -E '^from .ssl_ import' $APPDATA/python/python37/site-packages/urllib3/util
|
||||
echo golfed
|
||||
}
|
||||
|
||||
read a b _ < <(awk -F\" '/^S_VERSION =/{$0=$2;sub(/\./," ");print}' < up2k.py)
|
||||
read a b _ < <(awk -F\" '/^S_VERSION =/{$0=$2;sub(/\./," ");print}' < u2c.py)
|
||||
sed -r 's/1,2,3,0/'$a,$b,0,0'/;s/1\.2\.3/'$a.$b.0/ <up2k.rc >up2k.rc2
|
||||
|
||||
#python uncomment.py up2k.py
|
||||
#python uncomment.py u2c.py
|
||||
$APPDATA/python/python37/scripts/pyinstaller -y --clean --upx-dir=. up2k.spec
|
||||
|
||||
./dist/up2k.exe --version
|
||||
./dist/u2c.exe --version
|
||||
|
||||
curl -fkT dist/up2k.exe -HPW:wark https://192.168.123.1:3923/
|
||||
curl -fkT dist/u2c.exe -HPW:wark https://192.168.123.1:3923/
|
||||
|
||||
@@ -5,7 +5,7 @@ block_cipher = None
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['up2k.py'],
|
||||
['u2c.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
@@ -60,7 +60,7 @@ exe = EXE(
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='up2k',
|
||||
name='u2c',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
|
||||
@@ -11,4 +11,4 @@ ex=(
|
||||
encodings.{zlib_codec,base64_codec,bz2_codec,charmap,hex_codec,palmos,punycode,rot_13}
|
||||
);
|
||||
cex=(); for a in "${ex[@]}"; do cex+=(--exclude "$a"); done
|
||||
$APPDATA/python/python37/scripts/pyi-makespec --version-file up2k.rc2 -i up2k.ico -n up2k -c -F up2k.py "${cex[@]}"
|
||||
$APPDATA/python/python37/scripts/pyi-makespec --version-file up2k.rc2 -i up2k.ico -n u2c -c -F u2c.py "${cex[@]}"
|
||||
|
||||
@@ -23,6 +23,7 @@ copyparty/mdns.py,
|
||||
copyparty/mtag.py,
|
||||
copyparty/multicast.py,
|
||||
copyparty/res,
|
||||
copyparty/res/__init__.py,
|
||||
copyparty/res/COPYING.txt,
|
||||
copyparty/res/insecure.pem,
|
||||
copyparty/smbd.py,
|
||||
@@ -62,7 +63,7 @@ copyparty/web,
|
||||
copyparty/web/a,
|
||||
copyparty/web/a/__init__.py,
|
||||
copyparty/web/a/partyfuse.py,
|
||||
copyparty/web/a/up2k.py,
|
||||
copyparty/web/a/u2c.py,
|
||||
copyparty/web/a/webdav-cfg.bat,
|
||||
copyparty/web/baguettebox.js,
|
||||
copyparty/web/browser.css,
|
||||
|
||||
7
setup.py
7
setup.py
@@ -7,6 +7,11 @@ import sys
|
||||
from shutil import rmtree
|
||||
from setuptools import setup, Command
|
||||
|
||||
_ = """
|
||||
this probably still works but is no longer in use;
|
||||
pyproject.toml and scripts/make-pypi-release.sh
|
||||
are in charge of packaging wheels now
|
||||
"""
|
||||
|
||||
NAME = "copyparty"
|
||||
VERSION = None
|
||||
@@ -137,7 +142,7 @@ args = {
|
||||
"ftps": ["pyftpdlib", "pyopenssl"],
|
||||
},
|
||||
"entry_points": {"console_scripts": ["copyparty = copyparty.__main__:main"]},
|
||||
"scripts": ["bin/partyfuse.py", "bin/up2k.py"],
|
||||
"scripts": ["bin/partyfuse.py", "bin/u2c.py"],
|
||||
"cmdclass": {"clean2": clean2},
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ class Cfg(Namespace):
|
||||
def __init__(self, a=None, v=None, c=None):
|
||||
ka = {}
|
||||
|
||||
ex = "daw dav_inf dav_mac dav_rt 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"
|
||||
ex = "daw dav_auth dav_inf dav_mac dav_rt 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